Use umtx_key objects to uniquely identify futexes. Private futexes in

different processes that happen to use the same user address in the
separate processes will now be treated as distinct futexes rather than the
same futex.  We can now honor shared futexes properly by mapping them to a
PROCESS_SHARED umtx_key.  Private futexes use THREAD_SHARED umtx_key
objects.

In conjunction with:	dchagin
Reviewed by:	kib
MFC after:	1 week
This commit is contained in:
jhb 2011-02-23 13:23:28 +00:00
parent 3eb951ea57
commit 6e95ae3692
2 changed files with 33 additions and 17 deletions

View File

@ -52,6 +52,7 @@ __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.7 2006/07/24 19:01:49 manu Exp $")
#include <sys/queue.h>
#include <sys/sched.h>
#include <sys/sx.h>
#include <sys/umtx.h>
#ifdef COMPAT_LINUX32
#include <machine/../linux32/linux.h>
@ -77,7 +78,8 @@ struct waiting_proc {
struct futex {
struct sx f_lck;
uint32_t *f_uaddr;
uint32_t *f_uaddr; /* user-supplied value, for debug */
struct umtx_key f_key;
uint32_t f_refcount;
uint32_t f_bitset;
LIST_ENTRY(futex) f_list;
@ -100,6 +102,7 @@ struct mtx futex_mtx; /* protects the futex list */
#define FUTEX_CREATE_WP 0x1 /* create waiting_proc */
#define FUTEX_DONTCREATE 0x2 /* don't create futex if not exists */
#define FUTEX_DONTEXISTS 0x4 /* return EINVAL if futex exists */
#define FUTEX_SHARED 0x8 /* shared futex */
/* wp_flags */
#define FUTEX_WP_REQUEUED 0x1 /* wp requeued - wp moved from wp_list
@ -136,6 +139,7 @@ futex_put(struct futex *f, struct waiting_proc *wp)
LINUX_CTR2(sys_futex, "futex_put destroy uaddr %p ref %d",
f->f_uaddr, f->f_refcount);
umtx_key_release(&f->f_key);
FUTEX_DESTROY(f);
free(f, M_FUTEX);
return;
@ -151,13 +155,19 @@ static int
futex_get0(uint32_t *uaddr, struct futex **newf, uint32_t flags)
{
struct futex *f, *tmpf;
struct umtx_key key;
int error;
*newf = tmpf = NULL;
error = umtx_key_get(uaddr, TYPE_FUTEX, (flags & FUTEX_SHARED) ?
PROCESS_SHARE : THREAD_SHARE, &key);
if (error)
return (error);
retry:
FUTEXES_LOCK;
LIST_FOREACH(f, &futex_list, f_list) {
if (f->f_uaddr == uaddr) {
if (umtx_key_match(&f->f_key, &key)) {
if (tmpf != NULL) {
FUTEX_UNLOCK(tmpf);
FUTEX_DESTROY(tmpf);
@ -165,6 +175,7 @@ retry:
}
if (flags & FUTEX_DONTEXISTS) {
FUTEXES_UNLOCK;
umtx_key_release(&key);
return (EINVAL);
}
@ -174,6 +185,7 @@ retry:
*/
++f->f_refcount;
FUTEXES_UNLOCK;
umtx_key_release(&key);
FUTEX_LOCK(f);
*newf = f;
@ -185,6 +197,7 @@ retry:
if (flags & FUTEX_DONTCREATE) {
FUTEXES_UNLOCK;
umtx_key_release(&key);
LINUX_CTR1(sys_futex, "futex_get uaddr %p null", uaddr);
return (0);
}
@ -193,6 +206,7 @@ retry:
FUTEXES_UNLOCK;
tmpf = malloc(sizeof(*tmpf), M_FUTEX, M_WAITOK | M_ZERO);
tmpf->f_uaddr = uaddr;
tmpf->f_key = key;
tmpf->f_refcount = 1;
tmpf->f_bitset = FUTEX_BITSET_MATCH_ANY;
FUTEX_INIT(tmpf);
@ -438,15 +452,13 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
struct waiting_proc *wp;
struct futex *f, *f2;
int error;
uint32_t flags;
/*
* Our implementation provides only privates futexes. Most of the apps
* should use private futexes but don't claim so. Therefore we treat
* 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.).
*/
args->op = args->op & ~LINUX_FUTEX_PRIVATE_FLAG;
if (args->op & LINUX_FUTEX_PRIVATE_FLAG) {
flags = 0;
args->op &= ~LINUX_FUTEX_PRIVATE_FLAG;
} else
flags = FUTEX_SHARED;
/*
* Currently support for switching between CLOCK_MONOTONIC and
@ -478,7 +490,8 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
"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);
error = futex_get(args->uaddr, &wp, &f,
flags | FUTEX_CREATE_WP);
if (error)
return (error);
error = copyin(args->uaddr, &val, sizeof(val));
@ -518,7 +531,8 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
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);
error = futex_get(args->uaddr, NULL, &f,
flags | FUTEX_DONTCREATE);
if (error)
return (error);
if (f == NULL) {
@ -550,7 +564,7 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
*/
if (args->uaddr == args->uaddr2)
return (EINVAL);
error = futex_get0(args->uaddr, &f, 0);
error = futex_get(args->uaddr, NULL, &f, flags);
if (error)
return (error);
@ -561,7 +575,8 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
* Glibc fall back to FUTEX_WAKE in case of any error
* returned by FUTEX_CMP_REQUEUE.
*/
error = futex_get0(args->uaddr2, &f2, FUTEX_DONTEXISTS);
error = futex_get(args->uaddr2, NULL, &f2,
flags | FUTEX_DONTEXISTS);
if (error) {
futex_put(f, NULL);
return (error);
@ -602,11 +617,11 @@ linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
args->uaddr, args->op, args->val,
args->uaddr2, args->val3);
#endif
error = futex_get0(args->uaddr, &f, 0);
error = futex_get(args->uaddr, NULL, &f, flags);
if (error)
return (error);
if (args->uaddr != args->uaddr2)
error = futex_get0(args->uaddr2, &f2, 0);
error = futex_get(args->uaddr2, NULL, &f2, flags);
if (error) {
futex_put(f, NULL);
return (error);
@ -800,7 +815,7 @@ retry:
if (!pi && (uval & FUTEX_WAITERS)) {
error = futex_get(uaddr, NULL, &f,
FUTEX_DONTCREATE);
FUTEX_DONTCREATE | FUTEX_SHARED);
if (error)
return (error);
if (f != NULL) {

View File

@ -185,6 +185,7 @@ enum {
TYPE_PI_UMUTEX,
TYPE_PP_UMUTEX,
TYPE_RWLOCK,
TYPE_FUTEX
};
/* Key to represent a unique userland synchronous object */