summaryrefslogtreecommitdiffstats
path: root/sys/compat/linux/linux_futex.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linux/linux_futex.c')
-rw-r--r--sys/compat/linux/linux_futex.c91
1 files changed, 60 insertions, 31 deletions
diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c
index 219b601..1a55419 100644
--- a/sys/compat/linux/linux_futex.c
+++ b/sys/compat/linux/linux_futex.c
@@ -60,8 +60,8 @@ __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.7 2006/07/24 19:01:49 manu Exp $")
#include <machine/../linux/linux.h>
#include <machine/../linux/linux_proto.h>
#endif
-#include <compat/linux/linux_futex.h>
#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_futex.h>
#include <compat/linux/linux_util.h>
MALLOC_DEFINE(M_FUTEX, "futex", "Linux futexes");
@@ -79,6 +79,7 @@ struct futex {
struct sx f_lck;
uint32_t *f_uaddr;
uint32_t f_refcount;
+ uint32_t f_bitset;
LIST_ENTRY(futex) f_list;
TAILQ_HEAD(lf_waiting_proc, waiting_proc) f_waiting_proc;
};
@@ -87,7 +88,7 @@ struct futex_list futex_list;
#define FUTEX_LOCK(f) sx_xlock(&(f)->f_lck)
#define FUTEX_UNLOCK(f) sx_xunlock(&(f)->f_lck)
-#define FUTEX_INIT(f) sx_init_flags(&(f)->f_lck, "ftlk", 0)
+#define FUTEX_INIT(f) sx_init_flags(&(f)->f_lck, "ftlk", SX_DUPOK)
#define FUTEX_DESTROY(f) sx_destroy(&(f)->f_lck)
#define FUTEX_ASSERT_LOCKED(f) sx_assert(&(f)->f_lck, SA_XLOCKED)
@@ -193,6 +194,7 @@ retry:
tmpf = malloc(sizeof(*tmpf), M_FUTEX, M_WAITOK | M_ZERO);
tmpf->f_uaddr = uaddr;
tmpf->f_refcount = 1;
+ tmpf->f_bitset = FUTEX_BITSET_MATCH_ANY;
FUTEX_INIT(tmpf);
TAILQ_INIT(&tmpf->f_waiting_proc);
@@ -264,15 +266,25 @@ futex_sleep(struct futex *f, struct waiting_proc *wp, int timeout)
}
static int
-futex_wake(struct futex *f, int n)
+futex_wake(struct futex *f, int n, uint32_t bitset)
{
struct waiting_proc *wp, *wpt;
int count = 0;
+ if (bitset == 0)
+ return (EINVAL);
+
FUTEX_ASSERT_LOCKED(f);
TAILQ_FOREACH_SAFE(wp, &f->f_waiting_proc, wp_list, wpt) {
LINUX_CTR3(sys_futex, "futex_wake uaddr %p wp %p ref %d",
f->f_uaddr, wp, f->f_refcount);
+ /*
+ * Unless we find a matching bit in
+ * the bitset, continue searching.
+ */
+ if (!(wp->wp_futex->f_bitset & bitset))
+ continue;
+
wp->wp_flags |= FUTEX_WP_REMOVED;
TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list);
wakeup_one(wp);
@@ -325,13 +337,18 @@ futex_requeue(struct futex *f, int n, struct futex *f2, int n2)
}
static int
-futex_wait(struct futex *f, struct waiting_proc *wp, struct l_timespec *ts)
+futex_wait(struct futex *f, struct waiting_proc *wp, struct l_timespec *ts,
+ uint32_t bitset)
{
struct l_timespec timeout;
struct timeval tv;
int timeout_hz;
int error;
+ if (bitset == 0)
+ return (EINVAL);
+ f->f_bitset = bitset;
+
if (ts != NULL) {
error = copyin(ts, &timeout, sizeof(timeout));
if (error)
@@ -419,8 +436,8 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
int clockrt, nrwake, op_ret, ret, val;
struct linux_emuldata *em;
struct waiting_proc *wp;
- struct futex *f, *f2 = NULL;
- int error = 0;
+ struct futex *f, *f2;
+ int error;
/*
* Our implementation provides only privates futexes. Most of the apps
@@ -443,15 +460,23 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
args->op != LINUX_FUTEX_WAIT_REQUEUE_PI)
return (ENOSYS);
+ error = 0;
+ f = f2 = NULL;
+
switch (args->op) {
case LINUX_FUTEX_WAIT:
+ args->val3 = FUTEX_BITSET_MATCH_ANY;
+ /* FALLTHROUGH */
+
+ case LINUX_FUTEX_WAIT_BITSET:
- LINUX_CTR2(sys_futex, "WAIT val %d uaddr %p",
- args->val, args->uaddr);
+ LINUX_CTR3(sys_futex, "WAIT uaddr %p val %d val3 %d",
+ args->uaddr, args->val, args->val3);
#ifdef DEBUG
if (ldebug(sys_futex))
- printf(ARGS(sys_futex, "futex_wait val %d uaddr %p"),
- args->val, args->uaddr);
+ printf(ARGS(sys_futex,
+ "futex_wait uaddr %p val %d val3 %d"),
+ args->uaddr, args->val, args->val3);
#endif
error = futex_get(args->uaddr, &wp, &f, FUTEX_CREATE_WP);
if (error)
@@ -464,19 +489,24 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
return (error);
}
if (val != args->val) {
- LINUX_CTR3(sys_futex, "WAIT uaddr %p val %d != uval %d",
- args->uaddr, args->val, val);
+ LINUX_CTR4(sys_futex,
+ "WAIT uaddr %p val %d != uval %d val3 %d",
+ args->uaddr, args->val, val, args->val3);
futex_put(f, wp);
return (EWOULDBLOCK);
}
- error = futex_wait(f, wp, args->timeout);
+ error = futex_wait(f, wp, args->timeout, args->val3);
break;
case LINUX_FUTEX_WAKE:
+ args->val3 = FUTEX_BITSET_MATCH_ANY;
+ /* FALLTHROUGH */
- LINUX_CTR2(sys_futex, "WAKE val %d uaddr %p",
- args->val, args->uaddr);
+ case LINUX_FUTEX_WAKE_BITSET:
+
+ LINUX_CTR3(sys_futex, "WAKE uaddr %p val % d val3 %d",
+ args->uaddr, args->val, args->val3);
/*
* XXX: Linux is able to cope with different addresses
@@ -485,8 +515,8 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
*/
#ifdef DEBUG
if (ldebug(sys_futex))
- printf(ARGS(sys_futex, "futex_wake val %d uaddr %p"),
- args->val, args->uaddr);
+ printf(ARGS(sys_futex, "futex_wake uaddr %p val %d val3 %d"),
+ args->uaddr, args->val, args->val3);
#endif
error = futex_get(args->uaddr, NULL, &f, FUTEX_DONTCREATE);
if (error)
@@ -495,7 +525,7 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
td->td_retval[0] = 0;
return (error);
}
- td->td_retval[0] = futex_wake(f, args->val);
+ td->td_retval[0] = futex_wake(f, args->val, args->val3);
futex_put(f, NULL);
break;
@@ -526,8 +556,7 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
/*
* To avoid deadlocks return EINVAL if second futex
- * exists at this time. Otherwise create the new futex
- * and ignore false positive LOR which thus happens.
+ * exists at this time.
*
* Glibc fall back to FUTEX_WAKE in case of any error
* returned by FUTEX_CMP_REQUEUE.
@@ -603,16 +632,16 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
return (EFAULT);
}
- ret = futex_wake(f, args->val);
+ ret = futex_wake(f, args->val, args->val3);
if (op_ret > 0) {
op_ret = 0;
nrwake = (int)(unsigned long)args->timeout;
if (f2 != NULL)
- op_ret += futex_wake(f2, nrwake);
+ op_ret += futex_wake(f2, nrwake, args->val3);
else
- op_ret += futex_wake(f, nrwake);
+ op_ret += futex_wake(f, nrwake, args->val3);
ret += op_ret;
}
@@ -652,26 +681,26 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
* FUTEX_REQUEUE returned EINVAL.
*/
em = em_find(td->td_proc, EMUL_DONTLOCK);
- if (em->used_requeue == 0) {
+ if ((em->flags & LINUX_XDEPR_REQUEUEOP) == 0) {
linux_msg(td,
"linux_sys_futex: "
"unsupported futex_requeue op\n");
- em->used_requeue = 1;
+ em->flags |= LINUX_XDEPR_REQUEUEOP;
}
return (EINVAL);
- case LINUX_FUTEX_WAIT_BITSET:
+ case LINUX_FUTEX_WAIT_REQUEUE_PI:
/* not yet implemented */
linux_msg(td,
"linux_sys_futex: "
- "op FUTEX_WAIT_BITSET not implemented\n");
+ "op FUTEX_WAIT_REQUEUE_PI not implemented\n");
return (ENOSYS);
- case LINUX_FUTEX_WAIT_REQUEUE_PI:
+ case LINUX_FUTEX_CMP_REQUEUE_PI:
/* not yet implemented */
linux_msg(td,
- "linux_sys_futex: "
- "op FUTEX_WAIT_REQUEUE_PI not implemented\n");
+ "linux_sys_futex: "
+ "op LINUX_FUTEX_CMP_REQUEUE_PI not implemented\n");
return (ENOSYS);
default:
@@ -775,7 +804,7 @@ retry:
if (error)
return (error);
if (f != NULL) {
- futex_wake(f, 1);
+ futex_wake(f, 1, FUTEX_BITSET_MATCH_ANY);
futex_put(f, NULL);
}
}
OpenPOWER on IntegriCloud