From 2122144749c4e2d78be8c3aea038117dc798cff5 Mon Sep 17 00:00:00 2001 From: netchild Date: Tue, 15 Aug 2006 12:20:59 +0000 Subject: Add some new files needed for linux 2.6.x compatibility. Please don't style(9) the NetBSD code, we want to stay in sync. Not imported on a vendor branch since we need local changes. Sponsored by: Google SoC 2006 Submitted by: rdivacky With help from: manu@NetBSD.org Obtained from: NetBSD (linux_{futex,time}.*) --- sys/compat/linux/linux_emul.c | 297 ++++++++++++++++++++++++ sys/compat/linux/linux_emul.h | 74 ++++++ sys/compat/linux/linux_futex.c | 500 +++++++++++++++++++++++++++++++++++++++++ sys/compat/linux/linux_futex.h | 61 +++++ sys/compat/linux/linux_time.c | 208 +++++++++++++++++ 5 files changed, 1140 insertions(+) create mode 100644 sys/compat/linux/linux_emul.c create mode 100644 sys/compat/linux/linux_emul.h create mode 100644 sys/compat/linux/linux_futex.c create mode 100644 sys/compat/linux/linux_futex.h create mode 100644 sys/compat/linux/linux_time.c (limited to 'sys/compat') diff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c new file mode 100644 index 0000000..f3398b9 --- /dev/null +++ b/sys/compat/linux/linux_emul.c @@ -0,0 +1,297 @@ +/*- + * Copyright (c) 2006 Roman Divacky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_compat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef COMPAT_LINUX32 +#include +#include +#else +#include +#include +#endif + +struct sx emul_shared_lock; +struct sx emul_lock; + +/* this returns locked reference to the emuldata entry (if found) */ +struct linux_emuldata * +em_find(struct proc *p, int locked) +{ + struct linux_emuldata *em; + + if (locked == EMUL_UNLOCKED) + EMUL_LOCK(&emul_lock); + + em = p->p_emuldata; + + if (em == NULL && locked == EMUL_UNLOCKED) + EMUL_UNLOCK(&emul_lock); + + return (em); +} + +int +linux_proc_init(struct thread *td, pid_t child, int flags) +{ + struct linux_emuldata *em, *p_em; + struct proc *p; + + if (child != 0) { + /* non-exec call */ + MALLOC(em, struct linux_emuldata *, sizeof *em, M_LINUX, M_WAITOK | M_ZERO); + em->pid = child; + if (flags & CLONE_VM) { + /* handled later in the code */ + } else { + struct linux_emuldata_shared *s; + + MALLOC(s, struct linux_emuldata_shared *, sizeof *s, M_LINUX, M_WAITOK | M_ZERO); + em->shared = s; + s->refs = 1; + s->group_pid = child; + + LIST_INIT(&s->threads); + } + p = pfind(child); + if (p == NULL) + panic("process not found in proc_init\n"); + p->p_emuldata = em; + PROC_UNLOCK(p); + } else { + /* lookup the old one */ + em = em_find(td->td_proc, EMUL_UNLOCKED); + KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n")); + } + + em->child_clear_tid = NULL; + em->child_set_tid = NULL; + + /* allocate the shared struct only in clone()/fork cases + * in the case of clone() td = calling proc and child = pid of + * the newly created proc + */ + if (child != 0) { + if (flags & CLONE_VM) { + /* lookup the parent */ + p_em = em_find(td->td_proc, EMUL_LOCKED); + KASSERT(p_em != NULL, ("proc_init: parent emuldata not found for CLONE_VM\n")); + em->shared = p_em->shared; + em->shared->refs++; + } else { + /* handled earlier to avoid malloc(M_WAITOK) with rwlock held */ + } + } + + + if (child != 0) { + EMUL_SHARED_WLOCK(&emul_shared_lock); + LIST_INSERT_HEAD(&em->shared->threads, em, threads); + EMUL_SHARED_WUNLOCK(&emul_shared_lock); + + p = pfind(child); + PROC_UNLOCK(p); + /* we might have a sleeping linux_schedtail */ + wakeup(&p->p_emuldata); + } else + EMUL_UNLOCK(&emul_lock); + + return (0); +} + +void +linux_proc_exit(void *arg __unused, struct proc *p) +{ + struct linux_emuldata *em; + int error; + struct thread *td = FIRST_THREAD_IN_PROC(p); + int *child_clear_tid; + + if (__predict_true(p->p_sysent != &elf_linux_sysvec)) + return; + + /* find the emuldata */ + em = em_find(p, EMUL_UNLOCKED); + + KASSERT(em != NULL, ("proc_exit: emuldata not found.\n")); + + child_clear_tid = em->child_clear_tid; + + EMUL_UNLOCK(&emul_lock); + + EMUL_SHARED_WLOCK(&emul_shared_lock); + LIST_REMOVE(em, threads); + + PROC_LOCK(p); + p->p_emuldata = NULL; + PROC_UNLOCK(p); + + em->shared->refs--; + if (em->shared->refs == 0) + FREE(em->shared, M_LINUX); + EMUL_SHARED_WUNLOCK(&emul_shared_lock); + + if (child_clear_tid != NULL) { + struct linux_sys_futex_args cup; + int null = 0; + + error = copyout(&null, child_clear_tid, sizeof(null)); + if (error) + return; + + /* futexes stuff */ + cup.uaddr = child_clear_tid; + cup.op = LINUX_FUTEX_WAKE; + cup.val = 0x7fffffff; /* Awake everyone */ + cup.timeout = NULL; + cup.uaddr2 = NULL; + cup.val3 = 0; + error = linux_sys_futex(FIRST_THREAD_IN_PROC(p), &cup); + /* this cannot happen at the moment and if this happens + * it probably mean there is a userspace bug + */ + if (error) + printf(LMSG("futex stuff in proc_exit failed.\n")); + } + + /* clean the stuff up */ + FREE(em, M_LINUX); +} + +/* This is used in a case of transition from FreeBSD binary execing to linux binary + * in this case we create linux emuldata proc entry with the pid of the currently running + * process. + */ +void linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp) +{ + if (__predict_false(imgp->sysent == &elf_linux_sysvec + && p->p_sysent != &elf_linux_sysvec)) + linux_proc_init(FIRST_THREAD_IN_PROC(p), p->p_pid, 0); + if (__predict_false(imgp->sysent != &elf_linux_sysvec + && p->p_sysent == &elf_linux_sysvec)) { + struct linux_emuldata *em; + + em = em_find(p, EMUL_UNLOCKED); + + KASSERT(em != NULL, ("proc_exec: emuldata not found.\n")); + + EMUL_UNLOCK(&emul_lock); + + EMUL_SHARED_WLOCK(&emul_shared_lock); + LIST_REMOVE(em, threads); + + PROC_LOCK(p); + p->p_emuldata = NULL; + PROC_UNLOCK(p); + + em->shared->refs--; + if (em->shared->refs == 0) + FREE(em->shared, M_LINUX); + EMUL_SHARED_WUNLOCK(&emul_shared_lock); + + FREE(em, M_LINUX); + } +} + +extern int hz; /* in subr_param.c */ + +void +linux_schedtail(void *arg __unused, struct proc *p) +{ + struct linux_emuldata *em; + int error = 0; +#ifdef DEBUG + struct thread *td = FIRST_THREAD_IN_PROC(p); +#endif + int *child_set_tid; + + if (p->p_sysent != &elf_linux_sysvec) + return; + +retry: + /* find the emuldata */ + em = em_find(p, EMUL_UNLOCKED); + + if (em == NULL) { + /* We might have been called before proc_init for this process so + * tsleep and be woken up by it. We use p->p_emuldata for this + */ + + error = tsleep(&p->p_emuldata, PLOCK, "linux_schedtail", hz); + if (error == 0) + goto retry; + panic("no emuldata found for userreting process.\n"); + } + child_set_tid = em->child_set_tid; + EMUL_UNLOCK(&emul_lock); + + if (child_set_tid != NULL) + error = copyout(&p->p_pid, (int *) child_set_tid, sizeof(p->p_pid)); + + return; +} + +int +linux_set_tid_address(struct thread *td, struct linux_set_tid_address_args *args) +{ + struct linux_emuldata *em; + +#ifdef DEBUG + if (ldebug(set_tid_address)) + printf(ARGS(set_tid_address, "%p"), args->tidptr); +#endif + + /* find the emuldata */ + em = em_find(td->td_proc, EMUL_UNLOCKED); + + KASSERT(em != NULL, ("set_tid_address: emuldata not found.\n")); + + em->child_clear_tid = args->tidptr; + td->td_retval[0] = td->td_proc->p_pid; + + EMUL_UNLOCK(&emul_lock); + return 0; +} diff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h new file mode 100644 index 0000000..2eafb83 --- /dev/null +++ b/sys/compat/linux/linux_emul.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2006 Roman Divacky + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUX_EMUL_H_ +#define _LINUX_EMUL_H_ + +struct linux_emuldata_shared { + int refs; + pid_t group_pid; + + LIST_HEAD(, linux_emuldata) threads; /* head of list of linux threads */ +}; + +/* modeled after similar structure in NetBSD + * this will be extended as we need more functionality + */ +struct linux_emuldata { + pid_t pid; + + int *child_set_tid; /* in clone(): Child's TID to set on clone */ + int *child_clear_tid; /* in clone(): Child's TID to clear on exit */ + + struct linux_emuldata_shared *shared; + + LIST_ENTRY(linux_emuldata) threads; /* list of linux threads */ +}; + +struct linux_emuldata *em_find(struct proc *, int locked); + +#define EMUL_LOCK(l) sx_xlock(l) +#define EMUL_UNLOCK(l) sx_xunlock(l) + +#define EMUL_SHARED_RLOCK(l) sx_slock(l) +#define EMUL_SHARED_RUNLOCK(l) sx_sunlock(l) +#define EMUL_SHARED_WLOCK(l) sx_xlock(l) +#define EMUL_SHARED_WUNLOCK(l) sx_xunlock(l) + +/* for em_find use */ +#define EMUL_LOCKED 1 +#define EMUL_UNLOCKED 0 + +int linux_proc_init(struct thread *, pid_t, int); +void linux_proc_exit(void *, struct proc *); +void linux_schedtail(void *, struct proc *); +void linux_proc_exec(void *, struct proc *, struct image_params *); + +#endif /* !_LINUX_EMUL_H_ */ diff --git a/sys/compat/linux/linux_futex.c b/sys/compat/linux/linux_futex.c new file mode 100644 index 0000000..889d29d --- /dev/null +++ b/sys/compat/linux/linux_futex.c @@ -0,0 +1,500 @@ +/* $NetBSD: linux_futex.c,v 1.5 2005/11/23 16:14:57 manu Exp $ */ + +/*- + * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Emmanuel Dreyfus + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#if 0 + __KERNEL_RCSID(1, "$NetBSD: linux_futex.c,v 1.5 2005/11/23 16:14:57 manu Exp $"); +#endif + +#include "opt_compat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef COMPAT_LINUX32 +#include +#include +#else +#include +#include +#endif +#include + +struct futex; + +struct waiting_proc { + struct thread *wp_t; + struct futex *wp_new_futex; + TAILQ_ENTRY(waiting_proc) wp_list; +}; +struct futex { + void *f_uaddr; + int f_refcount; + LIST_ENTRY(futex) f_list; + TAILQ_HEAD(lf_waiting_proc, waiting_proc) f_waiting_proc; +}; + +LIST_HEAD(futex_list, futex) futex_list; +struct mtx futex_mtx; /* this protects the LIST of futexes */ + +#define FUTEX_LOCK mtx_lock(&futex_mtx) +#define FUTEX_UNLOCK mtx_unlock(&futex_mtx) + +#define FUTEX_LOCKED 1 +#define FUTEX_UNLOCKED 0 + +#define FUTEX_SYSTEM_LOCK mtx_lock(&Giant) +#define FUTEX_SYSTEM_UNLOCK mtx_unlock(&Giant) + +static struct futex *futex_get(void *, int); +static void futex_put(struct futex *); +static int futex_sleep(struct futex *, struct thread *, unsigned long); +static int futex_wake(struct futex *, int, struct futex *); +#ifdef __i386__ +static int futex_atomic_op(struct thread *td, int encoded_op, caddr_t uaddr); +#endif + +/* support.s */ +int futex_xchgl(int oparg, caddr_t uaddr, int *oldval); +int futex_addl(int oparg, caddr_t uaddr, int *oldval); +int futex_orl(int oparg, caddr_t uaddr, int *oldval); +int futex_andnl(int oparg, caddr_t uaddr, int *oldval); +int futex_xorl(int oparg, caddr_t uaddr, int *oldval); + +int +linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args) +{ + int val; + int ret; + struct l_timespec timeout = { 0, 0 }; + int error = 0; + struct futex *f; + struct futex *newf; + int timeout_hz; + struct timeval tv = {0, 0}; +#ifdef __i386__ + struct futex *f2; + int op_ret; +#endif + +#ifdef DEBUG + if (ldebug(sys_futex)) + printf(ARGS(futex,"%p, %i, %i"), args->uaddr, args->op, args->val); +#endif + + switch (args->op) { + case LINUX_FUTEX_WAIT: + FUTEX_SYSTEM_LOCK; + + if ((error = copyin(args->uaddr, + &val, sizeof(val))) != 0) { + FUTEX_SYSTEM_UNLOCK; + return error; + } + + if (val != args->val) { + FUTEX_SYSTEM_UNLOCK; + return EWOULDBLOCK; + } + + if (args->timeout != NULL) { + if ((error = copyin(args->timeout, + &timeout, sizeof(timeout))) != 0) { + FUTEX_SYSTEM_UNLOCK; + return error; + } + } + +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX_WAIT %d: val = %d, uaddr = %p, " + "*uaddr = %d, timeout = %d.%09ld\n", + td->td_proc->p_pid, args->val, + args->uaddr, val, timeout.tv_sec, timeout.tv_nsec); +#endif + tv.tv_usec = timeout.tv_sec * 1000000 + timeout.tv_nsec / 1000; + timeout_hz = tvtohz(&tv); + + if (timeout.tv_sec == 0 && timeout.tv_nsec == 0) + timeout_hz = 0; + /* + * If the user process requests a non null timeout, + * make sure we do not turn it into an infinite + * timeout because timeout_hz gets null. + * + * We use a minimal timeout of 1/hz. Maybe it would + * make sense to just return ETIMEDOUT without sleeping. + */ + if (((timeout.tv_sec != 0) || (timeout.tv_nsec != 0)) && + (timeout_hz == 0)) + timeout_hz = 1; + + + f = futex_get(args->uaddr, FUTEX_UNLOCKED); + ret = futex_sleep(f, td, timeout_hz); + futex_put(f); + +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX_WAIT %d: uaddr = %p, " + "ret = %d\n", td->td_proc->p_pid, args->uaddr, ret); +#endif + + FUTEX_SYSTEM_UNLOCK; + switch (ret) { + case EWOULDBLOCK: /* timeout */ + return ETIMEDOUT; + break; + case EINTR: /* signal */ + return EINTR; + break; + case 0: /* FUTEX_WAKE received */ +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX_WAIT %d: uaddr = %p, got FUTEX_WAKE\n", + td->td_proc->p_pid, args->uaddr); +#endif + return 0; + break; + default: +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX_WAIT: unexpected ret = %d\n", ret); +#endif + break; + } + + /* NOTREACHED */ + break; + + case LINUX_FUTEX_WAKE: + FUTEX_SYSTEM_LOCK; + + /* + * XXX: Linux is able cope with different addresses + * corresponding to the same mapped memory in the sleeping + * and the waker process. + */ +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX_WAKE %d: uaddr = %p, val = %d\n", + td->td_proc->p_pid, args->uaddr, args->val); +#endif + f = futex_get(args->uaddr, FUTEX_UNLOCKED); + td->td_retval[0] = futex_wake(f, args->val, NULL); + futex_put(f); + + FUTEX_SYSTEM_UNLOCK; + break; + + case LINUX_FUTEX_CMP_REQUEUE: + FUTEX_SYSTEM_LOCK; + + if ((error = copyin(args->uaddr, + &val, sizeof(val))) != 0) { + FUTEX_SYSTEM_UNLOCK; + return error; + } + + if (val != args->val3) { + FUTEX_SYSTEM_UNLOCK; + return EAGAIN; + } + + f = futex_get(args->uaddr, FUTEX_UNLOCKED); + newf = futex_get(args->uaddr2, FUTEX_UNLOCKED); + td->td_retval[0] = futex_wake(f, args->val, newf); + futex_put(f); + futex_put(newf); + + FUTEX_SYSTEM_UNLOCK; + break; + + case LINUX_FUTEX_REQUEUE: + FUTEX_SYSTEM_LOCK; + + f = futex_get(args->uaddr, FUTEX_UNLOCKED); + newf = futex_get(args->uaddr2, FUTEX_UNLOCKED); + td->td_retval[0] = futex_wake(f, args->val, newf); + futex_put(f); + futex_put(newf); + + FUTEX_SYSTEM_UNLOCK; + break; + + case LINUX_FUTEX_FD: + printf("linux_sys_futex: unimplemented op %d\n", + args->op); + break; + + case LINUX_FUTEX_WAKE_OP: +#ifdef __i386__ + FUTEX_SYSTEM_LOCK; +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX_WAKE_OP: %d: uaddr = %p, op = %d, val = %d, uaddr2 = %p, val3 = %d\n", + td->td_proc->p_pid, args->uaddr, args->op, args->val, args->uaddr2, args->val3); +#endif + f = futex_get(args->uaddr, FUTEX_UNLOCKED); + f2 = futex_get(args->uaddr2, FUTEX_UNLOCKED); + + /* This function returns positive number as results + * and negative as errors + */ + op_ret = futex_atomic_op(td, args->val3, args->uaddr2); + if (op_ret < 0) { + + /* XXX: we dont handle the EFAULT yet */ + if (op_ret != -EFAULT) { + futex_put(f); + futex_put(f2); + FUTEX_SYSTEM_UNLOCK; + return (-op_ret); + } + + futex_put(f); + futex_put(f2); + + FUTEX_SYSTEM_UNLOCK; + return (EFAULT); + + } + + ret = futex_wake(f, args->val, NULL); + futex_put(f); + if (op_ret > 0) { + printf("second wakeup\n"); + op_ret = 0; + /* Linux always puts there 0 retries */ + op_ret += futex_wake(f2, 0, NULL); + ret += op_ret; + } + futex_put(f2); + td->td_retval[0] = ret; + + FUTEX_SYSTEM_UNLOCK; +#else + printf("linux_sys_futex: wake_op not implemented"); +#endif + break; + + default: + printf("linux_sys_futex: unknown op %d\n", + args->op); + break; + } + return 0; +} + +static struct futex * +futex_get(void *uaddr, int locked) +{ + struct futex *f; + + if (locked == FUTEX_UNLOCKED) + FUTEX_LOCK; + LIST_FOREACH(f, &futex_list, f_list) { + if (f->f_uaddr == uaddr) { + f->f_refcount++; + if (locked == FUTEX_UNLOCKED) + FUTEX_UNLOCK; + return f; + } + } + if (locked == FUTEX_UNLOCKED) + FUTEX_UNLOCK; + + /* Not found, create it */ + f = malloc(sizeof(*f), M_LINUX, M_WAITOK); + f->f_uaddr = uaddr; + f->f_refcount = 1; + TAILQ_INIT(&f->f_waiting_proc); + if (locked == FUTEX_UNLOCKED) + FUTEX_LOCK; + LIST_INSERT_HEAD(&futex_list, f, f_list); + if (locked == FUTEX_UNLOCKED) + FUTEX_UNLOCK; + + return f; +} + +static void +futex_put(f) + struct futex *f; +{ + FUTEX_LOCK; + f->f_refcount--; + if (f->f_refcount == 0) { + LIST_REMOVE(f, f_list); + free(f, M_LINUX); + } + FUTEX_UNLOCK; + + return; +} + +static int +futex_sleep(struct futex *f, struct thread *td, unsigned long timeout) +{ + struct waiting_proc *wp; + int ret; + + wp = malloc(sizeof(*wp), M_LINUX, M_WAITOK); + wp->wp_t = td; + wp->wp_new_futex = NULL; + FUTEX_LOCK; + TAILQ_INSERT_TAIL(&f->f_waiting_proc, wp, wp_list); + FUTEX_UNLOCK; + +#ifdef DEBUG + if (ldebug(sys_futex)) + printf("FUTEX --> %d tlseep timeout = %ld\n", td->td_proc->p_pid, + timeout); +#endif + ret = tsleep(wp, PCATCH|PZERO, "linuxfutex", timeout); + + FUTEX_LOCK; + TAILQ_REMOVE(&f->f_waiting_proc, wp, wp_list); + FUTEX_UNLOCK; + + if ((ret == 0) && (wp->wp_new_futex != NULL)) { + ret = futex_sleep(wp->wp_new_futex, td, timeout); + futex_put(wp->wp_new_futex); /* futex_get called in wakeup */ + } + + free(wp, M_LINUX); + + return ret; +} + +static int +futex_wake(struct futex *f, int n, struct futex *newf) +{ + struct waiting_proc *wp; + int count = 0; + + FUTEX_LOCK; + TAILQ_FOREACH(wp, &f->f_waiting_proc, wp_list) { + if (count <= n) { + wakeup(wp); + count++; + } else { + if (newf != NULL) { + /* futex_put called after tsleep */ + wp->wp_new_futex = futex_get(newf->f_uaddr, FUTEX_LOCKED); + wakeup(wp); + } + } + } + FUTEX_UNLOCK; + + return count; +} + +#ifdef __i386__ +static int +futex_atomic_op(struct thread *td, int encoded_op, caddr_t uaddr) +{ + int op = (encoded_op >> 28) & 7; + int cmp = (encoded_op >> 24) & 15; + int oparg = (encoded_op << 8) >> 20; + int cmparg = (encoded_op << 20) >> 20; + int oldval = 0, ret; + + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) + oparg = 1 << oparg; + +#ifdef DEBUG + printf("futex_atomic_op: op = %d, cmp = %d, oparg = %d, cmparg = %d, uaddr = %p\n", + op, cmp, oparg, cmparg, uaddr); +#endif + /* XXX: linux verifies access here and returns EFAULT */ + + critical_enter(); + + switch (op) { + case FUTEX_OP_SET: + ret = futex_xchgl(oparg, uaddr, &oldval); + break; + case FUTEX_OP_ADD: + ret = futex_addl(oparg, uaddr, &oldval); + break; + case FUTEX_OP_OR: + ret = futex_orl(oparg, uaddr, &oldval); + break; + case FUTEX_OP_ANDN: + ret = futex_andnl(oparg, uaddr, &oldval); + break; + case FUTEX_OP_XOR: + ret = futex_xorl(oparg, uaddr, &oldval); + break; + default: + ret = -ENOSYS; + } + + critical_exit(); + + if (!ret) + switch (cmp) { + case FUTEX_OP_CMP_EQ: + ret = (oldval == cmparg); + break; + case FUTEX_OP_CMP_NE: + ret = (oldval != cmparg); + break; + case FUTEX_OP_CMP_LT: + ret = (oldval < cmparg); + break; + case FUTEX_OP_CMP_GE: + ret = (oldval >= cmparg); + break; + case FUTEX_OP_CMP_LE: + ret = (oldval <= cmparg); + break; + case FUTEX_OP_CMP_GT: + ret = (oldval > cmparg); + break; + default: ret = -ENOSYS; + } + + return (ret); +} +#endif diff --git a/sys/compat/linux/linux_futex.h b/sys/compat/linux/linux_futex.h new file mode 100644 index 0000000..8eafee1 --- /dev/null +++ b/sys/compat/linux/linux_futex.h @@ -0,0 +1,61 @@ +/* $NetBSD: linux_futex.h,v 1.2 2005/12/11 12:20:19 christos Exp $ */ + +/*- + * Copyright (c) 2005 Emmanuel Dreyfus, all rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Emmanuel Dreyfus + * 4. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _LINUX_FUTEX_H +#define _LINUX_FUTEX_H + +#define LINUX_FUTEX_WAIT 0 +#define LINUX_FUTEX_WAKE 1 +#define LINUX_FUTEX_FD 2 +#define LINUX_FUTEX_REQUEUE 3 +#define LINUX_FUTEX_CMP_REQUEUE 4 +#define LINUX_FUTEX_WAKE_OP 5 + +#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */ +#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */ +#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */ +#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */ +#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */ + +#define FUTEX_OP_OPARG_SHIFT 8 /* Use (1 << OPARG) instead of OPARG. */ + +#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */ +#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */ +#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */ +#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */ +#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */ +#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */ + +#endif /* !_LINUX_FUTEX_H */ diff --git a/sys/compat/linux/linux_time.c b/sys/compat/linux/linux_time.c new file mode 100644 index 0000000..aee0554 --- /dev/null +++ b/sys/compat/linux/linux_time.c @@ -0,0 +1,208 @@ +/* $NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Emmanuel Dreyfus. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); +#if 0 +__KERNEL_RCSID(0, "$NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $"); +#endif + +#include "opt_compat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef COMPAT_LINUX32 +#include +#include +#else +#include +#include +#endif + +static void native_to_linux_timespec(struct l_timespec *, + struct timespec *); +static void linux_to_native_timespec(struct timespec *, + struct l_timespec *); +static int linux_to_native_clockid(clockid_t *, clockid_t); + +static void +native_to_linux_timespec(struct l_timespec *ltp, struct timespec *ntp) +{ + ltp->tv_sec = ntp->tv_sec; + ltp->tv_nsec = ntp->tv_nsec; +} + +static void +linux_to_native_timespec(struct timespec *ntp, struct l_timespec *ltp) +{ + ntp->tv_sec = ltp->tv_sec; + ntp->tv_nsec = ltp->tv_nsec; +} + +static int +linux_to_native_clockid(clockid_t *n, clockid_t l) +{ + switch (l) { + case LINUX_CLOCK_REALTIME: + *n = CLOCK_REALTIME; + break; + case LINUX_CLOCK_MONOTONIC: + *n = CLOCK_MONOTONIC; + break; + case LINUX_CLOCK_PROCESS_CPUTIME_ID: + case LINUX_CLOCK_THREAD_CPUTIME_ID: + case LINUX_CLOCK_REALTIME_HR: + case LINUX_CLOCK_MONOTONIC_HR: + return EINVAL; + } + + return 0; +} + +int +linux_clock_gettime(struct thread *td, struct linux_clock_gettime_args *args) +{ + struct l_timespec lts; + int error; + clockid_t nwhich = 0; /* XXX: GCC */ + struct timespec tp; + + error = linux_to_native_clockid(&nwhich, args->which); + if (error != 0) + return error; + + error = kern_clock_gettime(td, nwhich, &tp); + if (error != 0) + return error; + + native_to_linux_timespec(<s, &tp); + + return copyout(<s, args->tp, sizeof lts); +} + +int +linux_clock_settime(struct thread *td, struct linux_clock_settime_args *args) +{ + struct timespec ts; + struct l_timespec lts; + int error; + clockid_t nwhich = 0; /* XXX: GCC */ + + error = linux_to_native_clockid(&nwhich, args->which); + if (error != 0) + return error; + + error = copyin(args->tp, <s, sizeof lts); + if (error != 0) + return error; + + linux_to_native_timespec(&ts, <s); + + return kern_clock_settime(td, nwhich, &ts); +} + +int +linux_clock_getres(struct thread *td, struct linux_clock_getres_args *args) +{ + struct timespec ts; + struct l_timespec lts; + int error; + clockid_t nwhich = 0; /* XXX: GCC */ + + if (args->tp == NULL) + return (0); + + error = linux_to_native_clockid(&nwhich, args->which); + if (error != 0) + return error; + + error = kern_clock_getres(td, nwhich, &ts); + if (error != 0) + return error; + + native_to_linux_timespec(<s, &ts); + + return copyout(<s, args->tp, sizeof lts); +} + +int +linux_clock_nanosleep(struct thread *td, struct linux_clock_nanosleep_args *args) +{ + struct timespec *rmtp; + struct l_timespec lrqts, lrmts; + struct timespec rqts, rmts; + int error; + + if (args->flags != 0) + return EINVAL; /* XXX deal with TIMER_ABSTIME */ + + if (args->which != LINUX_CLOCK_REALTIME) + return EINVAL; + + error = copyin(args->rqtp, &lrqts, sizeof lrqts); + if (error != 0) + return error; + + if (args->rmtp != NULL) + rmtp = &rmts; + else + rmtp = NULL; + + linux_to_native_timespec(&rqts, &lrqts); + + error = kern_nanosleep(td, &rqts, rmtp); + if (error != 0) + return error; + if (args->rmtp != NULL) { + native_to_linux_timespec(&lrmts, rmtp); + error = copyout(&lrmts, args->rmtp, sizeof lrmts ); + if (error != 0) + return error; + } + + return 0; +} -- cgit v1.1