diff options
-rw-r--r-- | sys/amd64/linux32/linux32_dummy.c | 2 | ||||
-rw-r--r-- | sys/amd64/linux32/syscalls.master | 6 | ||||
-rw-r--r-- | sys/compat/linux/linux_emul.c | 9 | ||||
-rw-r--r-- | sys/compat/linux/linux_emul.h | 4 | ||||
-rw-r--r-- | sys/compat/linux/linux_futex.c | 161 | ||||
-rw-r--r-- | sys/compat/linux/linux_futex.h | 18 | ||||
-rw-r--r-- | sys/compat/linux/linux_misc.c | 7 | ||||
-rw-r--r-- | sys/i386/linux/linux_dummy.c | 2 | ||||
-rw-r--r-- | sys/i386/linux/syscalls.master | 6 |
9 files changed, 200 insertions, 15 deletions
diff --git a/sys/amd64/linux32/linux32_dummy.c b/sys/amd64/linux32/linux32_dummy.c index c5e07b7..c20f422 100644 --- a/sys/amd64/linux32/linux32_dummy.c +++ b/sys/amd64/linux32/linux32_dummy.c @@ -98,8 +98,6 @@ DUMMY(migrate_pages); DUMMY(pselect6); DUMMY(ppoll); DUMMY(unshare); -DUMMY(set_robust_list); -DUMMY(get_robust_list); DUMMY(splice); DUMMY(sync_file_range); DUMMY(tee); diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master index 8c8da57..1b0857d 100644 --- a/sys/amd64/linux32/syscalls.master +++ b/sys/amd64/linux32/syscalls.master @@ -493,8 +493,10 @@ 308 AUE_NULL STD { int linux_pselect6(void); } 309 AUE_NULL STD { int linux_ppoll(void); } 310 AUE_NULL STD { int linux_unshare(void); } -311 AUE_NULL STD { int linux_set_robust_list(void); } -312 AUE_NULL STD { int linux_get_robust_list(void); } +311 AUE_NULL STD { int linux_set_robust_list(struct linux_robust_list_head *head, \ + l_size_t len); } +312 AUE_NULL STD { int linux_get_robust_list(l_int pid, struct linux_robust_list_head *head, \ + l_size_t *len); } 313 AUE_NULL STD { int linux_splice(void); } 314 AUE_NULL STD { int linux_sync_file_range(void); } 315 AUE_NULL STD { int linux_tee(void); } diff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c index 1a0d5a3..16e6033 100644 --- a/sys/compat/linux/linux_emul.c +++ b/sys/compat/linux/linux_emul.c @@ -44,9 +44,6 @@ __FBSDID("$FreeBSD$"); #include <sys/sysproto.h> #include <sys/unistd.h> -#include <compat/linux/linux_emul.h> -#include <compat/linux/linux_futex.h> - #ifdef COMPAT_LINUX32 #include <machine/../linux32/linux.h> #include <machine/../linux32/linux32_proto.h> @@ -55,6 +52,9 @@ __FBSDID("$FreeBSD$"); #include <machine/../linux/linux_proto.h> #endif +#include <compat/linux/linux_emul.h> +#include <compat/linux/linux_futex.h> + struct sx emul_shared_lock; struct mtx emul_lock; @@ -86,6 +86,7 @@ linux_proc_init(struct thread *td, pid_t child, int flags) em = malloc(sizeof *em, M_LINUX, M_WAITOK | M_ZERO); em->pid = child; em->pdeath_signal = 0; + em->robust_futexes = NULL; if (flags & LINUX_CLONE_THREAD) { /* handled later in the code */ } else { @@ -161,6 +162,8 @@ linux_proc_exit(void *arg __unused, struct proc *p) if (__predict_true(p->p_sysent != &elf_linux_sysvec)) return; + release_futexes(p); + /* find the emuldata */ em = em_find(p, EMUL_DOLOCK); diff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h index 0b9dd88..7125c92 100644 --- a/sys/compat/linux/linux_emul.h +++ b/sys/compat/linux/linux_emul.h @@ -31,6 +31,8 @@ #ifndef _LINUX_EMUL_H_ #define _LINUX_EMUL_H_ +#include <compat/linux/linux_futex.h> + struct linux_emuldata_shared { int refs; pid_t group_pid; @@ -52,6 +54,8 @@ struct linux_emuldata { int pdeath_signal; /* parent death signal */ + struct linux_robust_list_head *robust_futexes; + LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */ }; diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c index f4423b4..043962e 100644 --- a/sys/compat/linux/linux_futex.c +++ b/sys/compat/linux/linux_futex.c @@ -45,8 +45,11 @@ __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.7 2006/07/24 19:01:49 manu Exp $") #include <sys/systm.h> #include <sys/proc.h> #include <sys/queue.h> +#include <sys/imgact.h> #include <sys/lock.h> #include <sys/mutex.h> +#include <sys/priv.h> +#include <sys/sched.h> #include <sys/sx.h> #include <sys/malloc.h> @@ -57,6 +60,7 @@ __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_emul.h> #include <compat/linux/linux_futex.h> struct futex; @@ -533,3 +537,160 @@ futex_atomic_op(struct thread *td, int encoded_op, caddr_t uaddr) return (-ENOSYS); } } + +int +linux_set_robust_list(struct thread *td, struct linux_set_robust_list_args *args) +{ + struct linux_emuldata *em; + +#ifdef DEBUG + if (ldebug(set_robust_list)) + printf(ARGS(set_robust_list, "")); +#endif + if (args->len != sizeof(struct linux_robust_list_head)) + return (EINVAL); + + em = em_find(td->td_proc, EMUL_DOLOCK); + em->robust_futexes = args->head; + EMUL_UNLOCK(&emul_lock); + + return (0); +} + +int +linux_get_robust_list(struct thread *td, struct linux_get_robust_list_args *args) +{ + struct linux_emuldata *em; + struct linux_robust_list_head *head; + l_size_t len = sizeof(struct linux_robust_list_head); + int error = 0; + +#ifdef DEBUG + if (ldebug(get_robust_list)) + printf(ARGS(get_robust_list, "")); +#endif + + if (!args->pid) { + em = em_find(td->td_proc, EMUL_DONTLOCK); + head = em->robust_futexes; + } else { + struct proc *p; + + p = pfind(args->pid); + if (p == NULL) + return (ESRCH); + + em = em_find(p, EMUL_DONTLOCK); + /* XXX: ptrace? */ + if (priv_check(td, PRIV_CRED_SETUID) || + priv_check(td, PRIV_CRED_SETEUID) || + p_candebug(td, p)) + return (EPERM); + head = em->robust_futexes; + + PROC_UNLOCK(p); + } + + error = copyout(&len, args->len, sizeof(l_size_t)); + if (error) + return (EFAULT); + + error = copyout(head, args->head, sizeof(struct linux_robust_list_head)); + + return (error); +} + +static int +handle_futex_death(void *uaddr, pid_t pid, int pi) +{ + int uval, nval, mval; + struct futex *f; + +retry: + if (copyin(uaddr, &uval, 4)) + return (EFAULT); + + if ((uval & FUTEX_TID_MASK) == pid) { + mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; + nval = casuword32(uaddr, uval, mval); + + if (nval == -1) + return (EFAULT); + + if (nval != uval) + goto retry; + + if (!pi && (uval & FUTEX_WAITERS)) { + f = futex_get(uaddr, FUTEX_UNLOCKED); + futex_wake(f, 1, NULL, 0); + } + } + + return (0); +} + +static int +fetch_robust_entry(struct linux_robust_list **entry, + struct linux_robust_list **head, int *pi) +{ + l_ulong uentry; + + if (copyin((const void *)head, &uentry, sizeof(l_ulong))) + return (EFAULT); + + *entry = (void *)(uentry & ~1UL); + *pi = uentry & 1; + + return (0); +} + +/* This walks the list of robust futexes releasing them. */ +void +release_futexes(struct proc *p) +{ + struct linux_robust_list_head *head = NULL; + struct linux_robust_list *entry, *next_entry, *pending; + unsigned int limit = 2048, pi, next_pi, pip; + struct linux_emuldata *em; + l_ulong futex_offset; + int rc; + + em = em_find(p, EMUL_DONTLOCK); + head = em->robust_futexes; + + if (head == NULL) + return; + + if (fetch_robust_entry(&entry, &head->list.next, &pi)) + return; + + if (copyin(&head->futex_offset, &futex_offset, sizeof(l_ulong))) + return; + + if (fetch_robust_entry(&pending, &head->pending_list, &pip)) + return; + + while (entry != &head->list) { + rc = fetch_robust_entry(&next_entry, &entry->next, &next_pi); + + if (entry != pending) + if (handle_futex_death((char *)entry + futex_offset, + p->p_pid, pi)) + return; + + if (rc) + return; + + entry = next_entry; + pi = next_pi; + + if (!--limit) + break; + + sched_relinquish(curthread); + } + + if (pending) + handle_futex_death((char *) pending + futex_offset, + p->p_pid, pip); +} diff --git a/sys/compat/linux/linux_futex.h b/sys/compat/linux/linux_futex.h index 3ca6f3b..0f7a393 100644 --- a/sys/compat/linux/linux_futex.h +++ b/sys/compat/linux/linux_futex.h @@ -63,4 +63,22 @@ #define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */ #define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */ +/* This is defined by Linux user-space */ + +struct linux_robust_list { + struct linux_robust_list *next; +}; + +struct linux_robust_list_head { + struct linux_robust_list list; + l_ulong futex_offset; + struct linux_robust_list *pending_list; +}; + +#define FUTEX_WAITERS 0x80000000 +#define FUTEX_OWNER_DIED 0x40000000 +#define FUTEX_TID_MASK 0x3fffffff + +void release_futexes(struct proc *); + #endif /* !_LINUX_FUTEX_H */ diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 1ce930f..9989948 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -75,10 +75,6 @@ __FBSDID("$FreeBSD$"); #include <vm/vm_object.h> #include <vm/swap_pager.h> -#include <compat/linux/linux_sysproto.h> -#include <compat/linux/linux_emul.h> -#include <compat/linux/linux_misc.h> - #ifdef COMPAT_LINUX32 #include <machine/../linux32/linux.h> #include <machine/../linux32/linux32_proto.h> @@ -91,6 +87,9 @@ __FBSDID("$FreeBSD$"); #include <compat/linux/linux_mib.h> #include <compat/linux/linux_signal.h> #include <compat/linux/linux_util.h> +#include <compat/linux/linux_sysproto.h> +#include <compat/linux/linux_emul.h> +#include <compat/linux/linux_misc.h> #ifdef __i386__ #include <machine/cputypes.h> diff --git a/sys/i386/linux/linux_dummy.c b/sys/i386/linux/linux_dummy.c index 99a9d3d..f047361 100644 --- a/sys/i386/linux/linux_dummy.c +++ b/sys/i386/linux/linux_dummy.c @@ -89,8 +89,6 @@ DUMMY(migrate_pages); DUMMY(pselect6); DUMMY(ppoll); DUMMY(unshare); -DUMMY(set_robust_list); -DUMMY(get_robust_list); DUMMY(splice); DUMMY(sync_file_range); DUMMY(tee); diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master index 313be8a..f13741c 100644 --- a/sys/i386/linux/syscalls.master +++ b/sys/i386/linux/syscalls.master @@ -503,8 +503,10 @@ 308 AUE_NULL STD { int linux_pselect6(void); } 309 AUE_NULL STD { int linux_ppoll(void); } 310 AUE_NULL STD { int linux_unshare(void); } -311 AUE_NULL STD { int linux_set_robust_list(void); } -312 AUE_NULL STD { int linux_get_robust_list(void); } +311 AUE_NULL STD { int linux_set_robust_list(struct linux_robust_list_head *head, \ + l_size_t len); } +312 AUE_NULL STD { int linux_get_robust_list(l_int pid, struct linux_robust_list_head **head, \ + l_size_t *len); } 313 AUE_NULL STD { int linux_splice(void); } 314 AUE_NULL STD { int linux_sync_file_range(void); } 315 AUE_NULL STD { int linux_tee(void); } |