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>
This commit is contained in:
@@ -243,12 +243,28 @@ class Linux : public OperatingSystem
|
||||
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_WAKE_OP = 5;
|
||||
static const unsigned TGT_FUTEX_WAIT_BITSET = 9;
|
||||
static const unsigned TGT_FUTEX_WAKE_BITSET = 10;
|
||||
static const unsigned TGT_EAGAIN = 11;
|
||||
static const unsigned TGT_EWOULDBLOCK = TGT_EAGAIN;
|
||||
static const unsigned TGT_FUTEX_PRIVATE_FLAG = 128;
|
||||
static const unsigned TGT_FUTEX_CLOCK_REALTIME_FLAG = 256;
|
||||
// op field of futex_wake_op operation
|
||||
static const unsigned TGT_FUTEX_OP_SET = 0; // uaddr2 = oparg;
|
||||
static const unsigned TGT_FUTEX_OP_ADD = 1; // uaddr2 += oparg;
|
||||
static const unsigned TGT_FUTEX_OP_OR = 2; // uaddr2 |= oparg;
|
||||
static const unsigned TGT_FUTEX_OP_ANDN = 3; // uaddr2 &= ~oparg;
|
||||
static const unsigned TGT_FUTEX_OP_XOR = 4; // uaddr2 ^= oparg;
|
||||
// Use (1 << oparg) as operand
|
||||
static const unsigned TGT_FUTEX_OP_ARG_SHIFT = 8;
|
||||
// cmp field of futex_wake_op operation
|
||||
static const unsigned TGT_FUTEX_OP_CMP_EQ = 0;
|
||||
static const unsigned TGT_FUTEX_OP_CMP_NE = 1;
|
||||
static const unsigned TGT_FUTEX_OP_CMP_LT = 2;
|
||||
static const unsigned TGT_FUTEX_OP_CMP_LE = 3;
|
||||
static const unsigned TGT_FUTEX_OP_CMP_GT = 4;
|
||||
static const unsigned TGT_FUTEX_OP_CMP_GE = 5;
|
||||
|
||||
// for *at syscalls
|
||||
static const int TGT_AT_FDCWD = -100;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user