diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/kern/linux/linux.hh | 2 | ||||
-rw-r--r-- | src/sim/futex_map.hh | 54 | ||||
-rw-r--r-- | src/sim/syscall_emul.hh | 15 |
3 files changed, 70 insertions, 1 deletions
diff --git a/src/kern/linux/linux.hh b/src/kern/linux/linux.hh index 2da596814..4ed39028b 100644 --- a/src/kern/linux/linux.hh +++ b/src/kern/linux/linux.hh @@ -241,6 +241,8 @@ class Linux : public OperatingSystem // For futex system call static const unsigned TGT_FUTEX_WAIT = 0; static const unsigned TGT_FUTEX_WAKE = 1; + static const unsigned TGT_FUTEX_REQUEUE = 3; + static const unsigned TGT_FUTEX_CMP_REQUEUE = 4; static const unsigned TGT_FUTEX_WAIT_BITSET = 9; static const unsigned TGT_FUTEX_WAKE_BITSET = 10; static const unsigned TGT_EAGAIN = 11; diff --git a/src/sim/futex_map.hh b/src/sim/futex_map.hh index 6f1f7a2a1..3d34109be 100644 --- a/src/sim/futex_map.hh +++ b/src/sim/futex_map.hh @@ -221,6 +221,60 @@ class FutexMap : public std::unordered_map<FutexKey, WaiterList> return woken_up; } + + /** + * This operation wakes a given number (val) of waiters. If there are + * more threads waiting than woken, they are removed from the wait + * queue of the futex pointed to by addr1 and added to the wait queue + * of the futex pointed to by addr2. The number of waiter moved is + * capped by count2 (misused timeout parameter). + * + * The return value is the number of waiters that are woken or + * requeued. + */ + int + requeue(Addr addr1, uint64_t tgid, int count, int count2, Addr addr2) + { + FutexKey key1(addr1, tgid); + auto it1 = find(key1); + + if (it1 == end()) + return 0; + + int woken_up = 0; + auto &waiterList1 = it1->second; + + while (!waiterList1.empty() && woken_up < count) { + waiterList1.front().tc->activate(); + waiterList1.pop_front(); + woken_up++; + } + + WaiterList tmpList; + int requeued = 0; + + while (!waiterList1.empty() && requeued < count2) { + auto w = waiterList1.front(); + waiterList1.pop_front(); + tmpList.push_back(w); + requeued++; + } + + FutexKey key2(addr2, tgid); + auto it2 = find(key2); + + if (it2 == end() && requeued > 0) { + insert({key2, tmpList}); + } else { + it2->second.insert(it2->second.end(), + tmpList.begin(), tmpList.end()); + } + + if (waiterList1.empty()) + erase(it1); + + return woken_up + requeued; + } }; #endif // __FUTEX_MAP_HH__ diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index 295598c52..0b7585cb4 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -441,8 +441,21 @@ futexFunc(SyscallDesc *desc, int callnum, Process *process, return futex_map.wakeup(uaddr, process->tgid(), val); } else if (OS::TGT_FUTEX_WAKE_BITSET == op) { return futex_map.wakeup_bitset(uaddr, process->tgid(), val3); - } + } else if (OS::TGT_FUTEX_REQUEUE == op || + OS::TGT_FUTEX_CMP_REQUEUE == op) { + // Ensure futex system call accessed atomically. + BufferArg buf(uaddr, sizeof(int)); + buf.copyIn(tc->getMemProxy()); + int mem_val = *(int*)buf.bufferPtr(); + /* + * For CMP_REQUEUE, the whole operation is only started only if + * val3 is still the value of the futex pointed to by uaddr. + */ + if (OS::TGT_FUTEX_CMP_REQUEUE && val3 != mem_val) + return -OS::TGT_EWOULDBLOCK; + return futex_map.requeue(uaddr, process->tgid(), val, timeout, uaddr2); + } warn("futex: op %d not implemented; ignoring.", op); return -ENOSYS; } |