MFC: Support for running 32-bit multithreaded binaries using libthr on
amd64 including: - Add 32-bit wrappers for thr_new(), thr_suspend(), and the umtx system calls. - Add support to amd64 for constructing thread upcalls for 32-bit processes. - Leave %fs and %gs alone in the signal trampoline for 32-bit processes on amd64. - Add 'casuword32()' to amd64 and ia64. Tested by: emaste
This commit is contained in:
parent
952376d89d
commit
9d52499b7d
@ -313,6 +313,34 @@ copyin_fault:
|
||||
movq $EFAULT,%rax
|
||||
ret
|
||||
|
||||
/*
|
||||
* casuword32. Compare and set user integer. Returns -1 or the current value.
|
||||
* dst = %rdi, old = %rsi, new = %rdx
|
||||
*/
|
||||
ENTRY(casuword32)
|
||||
movq PCPU(CURPCB),%rcx
|
||||
movq $fusufault,PCB_ONFAULT(%rcx)
|
||||
|
||||
movq $VM_MAXUSER_ADDRESS-4,%rax
|
||||
cmpq %rax,%rdi /* verify address is valid */
|
||||
ja fusufault
|
||||
|
||||
movl %esi,%eax /* old */
|
||||
#ifdef SMP
|
||||
lock
|
||||
#endif
|
||||
cmpxchgl %edx,(%rdi) /* new = %edx */
|
||||
|
||||
/*
|
||||
* The old value is in %eax. If the store succeeded it will be the
|
||||
* value we expected (old) from before the store, otherwise it will
|
||||
* be the current value.
|
||||
*/
|
||||
|
||||
movq PCPU(CURPCB),%rcx
|
||||
movq $0,PCB_ONFAULT(%rcx)
|
||||
ret
|
||||
|
||||
/*
|
||||
* casuptr. Compare and set user pointer. Returns -1 or the current value.
|
||||
* dst = %rdi, old = %rsi, new = %rdx
|
||||
|
@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_isa.h"
|
||||
#include "opt_cpu.h"
|
||||
#include "opt_compat.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
@ -69,6 +70,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/pcb.h>
|
||||
#include <machine/specialreg.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_extern.h>
|
||||
@ -79,6 +81,12 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <amd64/isa/isa.h>
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
|
||||
extern struct sysentvec ia32_freebsd_sysvec;
|
||||
|
||||
#endif
|
||||
|
||||
static void cpu_reset_real(void);
|
||||
#ifdef SMP
|
||||
static void cpu_reset_proxy(void);
|
||||
@ -320,6 +328,28 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg,
|
||||
*/
|
||||
cpu_thread_clean(td);
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
if (td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
|
||||
/*
|
||||
* Set the trap frame to point at the beginning of the uts
|
||||
* function.
|
||||
*/
|
||||
td->td_frame->tf_rbp = 0;
|
||||
td->td_frame->tf_rsp =
|
||||
(((uintptr_t)stack->ss_sp + stack->ss_size - 4) & ~0x0f) - 4;
|
||||
td->td_frame->tf_rip = (uintptr_t)entry;
|
||||
|
||||
/*
|
||||
* Pass the address of the mailbox for this kse to the uts
|
||||
* function as a parameter on the stack.
|
||||
*/
|
||||
suword32((void *)(td->td_frame->tf_rsp + sizeof(int32_t)),
|
||||
(uint32_t)(uintptr_t)arg);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set the trap frame to point at the beginning of the uts
|
||||
* function.
|
||||
@ -328,7 +358,6 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg,
|
||||
td->td_frame->tf_rsp =
|
||||
((register_t)stack->ss_sp + stack->ss_size) & ~0x0f;
|
||||
td->td_frame->tf_rsp -= 8;
|
||||
td->td_frame->tf_rbp = 0;
|
||||
td->td_frame->tf_rip = (register_t)entry;
|
||||
|
||||
/*
|
||||
@ -345,6 +374,19 @@ cpu_set_user_tls(struct thread *td, void *tls_base)
|
||||
if ((u_int64_t)tls_base >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
if (td->td_proc->p_sysent == &ia32_freebsd_sysvec) {
|
||||
if (td == curthread) {
|
||||
critical_enter();
|
||||
td->td_pcb->pcb_gsbase = (register_t)tls_base;
|
||||
wrmsr(MSR_KGSBASE, td->td_pcb->pcb_gsbase);
|
||||
critical_exit();
|
||||
} else {
|
||||
td->td_pcb->pcb_gsbase = (register_t)tls_base;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
if (td == curthread) {
|
||||
critical_enter();
|
||||
td->td_pcb->pcb_fsbase = (register_t)tls_base;
|
||||
|
@ -45,8 +45,6 @@ ia32_sigcode:
|
||||
calll *IA32_SIGF_HANDLER(%esp)
|
||||
leal IA32_SIGF_UC(%esp),%eax /* get ucontext */
|
||||
pushl %eax
|
||||
movl IA32_UC_GS(%eax),%gs /* restore %gs */
|
||||
movl IA32_UC_FS(%eax),%fs /* restore %fs */
|
||||
movl IA32_UC_ES(%eax),%es /* restore %es */
|
||||
movl IA32_UC_DS(%eax),%ds /* restore %ds */
|
||||
movl $SYS_sigreturn,%eax
|
||||
@ -62,8 +60,6 @@ freebsd4_ia32_sigcode:
|
||||
calll *IA32_SIGF_HANDLER(%esp)
|
||||
leal IA32_SIGF_UC4(%esp),%eax/* get ucontext */
|
||||
pushl %eax
|
||||
movl IA32_UC4_GS(%eax),%gs /* restore %gs */
|
||||
movl IA32_UC4_FS(%eax),%fs /* restore %fs */
|
||||
movl IA32_UC4_ES(%eax),%es /* restore %es */
|
||||
movl IA32_UC4_DS(%eax),%ds /* restore %ds */
|
||||
movl $344,%eax /* 4.x SYS_sigreturn */
|
||||
|
@ -103,4 +103,17 @@ struct statfs32 {
|
||||
int32_t f_spare[2];
|
||||
};
|
||||
|
||||
struct thr_param32 {
|
||||
uint32_t start_func;
|
||||
uint32_t arg;
|
||||
uint32_t stack_base;
|
||||
uint32_t stack_size;
|
||||
uint32_t tls_base;
|
||||
uint32_t tls_size;
|
||||
uint32_t child_tid;
|
||||
uint32_t parent_tid;
|
||||
int32_t flags;
|
||||
uint32_t spare[4];
|
||||
};
|
||||
|
||||
#endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */
|
||||
|
@ -2115,6 +2115,60 @@ freebsd32_clock_getres(struct thread *td,
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
freebsd32_thr_new(struct thread *td,
|
||||
struct freebsd32_thr_new_args *uap)
|
||||
{
|
||||
struct thr_param32 param32;
|
||||
struct thr_param param;
|
||||
int error;
|
||||
|
||||
if (uap->param_size < 0 ||
|
||||
uap->param_size > sizeof(struct thr_param32))
|
||||
return (EINVAL);
|
||||
bzero(¶m, sizeof(struct thr_param));
|
||||
bzero(¶m32, sizeof(struct thr_param32));
|
||||
error = copyin(uap->param, ¶m32, uap->param_size);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
param.start_func = PTRIN(param32.start_func);
|
||||
param.arg = PTRIN(param32.arg);
|
||||
param.stack_base = PTRIN(param32.stack_base);
|
||||
param.stack_size = param32.stack_size;
|
||||
param.tls_base = PTRIN(param32.tls_base);
|
||||
param.tls_size = param32.tls_size;
|
||||
param.child_tid = PTRIN(param32.child_tid);
|
||||
param.parent_tid = PTRIN(param32.parent_tid);
|
||||
param.flags = param32.flags;
|
||||
param.spare[0] = PTRIN(param32.spare[0]);
|
||||
param.spare[1] = PTRIN(param32.spare[1]);
|
||||
param.spare[2] = PTRIN(param32.spare[2]);
|
||||
param.spare[3] = PTRIN(param32.spare[3]);
|
||||
|
||||
return (kern_thr_new(td, ¶m));
|
||||
}
|
||||
|
||||
int
|
||||
freebsd32_thr_suspend(struct thread *td, struct freebsd32_thr_suspend_args *uap)
|
||||
{
|
||||
struct timespec32 ts32;
|
||||
struct timespec ts, *tsp;
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
tsp = NULL;
|
||||
if (uap->timeout != NULL) {
|
||||
error = copyin((const void *)uap->timeout, (void *)&ts32,
|
||||
sizeof(struct timespec32));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
ts.tv_sec = ts32.tv_sec;
|
||||
ts.tv_nsec = ts32.tv_nsec;
|
||||
tsp = &ts;
|
||||
}
|
||||
return (kern_thr_suspend(td, tsp));
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
int
|
||||
|
@ -718,21 +718,20 @@
|
||||
428 AUE_NULL UNIMPL __acl_aclcheck_link
|
||||
; XXX implement
|
||||
429 AUE_SIGWAIT UNIMPL sigwait
|
||||
430 AUE_NULL MNOPROTO { int thr_create(ucontext_t *ctx, long *id, \
|
||||
int flag s); }
|
||||
430 AUE_NULL UNIMPL thr_create
|
||||
431 AUE_NULL MNOPROTO { void thr_exit(long *state); }
|
||||
432 AUE_NULL MNOPROTO { int thr_self(long *id); }
|
||||
433 AUE_NULL MNOPROTO { int thr_kill(long id, int sig); }
|
||||
434 AUE_NULL MNOPROTO { int _umtx_lock(struct umtx *umtx); }
|
||||
435 AUE_NULL MNOPROTO { int _umtx_unlock(struct umtx *umtx); }
|
||||
434 AUE_NULL MSTD { int freebsd32_umtx_lock(struct umtx *umtx); }
|
||||
435 AUE_NULL MSTD { int freebsd32_umtx_unlock(struct umtx *umtx); }
|
||||
436 AUE_NULL MNOPROTO { int jail_attach(int jid); }
|
||||
437 AUE_EXTATTR_LIST_FD UNIMPL extattr_list_fd
|
||||
438 AUE_EXTATTR_LIST_FILE UNIMPL extattr_list_file
|
||||
439 AUE_EXTATTR_LIST_LINK UNIMPL extattr_list_link
|
||||
440 AUE_NULL UNIMPL kse_switchin
|
||||
441 AUE_NULL UNIMPL ksem_timedwait
|
||||
442 AUE_NULL MNOPROTO { int thr_suspend( \
|
||||
const struct timespec *timeout); }
|
||||
442 AUE_NULL MSTD { int freebsd32_thr_suspend( \
|
||||
const struct timespec32 *timeout); }
|
||||
443 AUE_NULL MNOPROTO { int thr_wake(long id); }
|
||||
444 AUE_MODUNLOAD MNOPROTO { int kldunloadf(int fileid, int flags); }
|
||||
445 AUE_AUDIT MNOPROTO { int audit(const void *record, \
|
||||
@ -750,4 +749,9 @@
|
||||
struct auditinfo_addr *auditinfo_addr, \
|
||||
u_int length); }
|
||||
453 AUE_AUDITCTL MNOPROTO { int auditctl(char *path); }
|
||||
454 AUE_NULL UNIMPL _umtx_op
|
||||
454 AUE_NULL MSTD { int freebsd32_umtx_op(struct umtx *umtx, \
|
||||
int op, long id, void *uaddr, \
|
||||
void *uaddr2); }
|
||||
455 AUE_NULL MSTD { int freebsd32_thr_new( \
|
||||
struct thr_param32 *param, \
|
||||
int param_size); }
|
||||
|
@ -241,6 +241,56 @@ ENTRY(casuptr, 3)
|
||||
}
|
||||
END(casuptr)
|
||||
|
||||
/*
|
||||
* casuword32(int32_t *p, int32_t old, int32_t new)
|
||||
* Perform a 32-bit compare-exchange in user space.
|
||||
*/
|
||||
ENTRY(casuword32, 3)
|
||||
{ .mlx
|
||||
add r15=PC_CURTHREAD,r13
|
||||
movl r14=VM_MAX_ADDRESS
|
||||
;;
|
||||
}
|
||||
{ .mib
|
||||
ld8 r15=[r15] // r15 = curthread
|
||||
cmp.geu p6,p0=in0,r14
|
||||
(p6) br.dpnt.few 1f
|
||||
;;
|
||||
}
|
||||
{ .mlx
|
||||
add r15=TD_PCB,r15
|
||||
movl r14=fusufault
|
||||
;;
|
||||
}
|
||||
{ .mmi
|
||||
ld8 r15=[r15] // r15 = PCB
|
||||
;;
|
||||
mov ar.ccv=in1
|
||||
add r15=PCB_ONFAULT,r15
|
||||
;;
|
||||
}
|
||||
{ .mmi
|
||||
st8 [r15]=r14 // Set onfault
|
||||
;;
|
||||
cmpxchg4.rel ret0=[in0],in2,ar.ccv
|
||||
nop 0
|
||||
;;
|
||||
}
|
||||
{ .mfb
|
||||
st8.rel [r15]=r0 // Clear onfault
|
||||
nop 0
|
||||
br.ret.sptk rp
|
||||
;;
|
||||
}
|
||||
1:
|
||||
{ .mfb
|
||||
add ret0=-1,r0
|
||||
nop 0
|
||||
br.ret.sptk rp
|
||||
;;
|
||||
}
|
||||
END(casuword32)
|
||||
|
||||
/*
|
||||
* subyte(void *addr, int byte)
|
||||
* suword16(void *addr, int word)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_compat.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
@ -36,6 +37,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/sched.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/smp.h>
|
||||
#include <sys/syscallsubr.h>
|
||||
#include <sys/sysent.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sysproto.h>
|
||||
@ -46,6 +48,26 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <machine/frame.h>
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
|
||||
extern struct sysentvec ia32_freebsd_sysvec;
|
||||
|
||||
static inline int
|
||||
suword_lwpid(void *addr, lwpid_t lwpid)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (curproc->p_sysent != &ia32_freebsd_sysvec)
|
||||
error = suword(addr, lwpid);
|
||||
else
|
||||
error = suword32(addr, lwpid);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#else
|
||||
#define suword_lwpid suword
|
||||
#endif
|
||||
|
||||
extern int max_threads_per_proc;
|
||||
extern int max_groups_per_proc;
|
||||
|
||||
@ -94,9 +116,17 @@ thr_new(struct thread *td, struct thr_new_args *uap)
|
||||
return (EINVAL);
|
||||
if ((error = copyin(uap->param, ¶m, sizeof(param))))
|
||||
return (error);
|
||||
error = create_thread(td, NULL, param.start_func, param.arg,
|
||||
param.stack_base, param.stack_size, param.tls_base,
|
||||
param.child_tid, param.parent_tid, param.flags);
|
||||
return (kern_thr_new(td, ¶m));
|
||||
}
|
||||
|
||||
int
|
||||
kern_thr_new(struct thread *td, struct thr_param *param)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = create_thread(td, NULL, param->start_func, param->arg,
|
||||
param->stack_base, param->stack_size, param->tls_base,
|
||||
param->child_tid, param->parent_tid, param->flags);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -112,7 +142,6 @@ create_thread(struct thread *td, mcontext_t *ctx,
|
||||
struct thread *newtd;
|
||||
struct ksegrp *kg, *newkg;
|
||||
struct proc *p;
|
||||
long id;
|
||||
int error, scope_sys, linkkg;
|
||||
|
||||
error = 0;
|
||||
@ -146,11 +175,10 @@ create_thread(struct thread *td, mcontext_t *ctx,
|
||||
* its storage, because child thread may exit quickly and
|
||||
* memory is freed before parent thread can access it.
|
||||
*/
|
||||
id = newtd->td_tid;
|
||||
if ((child_tid != NULL &&
|
||||
(error = copyout(&id, child_tid, sizeof(long)))) ||
|
||||
suword_lwpid(child_tid, newtd->td_tid)) ||
|
||||
(parent_tid != NULL &&
|
||||
(error = copyout(&id, parent_tid, sizeof(long))))) {
|
||||
suword_lwpid(parent_tid, newtd->td_tid))) {
|
||||
thread_free(newtd);
|
||||
return (error);
|
||||
}
|
||||
@ -257,13 +285,11 @@ int
|
||||
thr_self(struct thread *td, struct thr_self_args *uap)
|
||||
/* long *id */
|
||||
{
|
||||
long id;
|
||||
int error;
|
||||
|
||||
id = td->td_tid;
|
||||
if ((error = copyout(&id, uap->id, sizeof(long))))
|
||||
return (error);
|
||||
|
||||
error = suword_lwpid(uap->id, (unsigned)td->td_tid);
|
||||
if (error == -1)
|
||||
return (EFAULT);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -277,7 +303,7 @@ thr_exit(struct thread *td, struct thr_exit_args *uap)
|
||||
|
||||
/* Signal userland that it can free the stack. */
|
||||
if ((void *)uap->state != NULL) {
|
||||
suword((void *)uap->state, 1);
|
||||
suword_lwpid(uap->state, 1);
|
||||
kern_umtx_wake(td, uap->state, INT_MAX);
|
||||
}
|
||||
|
||||
@ -348,23 +374,34 @@ int
|
||||
thr_suspend(struct thread *td, struct thr_suspend_args *uap)
|
||||
/* const struct timespec *timeout */
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
struct timespec ts, *tsp;
|
||||
int error;
|
||||
int hz;
|
||||
|
||||
hz = 0;
|
||||
error = 0;
|
||||
tsp = NULL;
|
||||
if (uap->timeout != NULL) {
|
||||
error = copyin((const void *)uap->timeout, (void *)&ts,
|
||||
sizeof(struct timespec));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (ts.tv_nsec < 0 || ts.tv_nsec > 1000000000)
|
||||
tsp = &ts;
|
||||
}
|
||||
|
||||
return (kern_thr_suspend(td, tsp));
|
||||
}
|
||||
|
||||
int
|
||||
kern_thr_suspend(struct thread *td, struct timespec *tsp)
|
||||
{
|
||||
struct timeval tv;
|
||||
int error = 0, hz = 0;
|
||||
|
||||
if (tsp != NULL) {
|
||||
if (tsp->tv_nsec < 0 || tsp->tv_nsec > 1000000000)
|
||||
return (EINVAL);
|
||||
if (ts.tv_sec == 0 && ts.tv_nsec == 0)
|
||||
if (tsp->tv_sec == 0 && tsp->tv_nsec == 0)
|
||||
return (ETIMEDOUT);
|
||||
TIMESPEC_TO_TIMEVAL(&tv, &ts);
|
||||
TIMESPEC_TO_TIMEVAL(&tv, tsp);
|
||||
hz = tvtohz(&tv);
|
||||
}
|
||||
PROC_LOCK(td->td_proc);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_compat.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/limits.h>
|
||||
@ -48,6 +49,12 @@ __FBSDID("$FreeBSD$");
|
||||
#include <vm/vm_map.h>
|
||||
#include <vm/vm_object.h>
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
#include <compat/freebsd32/freebsd32_proto.h>
|
||||
|
||||
#define UMTX_CONTESTED32 (-0x7fffffff - 1)
|
||||
#endif
|
||||
|
||||
#define UMTX_PRIVATE 0
|
||||
#define UMTX_SHARED 1
|
||||
|
||||
@ -113,7 +120,7 @@ static void fork_handler(void *arg, struct proc *p1, struct proc *p2,
|
||||
int flags);
|
||||
#endif
|
||||
static int umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2);
|
||||
static int umtx_key_get(struct thread *td, struct umtx *umtx,
|
||||
static int umtx_key_get(struct thread *td, void *umtx,
|
||||
struct umtx_key *key);
|
||||
static void umtx_key_release(struct umtx_key *key);
|
||||
|
||||
@ -299,7 +306,7 @@ umtxq_sleep(struct thread *td, struct umtx_key *key, int priority,
|
||||
}
|
||||
|
||||
static int
|
||||
umtx_key_get(struct thread *td, struct umtx *umtx, struct umtx_key *key)
|
||||
umtx_key_get(struct thread *td, void *umtx, struct umtx_key *key)
|
||||
{
|
||||
#if defined(UMTX_DYNAMIC_SHARED) || defined(UMTX_STATIC_SHARED)
|
||||
vm_map_t map;
|
||||
@ -355,7 +362,7 @@ umtx_key_release(struct umtx_key *key)
|
||||
}
|
||||
|
||||
static inline int
|
||||
umtxq_queue_me(struct thread *td, struct umtx *umtx, struct umtx_q *uq)
|
||||
umtxq_queue_me(struct thread *td, void *umtx, struct umtx_q *uq)
|
||||
{
|
||||
int error;
|
||||
|
||||
@ -621,6 +628,186 @@ do_unlock(struct thread *td, struct umtx *umtx, long id)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
static int
|
||||
_do_lock32(struct thread *td, uint32_t *m, uint32_t id, int timo)
|
||||
{
|
||||
struct umtx_q *uq;
|
||||
int32_t owner;
|
||||
int32_t old;
|
||||
int error = 0;
|
||||
|
||||
uq = td->td_umtxq;
|
||||
/*
|
||||
* Care must be exercised when dealing with umtx structure. It
|
||||
* can fault on any access.
|
||||
*/
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* Try the uncontested case. This should be done in userland.
|
||||
*/
|
||||
owner = casuword32(m, UMTX_UNOWNED, id);
|
||||
|
||||
/* The acquire succeeded. */
|
||||
if (owner == UMTX_UNOWNED)
|
||||
return (0);
|
||||
|
||||
/* The address was invalid. */
|
||||
if (owner == -1)
|
||||
return (EFAULT);
|
||||
|
||||
/* If no one owns it but it is contested try to acquire it. */
|
||||
if (owner == UMTX_CONTESTED32) {
|
||||
owner = casuword32(m,
|
||||
UMTX_CONTESTED32, id | UMTX_CONTESTED32);
|
||||
|
||||
if (owner == UMTX_CONTESTED32)
|
||||
return (0);
|
||||
|
||||
/* The address was invalid. */
|
||||
if (owner == -1)
|
||||
return (EFAULT);
|
||||
|
||||
/* If this failed the lock has changed, restart. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we caught a signal, we have retried and now
|
||||
* exit immediately.
|
||||
*/
|
||||
if (error || (error = umtxq_queue_me(td, m, uq)) != 0)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
* Set the contested bit so that a release in user space
|
||||
* knows to use the system call for unlock. If this fails
|
||||
* either some one else has acquired the lock or it has been
|
||||
* released.
|
||||
*/
|
||||
old = casuword32(m, owner, owner | UMTX_CONTESTED32);
|
||||
|
||||
/* The address was invalid. */
|
||||
if (old == -1) {
|
||||
umtxq_lock(&uq->uq_key);
|
||||
umtxq_busy(&uq->uq_key);
|
||||
umtxq_remove(uq);
|
||||
umtxq_unbusy(&uq->uq_key);
|
||||
umtxq_unlock(&uq->uq_key);
|
||||
umtx_key_release(&uq->uq_key);
|
||||
return (EFAULT);
|
||||
}
|
||||
|
||||
/*
|
||||
* We set the contested bit, sleep. Otherwise the lock changed
|
||||
* and we need to retry or we lost a race to the thread
|
||||
* unlocking the umtx.
|
||||
*/
|
||||
umtxq_lock(&uq->uq_key);
|
||||
if (old == owner && (td->td_flags & TDF_UMTXQ)) {
|
||||
error = umtxq_sleep(td, &uq->uq_key, PCATCH,
|
||||
"umtx", timo);
|
||||
}
|
||||
umtxq_busy(&uq->uq_key);
|
||||
umtxq_remove(uq);
|
||||
umtxq_unbusy(&uq->uq_key);
|
||||
umtxq_unlock(&uq->uq_key);
|
||||
umtx_key_release(&uq->uq_key);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
do_lock32(struct thread *td, void *m, uint32_t id,
|
||||
struct timespec *timeout)
|
||||
{
|
||||
struct timespec ts, ts2, ts3;
|
||||
struct timeval tv;
|
||||
int error;
|
||||
|
||||
if (timeout == NULL) {
|
||||
error = _do_lock32(td, m, id, 0);
|
||||
} else {
|
||||
getnanouptime(&ts);
|
||||
timespecadd(&ts, timeout);
|
||||
TIMESPEC_TO_TIMEVAL(&tv, timeout);
|
||||
for (;;) {
|
||||
error = _do_lock32(td, m, id, tvtohz(&tv));
|
||||
if (error != ETIMEDOUT)
|
||||
break;
|
||||
getnanouptime(&ts2);
|
||||
if (timespeccmp(&ts2, &ts, >=)) {
|
||||
error = ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
ts3 = ts;
|
||||
timespecsub(&ts3, &ts2);
|
||||
TIMESPEC_TO_TIMEVAL(&tv, &ts3);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This lets userland back off critical region if needed.
|
||||
*/
|
||||
if (error == ERESTART)
|
||||
error = EINTR;
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
do_unlock32(struct thread *td, uint32_t *m, uint32_t id)
|
||||
{
|
||||
struct umtx_key key;
|
||||
int32_t owner;
|
||||
int32_t old;
|
||||
int error;
|
||||
int count;
|
||||
|
||||
/*
|
||||
* Make sure we own this mtx.
|
||||
*
|
||||
* XXX Need a {fu,su}ptr this is not correct on arch where
|
||||
* sizeof(intptr_t) != sizeof(long).
|
||||
*/
|
||||
if ((owner = fuword32(m)) == -1)
|
||||
return (EFAULT);
|
||||
|
||||
if ((owner & ~UMTX_CONTESTED32) != id)
|
||||
return (EPERM);
|
||||
|
||||
/* We should only ever be in here for contested locks */
|
||||
if ((owner & UMTX_CONTESTED32) == 0)
|
||||
return (EINVAL);
|
||||
|
||||
if ((error = umtx_key_get(td, m, &key)) != 0)
|
||||
return (error);
|
||||
|
||||
umtxq_lock(&key);
|
||||
umtxq_busy(&key);
|
||||
count = umtxq_count(&key);
|
||||
umtxq_unlock(&key);
|
||||
|
||||
/*
|
||||
* When unlocking the umtx, it must be marked as unowned if
|
||||
* there is zero or one thread only waiting for it.
|
||||
* Otherwise, it must be marked as contested.
|
||||
*/
|
||||
old = casuword32(m, owner,
|
||||
count <= 1 ? UMTX_UNOWNED : UMTX_CONTESTED32);
|
||||
umtxq_lock(&key);
|
||||
umtxq_signal(&key, 0);
|
||||
umtxq_unbusy(&key);
|
||||
umtxq_unlock(&key);
|
||||
umtx_key_release(&key);
|
||||
if (old == -1)
|
||||
return (EFAULT);
|
||||
if (old != owner)
|
||||
return (EINVAL);
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
do_wait(struct thread *td, struct umtx *umtx, long id, struct timespec *timeout)
|
||||
{
|
||||
@ -768,3 +955,107 @@ _umtx_op(struct thread *td, struct _umtx_op_args *uap)
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
#ifdef COMPAT_IA32
|
||||
int
|
||||
freebsd32_umtx_lock(struct thread *td, struct freebsd32_umtx_lock_args *uap)
|
||||
/* struct umtx *umtx */
|
||||
{
|
||||
return (do_lock32(td, (uint32_t *)uap->umtx, td->td_tid, NULL));
|
||||
}
|
||||
|
||||
int
|
||||
freebsd32_umtx_unlock(struct thread *td, struct freebsd32_umtx_unlock_args *uap)
|
||||
/* struct umtx *umtx */
|
||||
{
|
||||
return (do_unlock32(td, (uint32_t *)uap->umtx, td->td_tid));
|
||||
}
|
||||
|
||||
struct timespec32 {
|
||||
u_int32_t tv_sec;
|
||||
u_int32_t tv_nsec;
|
||||
};
|
||||
|
||||
static inline int
|
||||
copyin_timeout32(void *addr, struct timespec *tsp)
|
||||
{
|
||||
struct timespec32 ts32;
|
||||
int error;
|
||||
|
||||
error = copyin(addr, &ts32, sizeof(struct timespec32));
|
||||
if (error == 0) {
|
||||
tsp->tv_sec = ts32.tv_sec;
|
||||
tsp->tv_nsec = ts32.tv_nsec;
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
__umtx_op_lock_umtx_compat32(struct thread *td, struct _umtx_op_args *uap)
|
||||
{
|
||||
struct timespec *ts, timeout;
|
||||
int error;
|
||||
|
||||
/* Allow a null timespec (wait forever). */
|
||||
if (uap->uaddr2 == NULL)
|
||||
ts = NULL;
|
||||
else {
|
||||
error = copyin_timeout32(uap->uaddr2, &timeout);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (timeout.tv_nsec >= 1000000000 ||
|
||||
timeout.tv_nsec < 0) {
|
||||
return (EINVAL);
|
||||
}
|
||||
ts = &timeout;
|
||||
}
|
||||
return (do_lock32(td, uap->umtx, uap->id, ts));
|
||||
}
|
||||
|
||||
static int
|
||||
__umtx_op_unlock_umtx_compat32(struct thread *td, struct _umtx_op_args *uap)
|
||||
{
|
||||
return (do_unlock32(td, (uint32_t *)uap->umtx, (uint32_t)uap->id));
|
||||
}
|
||||
|
||||
static int
|
||||
__umtx_op_wait_compat32(struct thread *td, struct _umtx_op_args *uap)
|
||||
{
|
||||
struct timespec *ts, timeout;
|
||||
int error;
|
||||
|
||||
if (uap->uaddr2 == NULL)
|
||||
ts = NULL;
|
||||
else {
|
||||
error = copyin_timeout32(uap->uaddr2, &timeout);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (timeout.tv_nsec >= 1000000000 ||
|
||||
timeout.tv_nsec < 0)
|
||||
return (EINVAL);
|
||||
ts = &timeout;
|
||||
}
|
||||
return do_wait(td, uap->umtx, uap->id, ts);
|
||||
}
|
||||
|
||||
int
|
||||
freebsd32_umtx_op(struct thread *td, struct freebsd32_umtx_op_args *uap)
|
||||
{
|
||||
|
||||
switch ((unsigned)uap->op) {
|
||||
case UMTX_OP_LOCK:
|
||||
return __umtx_op_lock_umtx_compat32(td,
|
||||
(struct _umtx_op_args *)uap);
|
||||
case UMTX_OP_UNLOCK:
|
||||
return __umtx_op_unlock_umtx_compat32(td,
|
||||
(struct _umtx_op_args *)uap);
|
||||
case UMTX_OP_WAIT:
|
||||
return __umtx_op_wait_compat32(td,
|
||||
(struct _umtx_op_args *)uap);
|
||||
case UMTX_OP_WAKE:
|
||||
return kern_umtx_wake(td, (uint32_t *)uap->umtx, uap->id);
|
||||
default:
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -46,6 +46,7 @@ struct stat;
|
||||
struct kevent;
|
||||
struct kevent_copyops;
|
||||
struct sendfile_args;
|
||||
struct thr_param;
|
||||
|
||||
int kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg,
|
||||
u_int buflen);
|
||||
@ -148,6 +149,8 @@ int kern_statfs(struct thread *td, char *path, enum uio_seg pathseg,
|
||||
struct statfs *buf);
|
||||
int kern_symlink(struct thread *td, char *path, char *link,
|
||||
enum uio_seg segflg);
|
||||
int kern_thr_new(struct thread *td, struct thr_param *param);
|
||||
int kern_thr_suspend(struct thread *td, struct timespec *tsp);
|
||||
int kern_truncate(struct thread *td, char *path, enum uio_seg pathseg,
|
||||
off_t length);
|
||||
int kern_unlink(struct thread *td, char *path, enum uio_seg pathseg);
|
||||
|
@ -204,6 +204,7 @@ int suword(void *base, long word);
|
||||
int suword16(void *base, int word);
|
||||
int suword32(void *base, int32_t word);
|
||||
int suword64(void *base, int64_t word);
|
||||
int32_t casuword32(volatile uint32_t *base, uint32_t oldval, uint32_t newval);
|
||||
intptr_t casuptr(intptr_t *p, intptr_t old, intptr_t new);
|
||||
|
||||
void realitexpire(void *);
|
||||
|
Loading…
x
Reference in New Issue
Block a user