--- //depot/yahoo/ybsd_6/src/sys/amd64/amd64/support.S 2005/06/30 07:45:01 +++ //depot/jhb/bsd6/src/sys/amd64/amd64/support.S 2007/03/26 14:33:00 @@ -314,6 +314,34 @@ 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 */ --- //depot/yahoo/ybsd_6/src/sys/amd64/amd64/vm_machdep.c 2007/05/04 12:32:07 +++ //depot/jhb/bsd6/src/sys/amd64/amd64/vm_machdep.c 2007/06/16 16:42:07 @@ -46,6 +46,7 @@ #include "opt_isa.h" #include "opt_cpu.h" +#include "opt_compat.h" #include #include @@ -70,6 +71,7 @@ #include #include #include +#include #include #include @@ -80,6 +82,12 @@ #include +#ifdef COMPAT_IA32 + +extern struct sysentvec ia32_freebsd_sysvec; + +#endif + static void cpu_reset_real(void); #ifdef SMP static void cpu_reset_proxy(void); @@ -321,6 +329,28 @@ */ 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. @@ -329,7 +359,6 @@ 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; /* @@ -346,6 +375,19 @@ 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; --- //depot/yahoo/ybsd_6/src/sys/amd64/ia32/ia32_sigtramp.S 2004/12/23 17:57:00 +++ //depot/jhb/bsd6/src/sys/amd64/ia32/ia32_sigtramp.S 2007/06/16 11:11:39 @@ -45,8 +45,6 @@ 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 @@ 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 */ --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/freebsd32.h 2006/10/31 15:17:15 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/freebsd32.h 2007/06/07 11:00:24 @@ -100,4 +100,17 @@ 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_ */ --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/freebsd32_misc.c 2006/08/24 10:57:46 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/freebsd32_misc.c 2007/06/07 11:00:24 @@ -2115,6 +2115,60 @@ 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 --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/freebsd32_proto.h 2007/05/09 10:01:22 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/freebsd32_proto.h 2007/06/07 11:16:30 @@ -281,6 +281,26 @@ char oucp_l_[PADL_(struct freebsd32_ucontext *)]; struct freebsd32_ucontext * oucp; char oucp_r_[PADR_(struct freebsd32_ucontext *)]; char ucp_l_[PADL_(const struct freebsd32_ucontext *)]; const struct freebsd32_ucontext * ucp; char ucp_r_[PADR_(const struct freebsd32_ucontext *)]; }; +struct freebsd32_umtx_lock_args { + char umtx_l_[PADL_(struct umtx *)]; struct umtx * umtx; char umtx_r_[PADR_(struct umtx *)]; +}; +struct freebsd32_umtx_unlock_args { + char umtx_l_[PADL_(struct umtx *)]; struct umtx * umtx; char umtx_r_[PADR_(struct umtx *)]; +}; +struct freebsd32_thr_suspend_args { + char timeout_l_[PADL_(const struct timespec32 *)]; const struct timespec32 * timeout; char timeout_r_[PADR_(const struct timespec32 *)]; +}; +struct freebsd32_umtx_op_args { + char umtx_l_[PADL_(struct umtx *)]; struct umtx * umtx; char umtx_r_[PADR_(struct umtx *)]; + char op_l_[PADL_(int)]; int op; char op_r_[PADR_(int)]; + char id_l_[PADL_(long)]; long id; char id_r_[PADR_(long)]; + char uaddr_l_[PADL_(void *)]; void * uaddr; char uaddr_r_[PADR_(void *)]; + char uaddr2_l_[PADL_(void *)]; void * uaddr2; char uaddr2_r_[PADR_(void *)]; +}; +struct freebsd32_thr_new_args { + char param_l_[PADL_(struct thr_param32 *)]; struct thr_param32 * param; char param_r_[PADR_(struct thr_param32 *)]; + char param_size_l_[PADL_(int)]; int param_size; char param_size_r_[PADR_(int)]; +}; int freebsd32_wait4(struct thread *, struct freebsd32_wait4_args *); int freebsd32_recvmsg(struct thread *, struct freebsd32_recvmsg_args *); int freebsd32_sendmsg(struct thread *, struct freebsd32_sendmsg_args *); @@ -327,6 +347,11 @@ int freebsd32_getcontext(struct thread *, struct freebsd32_getcontext_args *); int freebsd32_setcontext(struct thread *, struct freebsd32_setcontext_args *); int freebsd32_swapcontext(struct thread *, struct freebsd32_swapcontext_args *); +int freebsd32_umtx_lock(struct thread *, struct freebsd32_umtx_lock_args *); +int freebsd32_umtx_unlock(struct thread *, struct freebsd32_umtx_unlock_args *); +int freebsd32_thr_suspend(struct thread *, struct freebsd32_thr_suspend_args *); +int freebsd32_umtx_op(struct thread *, struct freebsd32_umtx_op_args *); +int freebsd32_thr_new(struct thread *, struct freebsd32_thr_new_args *); #ifdef COMPAT_43 --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/freebsd32_syscall.h 2007/05/09 10:01:22 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/freebsd32_syscall.h 2007/06/07 11:16:30 @@ -304,14 +304,13 @@ #define FREEBSD32_SYS_freebsd32_getcontext 421 #define FREEBSD32_SYS_freebsd32_setcontext 422 #define FREEBSD32_SYS_freebsd32_swapcontext 423 -#define FREEBSD32_SYS_thr_create 430 #define FREEBSD32_SYS_thr_exit 431 #define FREEBSD32_SYS_thr_self 432 #define FREEBSD32_SYS_thr_kill 433 -#define FREEBSD32_SYS__umtx_lock 434 -#define FREEBSD32_SYS__umtx_unlock 435 +#define FREEBSD32_SYS_freebsd32_umtx_lock 434 +#define FREEBSD32_SYS_freebsd32_umtx_unlock 435 #define FREEBSD32_SYS_jail_attach 436 -#define FREEBSD32_SYS_thr_suspend 442 +#define FREEBSD32_SYS_freebsd32_thr_suspend 442 #define FREEBSD32_SYS_thr_wake 443 #define FREEBSD32_SYS_kldunloadf 444 #define FREEBSD32_SYS_audit 445 @@ -323,4 +322,6 @@ #define FREEBSD32_SYS_getaudit_addr 451 #define FREEBSD32_SYS_setaudit_addr 452 #define FREEBSD32_SYS_auditctl 453 -#define FREEBSD32_SYS_MAXSYSCALL 455 +#define FREEBSD32_SYS_freebsd32_umtx_op 454 +#define FREEBSD32_SYS_freebsd32_thr_new 455 +#define FREEBSD32_SYS_MAXSYSCALL 456 --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/freebsd32_syscalls.c 2007/05/09 10:01:22 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/freebsd32_syscalls.c 2007/06/07 11:16:30 @@ -437,19 +437,19 @@ "#427", /* 427 = __acl_delete_link */ "#428", /* 428 = __acl_aclcheck_link */ "#429", /* 429 = sigwait */ - "thr_create", /* 430 = thr_create */ + "#430", /* 430 = thr_create */ "thr_exit", /* 431 = thr_exit */ "thr_self", /* 432 = thr_self */ "thr_kill", /* 433 = thr_kill */ - "_umtx_lock", /* 434 = _umtx_lock */ - "_umtx_unlock", /* 435 = _umtx_unlock */ + "freebsd32_umtx_lock", /* 434 = freebsd32_umtx_lock */ + "freebsd32_umtx_unlock", /* 435 = freebsd32_umtx_unlock */ "jail_attach", /* 436 = jail_attach */ "#437", /* 437 = extattr_list_fd */ "#438", /* 438 = extattr_list_file */ "#439", /* 439 = extattr_list_link */ "#440", /* 440 = kse_switchin */ "#441", /* 441 = ksem_timedwait */ - "thr_suspend", /* 442 = thr_suspend */ + "freebsd32_thr_suspend", /* 442 = freebsd32_thr_suspend */ "thr_wake", /* 443 = thr_wake */ "kldunloadf", /* 444 = kldunloadf */ "audit", /* 445 = audit */ @@ -461,5 +461,6 @@ "getaudit_addr", /* 451 = getaudit_addr */ "setaudit_addr", /* 452 = setaudit_addr */ "auditctl", /* 453 = auditctl */ - "#454", /* 454 = _umtx_op */ + "freebsd32_umtx_op", /* 454 = freebsd32_umtx_op */ + "freebsd32_thr_new", /* 455 = freebsd32_thr_new */ }; --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/freebsd32_sysent.c 2007/05/09 10:01:22 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/freebsd32_sysent.c 2007/06/07 11:16:30 @@ -462,19 +462,19 @@ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 427 = __acl_delete_link */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 428 = __acl_aclcheck_link */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 429 = sigwait */ - { SYF_MPSAFE | AS(thr_create_args), (sy_call_t *)thr_create, AUE_NULL }, /* 430 = thr_create */ + { 0, (sy_call_t *)nosys, AUE_NULL }, /* 430 = thr_create */ { SYF_MPSAFE | AS(thr_exit_args), (sy_call_t *)thr_exit, AUE_NULL }, /* 431 = thr_exit */ { SYF_MPSAFE | AS(thr_self_args), (sy_call_t *)thr_self, AUE_NULL }, /* 432 = thr_self */ { SYF_MPSAFE | AS(thr_kill_args), (sy_call_t *)thr_kill, AUE_NULL }, /* 433 = thr_kill */ - { SYF_MPSAFE | AS(_umtx_lock_args), (sy_call_t *)_umtx_lock, AUE_NULL }, /* 434 = _umtx_lock */ - { SYF_MPSAFE | AS(_umtx_unlock_args), (sy_call_t *)_umtx_unlock, AUE_NULL }, /* 435 = _umtx_unlock */ + { SYF_MPSAFE | AS(freebsd32_umtx_lock_args), (sy_call_t *)freebsd32_umtx_lock, AUE_NULL }, /* 434 = freebsd32_umtx_lock */ + { SYF_MPSAFE | AS(freebsd32_umtx_unlock_args), (sy_call_t *)freebsd32_umtx_unlock, AUE_NULL }, /* 435 = freebsd32_umtx_unlock */ { SYF_MPSAFE | AS(jail_attach_args), (sy_call_t *)jail_attach, AUE_NULL }, /* 436 = jail_attach */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 437 = extattr_list_fd */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 438 = extattr_list_file */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 439 = extattr_list_link */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 440 = kse_switchin */ { 0, (sy_call_t *)nosys, AUE_NULL }, /* 441 = ksem_timedwait */ - { SYF_MPSAFE | AS(thr_suspend_args), (sy_call_t *)thr_suspend, AUE_NULL }, /* 442 = thr_suspend */ + { SYF_MPSAFE | AS(freebsd32_thr_suspend_args), (sy_call_t *)freebsd32_thr_suspend, AUE_NULL }, /* 442 = freebsd32_thr_suspend */ { SYF_MPSAFE | AS(thr_wake_args), (sy_call_t *)thr_wake, AUE_NULL }, /* 443 = thr_wake */ { SYF_MPSAFE | AS(kldunloadf_args), (sy_call_t *)kldunloadf, AUE_MODUNLOAD }, /* 444 = kldunloadf */ { SYF_MPSAFE | AS(audit_args), (sy_call_t *)audit, AUE_AUDIT }, /* 445 = audit */ @@ -486,5 +486,6 @@ { SYF_MPSAFE | AS(getaudit_addr_args), (sy_call_t *)getaudit_addr, AUE_GETAUDIT_ADDR }, /* 451 = getaudit_addr */ { SYF_MPSAFE | AS(setaudit_addr_args), (sy_call_t *)setaudit_addr, AUE_SETAUDIT_ADDR }, /* 452 = setaudit_addr */ { SYF_MPSAFE | AS(auditctl_args), (sy_call_t *)auditctl, AUE_AUDITCTL }, /* 453 = auditctl */ - { 0, (sy_call_t *)nosys, AUE_NULL }, /* 454 = _umtx_op */ + { SYF_MPSAFE | AS(freebsd32_umtx_op_args), (sy_call_t *)freebsd32_umtx_op, AUE_NULL }, /* 454 = freebsd32_umtx_op */ + { SYF_MPSAFE | AS(freebsd32_thr_new_args), (sy_call_t *)freebsd32_thr_new, AUE_NULL }, /* 455 = freebsd32_thr_new */ }; --- //depot/yahoo/ybsd_6/src/sys/compat/freebsd32/syscalls.master 2007/05/09 09:59:05 +++ //depot/jhb/bsd6/src/sys/compat/freebsd32/syscalls.master 2007/06/07 11:10:50 @@ -723,21 +723,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, \ @@ -755,4 +754,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); } --- //depot/yahoo/ybsd_6/src/sys/kern/kern_thr.c 2006/06/18 08:14:39 +++ //depot/jhb/bsd6/src/sys/kern/kern_thr.c 2007/06/07 12:22:05 @@ -27,6 +27,7 @@ #include __FBSDID("$FreeBSD: src/sys/kern/kern_thr.c,v 1.34.2.4 2006/06/16 22:11:55 jhb Exp $"); +#include "opt_compat.h" #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +48,26 @@ #include +#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 @@ 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 @@ struct thread *newtd; struct ksegrp *kg, *newkg; struct proc *p; - long id; int error, scope_sys, linkkg; error = 0; @@ -146,11 +175,10 @@ * 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 @@ 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 @@ /* 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 @@ 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); --- //depot/yahoo/ybsd_6/src/sys/kern/kern_umtx.c 2006/06/18 08:14:39 +++ //depot/jhb/bsd6/src/sys/kern/kern_umtx.c 2007/03/26 14:55:30 @@ -28,6 +28,7 @@ #include __FBSDID("$FreeBSD: src/sys/kern/kern_umtx.c,v 1.33.2.2 2006/06/16 22:11:55 jhb Exp $"); +#include "opt_compat.h" #include #include #include @@ -48,6 +49,12 @@ #include #include +#ifdef COMPAT_IA32 +#include + +#define UMTX_CONTESTED32 (-0x7fffffff - 1) +#endif + #define UMTX_PRIVATE 0 #define UMTX_SHARED 1 @@ -113,7 +120,7 @@ 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 @@ } 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 @@ } 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,7 +628,187 @@ 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) { struct umtx_q *uq; @@ -768,3 +955,107 @@ } 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 --- //depot/yahoo/ybsd_6/src/sys/sys/syscallsubr.h 2006/10/31 15:16:12 +++ //depot/jhb/bsd6/src/sys/sys/syscallsubr.h 2007/06/07 11:00:24 @@ -46,6 +46,7 @@ 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 @@ 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); --- //depot/yahoo/ybsd_6/src/sys/sys/systm.h 2006/08/24 10:57:46 +++ //depot/jhb/bsd6/src/sys/sys/systm.h 2007/03/26 14:39:31 @@ -205,6 +205,7 @@ 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 *);