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}.*)
This commit is contained in:
Alexander Leidinger 2006-08-15 12:20:59 +00:00
parent fb5592c084
commit ad2056f2c4
5 changed files with 1140 additions and 0 deletions

View File

@ -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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/imgact.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/sx.h>
#include <sys/proc.h>
#include <sys/syscallsubr.h>
#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>
#else
#include <machine/../linux/linux.h>
#include <machine/../linux/linux_proto.h>
#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;
}

View File

@ -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_ */

View File

@ -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 <sys/cdefs.h>
__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 <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
#ifdef COMPAT_LINUX32
#include <machine/../linux32/linux.h>
#include <machine/../linux32/linux32_proto.h>
#else
#include <machine/../linux/linux.h>
#include <machine/../linux/linux_proto.h>
#endif
#include <compat/linux/linux_futex.h>
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

View File

@ -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 */

View File

@ -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 <sys/cdefs.h>
__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 <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#include <sys/signal.h>
#include <sys/stdint.h>
#include <sys/syscallsubr.h>
#include <sys/sysproto.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/proc.h>
#ifdef COMPAT_LINUX32
#include <machine/../linux32/linux.h>
#include <machine/../linux32/linux32_proto.h>
#else
#include <machine/../linux/linux.h>
#include <machine/../linux/linux_proto.h>
#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(&lts, &tp);
return copyout(&lts, 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, &lts, sizeof lts);
if (error != 0)
return error;
linux_to_native_timespec(&ts, &lts);
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(&lts, &ts);
return copyout(&lts, 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;
}