Module Name: src Committed By: christos Date: Tue Apr 16 23:03:05 UTC 2013
Modified Files: src/sys/compat/linux/common: linux_futex.c linux_futex.h Log Message: Add some more futex gunk and explain why it does not work (yet). Now skype aborts with a futex timeout, instead of a stack smash leading to a SEGV. To generate a diff of this commit: cvs rdiff -u -r1.28 -r1.29 src/sys/compat/linux/common/linux_futex.c cvs rdiff -u -r1.6 -r1.7 src/sys/compat/linux/common/linux_futex.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/compat/linux/common/linux_futex.c diff -u src/sys/compat/linux/common/linux_futex.c:1.28 src/sys/compat/linux/common/linux_futex.c:1.29 --- src/sys/compat/linux/common/linux_futex.c:1.28 Thu Nov 17 23:07:44 2011 +++ src/sys/compat/linux/common/linux_futex.c Tue Apr 16 19:03:05 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: linux_futex.c,v 1.28 2011/11/18 04:07:44 christos Exp $ */ +/* $NetBSD: linux_futex.c,v 1.29 2013/04/16 23:03:05 christos Exp $ */ /*- * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved. @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.28 2011/11/18 04:07:44 christos Exp $"); +__KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.29 2013/04/16 23:03:05 christos Exp $"); #include <sys/param.h> #include <sys/time.h> @@ -67,6 +67,7 @@ struct waiting_proc { struct futex { void *f_uaddr; int f_refcount; + uint32_t f_bitset; LIST_ENTRY(futex) f_list; TAILQ_HEAD(, waiting_proc) f_waiting_proc; TAILQ_HEAD(, waiting_proc) f_requeue_proc; @@ -105,7 +106,7 @@ linux_futex_fini(void) static struct waiting_proc *futex_wp_alloc(void); static void futex_wp_free(struct waiting_proc *); -static struct futex *futex_get(void *); +static struct futex *futex_get(void *, uint32_t); static void futex_ref(struct futex *); static void futex_put(struct futex *); static int futex_sleep(struct futex **, lwp_t *, int, struct waiting_proc *); @@ -127,7 +128,7 @@ linux_sys_futex(struct lwp *l, const str struct timespec ts = { 0, 0 }; int error; - if ((SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) == LINUX_FUTEX_WAIT && + if ((SCARG(uap, op) & LINUX_FUTEX_CMD_MASK) == LINUX_FUTEX_WAIT && SCARG(uap, timeout) != NULL) { if ((error = copyin(SCARG(uap, timeout), <s, sizeof(lts))) != 0) { @@ -149,15 +150,31 @@ linux_do_futex(struct lwp *l, const stru syscallarg(int *) uaddr2; syscallarg(int) val3; } */ - int val; + int val, val3; int ret; int error = 0; struct futex *f; struct futex *newf; - int timeout_hz; + int tout; struct futex *f2; struct waiting_proc *wp; - int op_ret; + int op_ret, cmd; + clockid_t clk; + + cmd = SCARG(uap, op) & LINUX_FUTEX_CMD_MASK; + val3 = SCARG(uap, val3); + + if (SCARG(uap, op) & LINUX_FUTEX_CLOCK_REALTIME) { + switch (cmd) { + case LINUX_FUTEX_WAIT_BITSET: + case LINUX_FUTEX_WAIT: + clk = CLOCK_REALTIME; + break; + default: + return ENOSYS; + } + } else + clk = CLOCK_MONOTONIC; /* * Our implementation provides only private futexes. Most of the apps @@ -165,11 +182,33 @@ linux_do_futex(struct lwp *l, const stru * all futexes as private by clearing the FUTEX_PRIVATE_FLAG. It works * in most cases (ie. when futexes are not shared on file descriptor * or between different processes). + * + * Note that we don't handle bitsets at all at the moment. We need + * to move from refcounting uaddr's to handling multiple futex entries + * pointing to the same uaddr, but having possibly different bitmask. + * Perhaps move to an implementation where each uaddr has a list of + * futexes. */ - switch (SCARG(uap, op) & ~LINUX_FUTEX_PRIVATE_FLAG) { + switch (cmd) { case LINUX_FUTEX_WAIT: + val3 = FUTEX_BITSET_MATCH_ANY; + /*FALLTHROUGH*/ + case LINUX_FUTEX_WAIT_BITSET: + if ((error = ts2timo(clk, 0, ts, &tout, NULL)) != 0) { + /* + * If the user process requests a non null timeout, + * make sure we do not turn it into an infinite + * timeout because tout is 0. + * + * We use a minimal timeout of 1/hz. Maybe it would make + * sense to just return ETIMEDOUT without sleeping. + */ + if (error == ETIMEDOUT && SCARG(uap, timeout) != NULL) + tout = 1; + else + return error; + } FUTEX_SYSTEM_LOCK; - if ((error = copyin(SCARG(uap, uaddr), &val, sizeof(val))) != 0) { FUTEX_SYSTEM_UNLOCK; @@ -187,27 +226,11 @@ linux_do_futex(struct lwp *l, const stru SCARG(uap, uaddr), val, (long long)ts->tv_sec, ts->tv_nsec)); - if ((error = itimespecfix(ts)) != 0) { - FUTEX_SYSTEM_UNLOCK; - return error; - } - timeout_hz = tstohz(ts); - - /* - * If the user process requests a non null timeout, - * make sure we do not turn it into an infinite - * timeout because timeout_hz is 0. - * - * We use a minimal timeout of 1/hz. Maybe it would make - * sense to just return ETIMEDOUT without sleeping. - */ - if (SCARG(uap, timeout) != NULL && timeout_hz == 0) - timeout_hz = 1; wp = futex_wp_alloc(); FUTEX_LOCK; - f = futex_get(SCARG(uap, uaddr)); - ret = futex_sleep(&f, l, timeout_hz, wp); + f = futex_get(SCARG(uap, uaddr), val3); + ret = futex_sleep(&f, l, tout, wp); futex_put(f); FUTEX_UNLOCK; futex_wp_free(wp); @@ -238,6 +261,9 @@ linux_do_futex(struct lwp *l, const stru break; case LINUX_FUTEX_WAKE: + val = FUTEX_BITSET_MATCH_ANY; + /*FALLTHROUGH*/ + case LINUX_FUTEX_WAKE_BITSET: /* * XXX: Linux is able cope with different addresses * corresponding to the same mapped memory in the sleeping @@ -249,7 +275,7 @@ linux_do_futex(struct lwp *l, const stru FUTEX_SYSTEM_LOCK; FUTEX_LOCK; - f = futex_get(SCARG(uap, uaddr)); + f = futex_get(SCARG(uap, uaddr), val3); *retval = futex_wake(f, SCARG(uap, val), NULL, 0); futex_put(f); FUTEX_UNLOCK; @@ -266,7 +292,7 @@ linux_do_futex(struct lwp *l, const stru return error; } - if (val != SCARG(uap, val3)) { + if (val != val3) { FUTEX_SYSTEM_UNLOCK; return EAGAIN; } @@ -278,8 +304,8 @@ linux_do_futex(struct lwp *l, const stru (int)(unsigned long)SCARG(uap, timeout))); FUTEX_LOCK; - f = futex_get(SCARG(uap, uaddr)); - newf = futex_get(SCARG(uap, uaddr2)); + f = futex_get(SCARG(uap, uaddr), val3); + newf = futex_get(SCARG(uap, uaddr2), val3); *retval = futex_wake(f, SCARG(uap, val), newf, (int)(unsigned long)SCARG(uap, timeout)); futex_put(f); @@ -299,8 +325,8 @@ linux_do_futex(struct lwp *l, const stru (int)(unsigned long)SCARG(uap, timeout))); FUTEX_LOCK; - f = futex_get(SCARG(uap, uaddr)); - newf = futex_get(SCARG(uap, uaddr2)); + f = futex_get(SCARG(uap, uaddr), val3); + newf = futex_get(SCARG(uap, uaddr2), val3); *retval = futex_wake(f, SCARG(uap, val), newf, (int)(unsigned long)SCARG(uap, timeout)); futex_put(f); @@ -311,8 +337,7 @@ linux_do_futex(struct lwp *l, const stru break; case LINUX_FUTEX_FD: - FUTEXPRINTF(("linux_sys_futex: unimplemented op %d\n", - SCARG(uap, op))); + FUTEXPRINTF(("%s: unimplemented op %d\n", __func__, cmd)); return ENOSYS; case LINUX_FUTEX_WAKE_OP: FUTEX_SYSTEM_LOCK; @@ -320,20 +345,20 @@ linux_do_futex(struct lwp *l, const stru FUTEXPRINTF(("FUTEX_WAKE_OP %d.%d: uaddr = %p, op = %d, " "val = %d, uaddr2 = %p, val2 = %d\n", l->l_proc->p_pid, l->l_lid, - SCARG(uap, uaddr), SCARG(uap, op), SCARG(uap, val), + SCARG(uap, uaddr), cmd, SCARG(uap, val), SCARG(uap, uaddr2), (int)(unsigned long)SCARG(uap, timeout))); FUTEX_LOCK; - f = futex_get(SCARG(uap, uaddr)); - f2 = futex_get(SCARG(uap, uaddr2)); + f = futex_get(SCARG(uap, uaddr), val3); + f2 = futex_get(SCARG(uap, uaddr2), val3); FUTEX_UNLOCK; /* * This function returns positive number as results and * negative as errors */ - op_ret = futex_atomic_op(l, SCARG(uap, val3), SCARG(uap, uaddr2)); + op_ret = futex_atomic_op(l, val3, SCARG(uap, uaddr2)); FUTEX_LOCK; if (op_ret < 0) { futex_put(f); @@ -361,8 +386,7 @@ linux_do_futex(struct lwp *l, const stru *retval = ret; break; default: - FUTEXPRINTF(("linux_sys_futex: unknown op %d\n", - SCARG(uap, op))); + FUTEXPRINTF(("%s: unknown op %d\n", __func__, cmd)); return ENOSYS; } return 0; @@ -387,7 +411,7 @@ futex_wp_free(struct waiting_proc *wp) } static struct futex * -futex_get(void *uaddr) +futex_get(void *uaddr, uint32_t bitset) { struct futex *f; @@ -403,6 +427,7 @@ futex_get(void *uaddr) /* Not found, create it */ f = kmem_zalloc(sizeof(*f), KM_SLEEP); f->f_uaddr = uaddr; + f->f_bitset = bitset; f->f_refcount = 1; TAILQ_INIT(&f->f_waiting_proc); TAILQ_INIT(&f->f_requeue_proc); @@ -483,8 +508,8 @@ futex_wake(struct futex *f, int n, struc TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) { KASSERT(wp->wp_new_futex == NULL); - FUTEXPRINTF(("futex_wake: signal f %p l %p ref %d\n", - f, wp->wp_l, f->f_refcount)); + FUTEXPRINTF(("%s: signal f %p l %p ref %d\n", __func__, + f, wp->wp_l, f->f_refcount)); cv_signal(&wp->wp_futex_cv); if (count <= n) { count++; @@ -496,8 +521,8 @@ futex_wake(struct futex *f, int n, struc futex_ref(newf); wp->wp_new_futex = newf; TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist); - FUTEXPRINTF(("futex_wake: requeue newf %p l %p ref %d\n", - newf, wp->wp_l, newf->f_refcount)); + FUTEXPRINTF(("%s: requeue newf %p l %p ref %d\n", + __func__, newf, wp->wp_l, newf->f_refcount)); if (count - n >= n2) goto out; } @@ -515,8 +540,8 @@ futex_wake(struct futex *f, int n, struc TAILQ_FOREACH_SAFE(wp, &f->f_requeue_proc, wp_rqlist, wpnext) { KASSERT(wp->wp_new_futex == f); - FUTEXPRINTF(("futex_wake: unrequeue f %p l %p ref %d\n", - f, wp->wp_l, f->f_refcount)); + FUTEXPRINTF(("%s: unrequeue f %p l %p ref %d\n", __func__, + f, wp->wp_l, f->f_refcount)); wp->wp_new_futex = NULL; TAILQ_REMOVE(&f->f_requeue_proc, wp, wp_rqlist); futex_put(f); @@ -531,8 +556,8 @@ futex_wake(struct futex *f, int n, struc futex_ref(newf); wp->wp_new_futex = newf; TAILQ_INSERT_TAIL(&newf->f_requeue_proc, wp, wp_rqlist); - FUTEXPRINTF(("futex_wake: rerequeue newf %p l %p ref %d\n", - newf, wp->wp_l, newf->f_refcount)); + FUTEXPRINTF(("%s: rerequeue newf %p l %p ref %d\n", + __func__, newf, wp->wp_l, newf->f_refcount)); if (count - n >= n2) break; } @@ -701,7 +726,7 @@ retry: if (!pi && (uval & FUTEX_WAITERS)) { FUTEX_LOCK; - f = futex_get(uaddr); + f = futex_get(uaddr, FUTEX_BITSET_MATCH_ANY); futex_wake(f, 1, NULL, 0); FUTEX_UNLOCK; } Index: src/sys/compat/linux/common/linux_futex.h diff -u src/sys/compat/linux/common/linux_futex.h:1.6 src/sys/compat/linux/common/linux_futex.h:1.7 --- src/sys/compat/linux/common/linux_futex.h:1.6 Thu Jan 10 21:49:09 2013 +++ src/sys/compat/linux/common/linux_futex.h Tue Apr 16 19:03:05 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: linux_futex.h,v 1.6 2013/01/11 02:49:09 christos Exp $ */ +/* $NetBSD: linux_futex.h,v 1.7 2013/04/16 23:03:05 christos Exp $ */ /*- * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved. @@ -50,6 +50,8 @@ #define LINUX_FUTEX_PRIVATE_FLAG 128 #define LINUX_FUTEX_CLOCK_REALTIME 256 +#define LINUX_FUTEX_CMD_MASK \ + (~(LINUX_FUTEX_PRIVATE_FLAG|LINUX_FUTEX_CLOCK_REALTIME)) #define FUTEX_OP_SET 0 #define FUTEX_OP_ADD 1 @@ -79,6 +81,8 @@ struct linux_robust_list_head { #define FUTEX_OWNER_DIED 0x40000000 #define FUTEX_TID_MASK 0x3fffffff +#define FUTEX_BITSET_MATCH_ANY 0xffffffff + void release_futexes(struct lwp *); struct linux_sys_futex_args; int linux_do_futex(struct lwp *, const struct linux_sys_futex_args *,