summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/kern/linux/linux.hh2
-rw-r--r--src/sim/futex_map.hh54
-rw-r--r--src/sim/syscall_emul.hh15
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;
}