Module Name: src Committed By: riastradh Date: Wed Mar 5 14:01:34 UTC 2025
Modified Files: src/sys/kern: sys_futex.c Log Message: futex(2): Rename various parameters to clarify correspondence. No functional change intended. I have spent way too much time puzzling over what val/val2/val3 mean for each operation; let's just give them all meaningful names and write down the correspondence in the dispatch switch in do_futex. Prompted by how much time I spent scratching my head for: PR kern/59129: futex(3): missing sign extension in FUTEX_WAKE_OP To generate a diff of this commit: cvs rdiff -u -r1.24 -r1.25 src/sys/kern/sys_futex.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/sys_futex.c diff -u src/sys/kern/sys_futex.c:1.24 src/sys/kern/sys_futex.c:1.25 --- src/sys/kern/sys_futex.c:1.24 Wed Mar 5 14:01:20 2025 +++ src/sys/kern/sys_futex.c Wed Mar 5 14:01:34 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: sys_futex.c,v 1.24 2025/03/05 14:01:20 riastradh Exp $ */ +/* $NetBSD: sys_futex.c,v 1.25 2025/03/05 14:01:34 riastradh Exp $ */ /*- * Copyright (c) 2018, 2019, 2020 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.24 2025/03/05 14:01:20 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_futex.c,v 1.25 2025/03/05 14:01:34 riastradh Exp $"); /* * Futexes @@ -1196,12 +1196,17 @@ futex_queue_unlock2(struct futex *f, str } /* - * futex_func_wait(uaddr, val, val3, timeout, clkid, clkflags, retval) + * futex_func_wait(uaddr, cmpval@val, bitset@val3, timeout, clkid, clkflags, + * retval) * - * Implement futex(FUTEX_WAIT). + * Implement futex(FUTEX_WAIT) and futex(FUTEX_WAIT_BITSET): If + * *uaddr == cmpval, wait until futex-woken on any of the bits in + * bitset. But if *uaddr != cmpval, fail with EAGAIN. + * + * For FUTEX_WAIT, bitset has all bits set and val3 is ignored. */ static int -futex_func_wait(bool shared, int *uaddr, int val, int val3, +futex_func_wait(bool shared, int *uaddr, int cmpval, int bitset, const struct timespec *timeout, clockid_t clkid, int clkflags, register_t *retval) { @@ -1215,11 +1220,11 @@ futex_func_wait(bool shared, int *uaddr, * If there's nothing to wait for, and nobody will ever wake * us, then don't set anything up to wait -- just stop here. */ - if (val3 == 0) + if (bitset == 0) return EINVAL; /* Optimistically test before anything else. */ - if (!futex_test(uaddr, val)) + if (!futex_test(uaddr, cmpval)) return EAGAIN; /* Determine a deadline on the specified clock. */ @@ -1240,7 +1245,7 @@ futex_func_wait(bool shared, int *uaddr, KASSERT(f); /* Get ready to wait. */ - futex_wait_init(fw, val3); + futex_wait_init(fw, bitset); /* * Under the queue lock, check the value again: if it has @@ -1250,7 +1255,7 @@ futex_func_wait(bool shared, int *uaddr, * is immaterial. */ futex_queue_lock(f); - if (!futex_test(uaddr, val)) { + if (!futex_test(uaddr, cmpval)) { futex_queue_unlock(f); error = EAGAIN; goto out; @@ -1283,19 +1288,26 @@ out: if (f != NULL) } /* - * futex_func_wake(uaddr, val, val3, retval) + * futex_func_wake(uaddr, nwake@val, bitset@val3, retval) + * + * Implement futex(FUTEX_WAKE) and futex(FUTEX_WAKE_BITSET): Wake + * up to nwake waiters on uaddr waiting on any of the bits in + * bitset. + * + * Return the number of waiters woken. * - * Implement futex(FUTEX_WAKE) and futex(FUTEX_WAKE_BITSET). + * For FUTEX_WAKE, bitset has all bits set and val3 is ignored. */ static int -futex_func_wake(bool shared, int *uaddr, int val, int val3, register_t *retval) +futex_func_wake(bool shared, int *uaddr, int nwake, int bitset, + register_t *retval) { struct futex *f; unsigned int nwoken = 0; int error = 0; /* Reject negative number of wakeups. */ - if (val < 0) { + if (nwake < 0) { error = EINVAL; goto out; } @@ -1314,8 +1326,7 @@ futex_func_wake(bool shared, int *uaddr, * number woken. */ futex_queue_lock(f); - nwoken = futex_wake(f, /*nwake*/val, NULL, /*nrequeue*/0, - /*bitset*/val3); + nwoken = futex_wake(f, nwake, NULL, /*nrequeue*/0, bitset); futex_queue_unlock(f); /* Release the futex. */ @@ -1330,20 +1341,29 @@ out: } /* - * futex_func_requeue(op, uaddr, val, uaddr2, val2, val3, retval) + * futex_func_requeue(op, uaddr, nwake@val, uaddr2, nrequeue@val2, + * cmpval@val3, retval) + * + * Implement futex(FUTEX_REQUEUE) and futex(FUTEX_CMP_REQUEUE): If + * *uaddr == cmpval or if op == FUTEX_REQUEUE, wake up to nwake + * waiters at uaddr and then requeue up to nrequeue waiters from + * uaddr to uaddr2. * - * Implement futex(FUTEX_REQUEUE) and futex(FUTEX_CMP_REQUEUE). + * Return the number of waiters woken or requeued. + * + * For FUTEX_CMP_REQUEUE, if *uaddr != cmpval, fail with EAGAIN + * and no wakeups. */ static int -futex_func_requeue(bool shared, int op, int *uaddr, int val, int *uaddr2, - int val2, int val3, register_t *retval) +futex_func_requeue(bool shared, int op, int *uaddr, int nwake, int *uaddr2, + int nrequeue, int cmpval, register_t *retval) { struct futex *f = NULL, *f2 = NULL; unsigned nwoken_or_requeued = 0; /* default to zero on early return */ int error; /* Reject negative number of wakeups or requeues. */ - if (val < 0 || val2 < 0) { + if (nwake < 0 || nrequeue < 0) { error = EINVAL; goto out; } @@ -1376,15 +1396,15 @@ futex_func_requeue(bool shared, int op, /* * Under the futexes' queue locks, check the value; if - * unchanged from val3, wake the waiters. + * unchanged from cmpval, or if this is the unconditional + * FUTEX_REQUEUE operation, wake the waiters. */ futex_queue_lock2(f, f2); - if (op == FUTEX_CMP_REQUEUE && !futex_test(uaddr, val3)) { + if (op == FUTEX_CMP_REQUEUE && !futex_test(uaddr, cmpval)) { error = EAGAIN; } else { error = 0; - nwoken_or_requeued = futex_wake(f, /*nwake*/val, - f2, /*nrequeue*/val2, + nwoken_or_requeued = futex_wake(f, nwake, f2, nrequeue, FUTEX_BITSET_MATCH_ANY); } futex_queue_unlock2(f, f2); @@ -1418,19 +1438,19 @@ futex_opcmp_arg(int arg) } /* - * futex_validate_op_cmp(val3) + * futex_validate_op_cmp(opcmp) * * Validate an op/cmp argument for FUTEX_WAKE_OP. */ static int -futex_validate_op_cmp(int val3) +futex_validate_op_cmp(int opcmp) { - int op = __SHIFTOUT(val3, FUTEX_OP_OP_MASK); - int cmp = __SHIFTOUT(val3, FUTEX_OP_CMP_MASK); + int op = __SHIFTOUT(opcmp, FUTEX_OP_OP_MASK); + int cmp = __SHIFTOUT(opcmp, FUTEX_OP_CMP_MASK); if (op & FUTEX_OP_OPARG_SHIFT) { int oparg = - futex_opcmp_arg(__SHIFTOUT(val3, FUTEX_OP_OPARG_MASK)); + futex_opcmp_arg(__SHIFTOUT(opcmp, FUTEX_OP_OPARG_MASK)); if (oparg < 0) return EINVAL; if (oparg >= 32) @@ -1465,15 +1485,15 @@ futex_validate_op_cmp(int val3) } /* - * futex_compute_op(oldval, val3) + * futex_compute_op(oldval, opcmp) * * Apply a FUTEX_WAKE_OP operation to oldval. */ static int -futex_compute_op(int oldval, int val3) +futex_compute_op(int oldval, int opcmp) { - int op = __SHIFTOUT(val3, FUTEX_OP_OP_MASK); - int oparg = futex_opcmp_arg(__SHIFTOUT(val3, FUTEX_OP_OPARG_MASK)); + int op = __SHIFTOUT(opcmp, FUTEX_OP_OP_MASK); + int oparg = futex_opcmp_arg(__SHIFTOUT(opcmp, FUTEX_OP_OPARG_MASK)); if (op & FUTEX_OP_OPARG_SHIFT) { KASSERT(oparg >= 0); @@ -1509,15 +1529,15 @@ futex_compute_op(int oldval, int val3) } /* - * futex_compute_cmp(oldval, val3) + * futex_compute_cmp(oldval, opcmp) * * Apply a FUTEX_WAKE_OP comparison to oldval. */ static bool -futex_compute_cmp(int oldval, int val3) +futex_compute_cmp(int oldval, int opcmp) { - int cmp = __SHIFTOUT(val3, FUTEX_OP_CMP_MASK); - int cmparg = futex_opcmp_arg(__SHIFTOUT(val3, FUTEX_OP_CMPARG_MASK)); + int cmp = __SHIFTOUT(opcmp, FUTEX_OP_CMP_MASK); + int cmparg = futex_opcmp_arg(__SHIFTOUT(opcmp, FUTEX_OP_CMPARG_MASK)); switch (cmp) { case FUTEX_OP_CMP_EQ: @@ -1544,13 +1564,23 @@ futex_compute_cmp(int oldval, int val3) } /* - * futex_func_wake_op(uaddr, val, uaddr2, val2, val3, retval) + * futex_func_wake_op(uaddr, nwake@val, uaddr2, nwake2@val2, opcmp@val3, + * retval) + * + * Implement futex(FUTEX_WAKE_OP): + * + * 1. Update *uaddr2 according to the r/m/w operation specified in + * opcmp. + * + * 2. If uaddr is nonnull, wake up to nwake waiters at uaddr. * - * Implement futex(FUTEX_WAKE_OP). + * 3. If what was previously at *uaddr2 matches the comparison + * operation specified in opcmp, additionally wake up to nwake2 + * waiters at uaddr2. */ static int -futex_func_wake_op(bool shared, int *uaddr, int val, int *uaddr2, int val2, - int val3, register_t *retval) +futex_func_wake_op(bool shared, int *uaddr, int nwake, int *uaddr2, int nwake2, + int opcmp, register_t *retval) { struct futex *f = NULL, *f2 = NULL; int oldval, newval, actual; @@ -1558,13 +1588,13 @@ futex_func_wake_op(bool shared, int *uad int error; /* Reject negative number of wakeups. */ - if (val < 0 || val2 < 0) { + if (nwake < 0 || nwake2 < 0) { error = EINVAL; goto out; } /* Reject invalid operations before we start doing things. */ - if ((error = futex_validate_op_cmp(val3)) != 0) + if ((error = futex_validate_op_cmp(opcmp)) != 0) goto out; /* Look up the first futex, if any. */ @@ -1580,16 +1610,17 @@ futex_func_wake_op(bool shared, int *uad /* * Under the queue locks: * - * 1. Read/modify/write: *uaddr2 op= oparg. + * 1. Read/modify/write: *uaddr2 op= oparg, as in opcmp. * 2. Unconditionally wake uaddr. - * 3. Conditionally wake uaddr2, if it previously matched val2. + * 3. Conditionally wake uaddr2, if it previously matched the + * comparison in opcmp. */ futex_queue_lock2(f, f2); do { error = futex_load(uaddr2, &oldval); if (error) goto out_unlock; - newval = futex_compute_op(oldval, val3); + newval = futex_compute_op(oldval, opcmp); error = ucas_int(uaddr2, oldval, newval, &actual); if (error) goto out_unlock; @@ -1597,11 +1628,11 @@ futex_func_wake_op(bool shared, int *uad if (f == NULL) { nwoken = 0; } else { - nwoken = futex_wake(f, /*nwake*/val, NULL, /*nrequeue*/0, + nwoken = futex_wake(f, nwake, NULL, /*nrequeue*/0, FUTEX_BITSET_MATCH_ANY); } - if (f2 && futex_compute_cmp(oldval, val3)) { - nwoken += futex_wake(f2, /*nwake*/val2, NULL, /*nrequeue*/0, + if (f2 && futex_compute_cmp(oldval, opcmp)) { + nwoken += futex_wake(f2, nwake2, NULL, /*nrequeue*/0, FUTEX_BITSET_MATCH_ANY); } @@ -1639,30 +1670,49 @@ do_futex(int *uaddr, int op, int val, co op &= FUTEX_CMD_MASK; switch (op) { - case FUTEX_WAIT: - return futex_func_wait(shared, uaddr, val, - FUTEX_BITSET_MATCH_ANY, timeout, clkid, TIMER_RELTIME, - retval); - - case FUTEX_WAKE: - val3 = FUTEX_BITSET_MATCH_ANY; - /* FALLTHROUGH */ - case FUTEX_WAKE_BITSET: - return futex_func_wake(shared, uaddr, val, val3, retval); + case FUTEX_WAIT: { + const int cmpval = val; + const int bitset = FUTEX_BITSET_MATCH_ANY; + return futex_func_wait(shared, uaddr, cmpval, bitset, timeout, + clkid, TIMER_RELTIME, retval); + } + case FUTEX_WAKE: { + const int nwake = val; + const int bitset = FUTEX_BITSET_MATCH_ANY; + + return futex_func_wake(shared, uaddr, nwake, bitset, retval); + } + case FUTEX_WAKE_BITSET: { + const int nwake = val; + const int bitset = val3; + + return futex_func_wake(shared, uaddr, nwake, bitset, retval); + } case FUTEX_REQUEUE: - case FUTEX_CMP_REQUEUE: - return futex_func_requeue(shared, op, uaddr, val, uaddr2, - val2, val3, retval); + case FUTEX_CMP_REQUEUE: { + const int nwake = val; + const int nrequeue = val2; + const int cmpval = val3; /* ignored if op=FUTEX_REQUEUE */ + + return futex_func_requeue(shared, op, uaddr, nwake, uaddr2, + nrequeue, cmpval, retval); + } + case FUTEX_WAIT_BITSET: { + const int cmpval = val; + const int bitset = val3; - case FUTEX_WAIT_BITSET: - return futex_func_wait(shared, uaddr, val, val3, timeout, + return futex_func_wait(shared, uaddr, cmpval, bitset, timeout, clkid, TIMER_ABSTIME, retval); + } + case FUTEX_WAKE_OP: { + const int nwake = val; + const int nwake2 = val2; + const int opcmp = val3; - case FUTEX_WAKE_OP: - return futex_func_wake_op(shared, uaddr, val, uaddr2, val2, - val3, retval); - + return futex_func_wake_op(shared, uaddr, nwake, uaddr2, nwake2, + opcmp, retval); + } case FUTEX_FD: default: return ENOSYS;