diff options
author | Moyang Wang <mw828@cornell.edu> | 2018-04-02 16:23:13 -0400 |
---|---|---|
committer | Tuan Ta <qtt2@cornell.edu> | 2019-02-08 15:25:30 +0000 |
commit | 165a7dab558c8118622a387683521bea1ebf2e6c (patch) | |
tree | e3d36c6e12abf4807306f6e4c0d2201f13287568 /src/sim | |
parent | 758b62cfb2ede5fa2187e4cab899691505179d43 (diff) | |
download | gem5-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')
-rw-r--r-- | src/sim/syscall_emul.hh | 71 |
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; |