summaryrefslogtreecommitdiff
path: root/src/sim/syscall_emul.hh
diff options
context:
space:
mode:
authorMoyang Wang <mw828@cornell.edu>2018-04-02 16:23:13 -0400
committerTuan Ta <qtt2@cornell.edu>2019-02-08 15:25:30 +0000
commit165a7dab558c8118622a387683521bea1ebf2e6c (patch)
treee3d36c6e12abf4807306f6e4c0d2201f13287568 /src/sim/syscall_emul.hh
parent758b62cfb2ede5fa2187e4cab899691505179d43 (diff)
downloadgem5-165a7dab558c8118622a387683521bea1ebf2e6c.tar.xz
kern,sim: implement FUTEX_WAKE_OP
This patch implements FUTEX_WAKE_OP operation in the futex syscall. Below is its description: int futex(int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, int val3); This operation was added to support some user-space use cases where more than one futex must be handled at the same time. The most notable example is the implementation of pthread_cond_signal(3), which requires operations on two futexes, the one used to implement the mutex and the one used in the implementation of the wait queue associated with the condition variable. FUTEX_WAKE_OP allows such cases to be implemented without leading to high rates of contention and context switching. Reference: http://man7.org/linux/man-pages/man2/futex.2.html Change-Id: I215f3c2a7bdc6374e5dfe06ee721c76933a10f2d Reviewed-on: https://gem5-review.googlesource.com/c/9630 Reviewed-by: Jason Lowe-Power <jason@lowepower.com> Reviewed-by: Brandon Potter <Brandon.Potter@amd.com> Maintainer: Jason Lowe-Power <jason@lowepower.com>
Diffstat (limited to 'src/sim/syscall_emul.hh')
-rw-r--r--src/sim/syscall_emul.hh71
1 files changed, 71 insertions, 0 deletions
diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh
index 0b7585cb4..0378bd770 100644
--- a/src/sim/syscall_emul.hh
+++ b/src/sim/syscall_emul.hh
@@ -455,6 +455,77 @@ futexFunc(SyscallDesc *desc, int callnum, Process *process,
if (OS::TGT_FUTEX_CMP_REQUEUE && val3 != mem_val)
return -OS::TGT_EWOULDBLOCK;
return futex_map.requeue(uaddr, process->tgid(), val, timeout, uaddr2);
+ } else if (OS::TGT_FUTEX_WAKE_OP == op) {
+ /*
+ * The FUTEX_WAKE_OP operation is equivalent to executing the
+ * following code atomically and totally ordered with respect to
+ * other futex operations on any of the two supplied futex words:
+ *
+ * int oldval = *(int *) addr2;
+ * *(int *) addr2 = oldval op oparg;
+ * futex(addr1, FUTEX_WAKE, val, 0, 0, 0);
+ * if (oldval cmp cmparg)
+ * futex(addr2, FUTEX_WAKE, val2, 0, 0, 0);
+ *
+ * (op, oparg, cmp, cmparg are encoded in val3)
+ *
+ * +---+---+-----------+-----------+
+ * |op |cmp| oparg | cmparg |
+ * +---+---+-----------+-----------+
+ * 4 4 12 12 <== # of bits
+ *
+ * reference: http://man7.org/linux/man-pages/man2/futex.2.html
+ *
+ */
+ // get value from simulated-space
+ BufferArg buf(uaddr2, sizeof(int));
+ buf.copyIn(tc->getMemProxy());
+ int oldval = *(int*)buf.bufferPtr();
+ int newval = oldval;
+ // extract op, oparg, cmp, cmparg from val3
+ int wake_cmparg = val3 & 0xfff;
+ int wake_oparg = (val3 & 0xfff000) >> 12;
+ int wake_cmp = (val3 & 0xf000000) >> 24;
+ int wake_op = (val3 & 0xf0000000) >> 28;
+ if ((wake_op & OS::TGT_FUTEX_OP_ARG_SHIFT) >> 3 == 1)
+ wake_oparg = (1 << wake_oparg);
+ wake_op &= ~OS::TGT_FUTEX_OP_ARG_SHIFT;
+ // perform operation on the value of the second futex
+ if (wake_op == OS::TGT_FUTEX_OP_SET)
+ newval = wake_oparg;
+ else if (wake_op == OS::TGT_FUTEX_OP_ADD)
+ newval += wake_oparg;
+ else if (wake_op == OS::TGT_FUTEX_OP_OR)
+ newval |= wake_oparg;
+ else if (wake_op == OS::TGT_FUTEX_OP_ANDN)
+ newval &= ~wake_oparg;
+ else if (wake_op == OS::TGT_FUTEX_OP_XOR)
+ newval ^= wake_oparg;
+ // copy updated value back to simulated-space
+ *(int*)buf.bufferPtr() = newval;
+ buf.copyOut(tc->getMemProxy());
+ // perform the first wake-up
+ int woken1 = futex_map.wakeup(uaddr, process->tgid(), val);
+ int woken2 = 0;
+ // calculate the condition of the second wake-up
+ bool is_wake2 = false;
+ if (wake_cmp == OS::TGT_FUTEX_OP_CMP_EQ)
+ is_wake2 = oldval == wake_cmparg;
+ else if (wake_cmp == OS::TGT_FUTEX_OP_CMP_NE)
+ is_wake2 = oldval != wake_cmparg;
+ else if (wake_cmp == OS::TGT_FUTEX_OP_CMP_LT)
+ is_wake2 = oldval < wake_cmparg;
+ else if (wake_cmp == OS::TGT_FUTEX_OP_CMP_LE)
+ is_wake2 = oldval <= wake_cmparg;
+ else if (wake_cmp == OS::TGT_FUTEX_OP_CMP_GT)
+ is_wake2 = oldval > wake_cmparg;
+ else if (wake_cmp == OS::TGT_FUTEX_OP_CMP_GE)
+ is_wake2 = oldval >= wake_cmparg;
+ // perform the second wake-up
+ if (is_wake2)
+ woken2 = futex_map.wakeup(uaddr2, process->tgid(), timeout);
+
+ return woken1 + woken2;
}
warn("futex: op %d not implemented; ignoring.", op);
return -ENOSYS;