sim, kern: support FUTEX_CMP_REQUEUE
authorMoyang Wang <mw828@cornell.edu>
Mon, 2 Apr 2018 20:23:02 +0000 (16:23 -0400)
committerTuan Ta <qtt2@cornell.edu>
Fri, 8 Feb 2019 15:25:30 +0000 (15:25 +0000)
This patch supports FUTEX_CMP_REQUEUE operation. Below is its
description from Linux man page:

futex syscall: int futex(int *uaddr, int futex_op, int val,
                         const struct timespec *timeout,
                         int *uaddr2, int val3);

This operation first checks whether the location uaddr still contains
the value val3.  If not, the operation fails with the error EAGAIN.
Otherwise, the operation wakes up a maximum of val waiters that are
waiting on the futex at uaddr.  If there are more than val waiters, then
the remaining waiters are removed from the wait queue of the source
futex at uaddr and added to the wait queue of the target futex at
uaddr2.  The val2 argument specifies an upper limit on the number of
waiters that are requeued to the futex at uaddr2.

Reference: http://man7.org/linux/man-pages/man2/futex.2.html

Change-Id: I6d2ebd19a935b656d19d8342f7ab450c0d2031f4
Reviewed-on: https://gem5-review.googlesource.com/c/9629
Reviewed-by: Brandon Potter <Brandon.Potter@amd.com>
Maintainer: Brandon Potter <Brandon.Potter@amd.com>

src/kern/linux/linux.hh
src/sim/futex_map.hh
src/sim/syscall_emul.hh

index 2da59681484267d6beda0298c4efd03e55f81d97..4ed39028b6087353aaa9c3e91dfd2e69d6f95cf2 100644 (file)
@@ -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;
index 6f1f7a2a1ea38aba4ee75dbd0f6caacd4bd0b4bc..3d34109be744ed8e8693e42b9dcae2ba1b9ff544 100644 (file)
@@ -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__
index 295598c52ed13aff491bac082e7c7653150d379d..0b7585cb4ae6c1bdce9b4a521b3e11ae176551b5 100644 (file)
@@ -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;
 }