19454b2d8SWarner Losh /*- 289bb1cefSJeff Roberson * Copyright (c) 2003, Jeffrey Roberson <jeff@freebsd.org> 389bb1cefSJeff Roberson * All rights reserved. 489bb1cefSJeff Roberson * 589bb1cefSJeff Roberson * Redistribution and use in source and binary forms, with or without 689bb1cefSJeff Roberson * modification, are permitted provided that the following conditions 789bb1cefSJeff Roberson * are met: 889bb1cefSJeff Roberson * 1. Redistributions of source code must retain the above copyright 989bb1cefSJeff Roberson * notice unmodified, this list of conditions, and the following 1089bb1cefSJeff Roberson * disclaimer. 1189bb1cefSJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 1289bb1cefSJeff Roberson * notice, this list of conditions and the following disclaimer in the 1389bb1cefSJeff Roberson * documentation and/or other materials provided with the distribution. 1489bb1cefSJeff Roberson * 1589bb1cefSJeff Roberson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1689bb1cefSJeff Roberson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1789bb1cefSJeff Roberson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1889bb1cefSJeff Roberson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1989bb1cefSJeff Roberson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2089bb1cefSJeff Roberson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2189bb1cefSJeff Roberson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2289bb1cefSJeff Roberson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2389bb1cefSJeff Roberson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2489bb1cefSJeff Roberson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2589bb1cefSJeff Roberson */ 2689bb1cefSJeff Roberson 27677b542eSDavid E. O'Brien #include <sys/cdefs.h> 28677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 29677b542eSDavid E. O'Brien 3089bb1cefSJeff Roberson #include <sys/param.h> 3189bb1cefSJeff Roberson #include <sys/kernel.h> 3289bb1cefSJeff Roberson #include <sys/lock.h> 3389bb1cefSJeff Roberson #include <sys/mutex.h> 3489bb1cefSJeff Roberson #include <sys/proc.h> 3589bb1cefSJeff Roberson #include <sys/resourcevar.h> 36a22ec9d8SJeff Roberson #include <sys/sched.h> 37a8b491c1SJulian Elischer #include <sys/sysctl.h> 38ed062c8dSJulian Elischer #include <sys/smp.h> 3989bb1cefSJeff Roberson #include <sys/sysent.h> 4089bb1cefSJeff Roberson #include <sys/systm.h> 4189bb1cefSJeff Roberson #include <sys/sysproto.h> 4289bb1cefSJeff Roberson #include <sys/signalvar.h> 4389bb1cefSJeff Roberson #include <sys/ucontext.h> 4489bb1cefSJeff Roberson #include <sys/thr.h> 454938faa6SDavid Xu #include <sys/umtx.h> 464938faa6SDavid Xu #include <sys/limits.h> 4789bb1cefSJeff Roberson 4889bb1cefSJeff Roberson #include <machine/frame.h> 4989bb1cefSJeff Roberson 50ed062c8dSJulian Elischer extern int max_threads_per_proc; 51ed062c8dSJulian Elischer extern int max_groups_per_proc; 52ed062c8dSJulian Elischer 53a8b491c1SJulian Elischer SYSCTL_DECL(_kern_threads); 54c4bd610fSDavid Xu static int thr_scope = 0; 55c4bd610fSDavid Xu SYSCTL_INT(_kern_threads, OID_AUTO, thr_scope, CTLFLAG_RW, 56c4bd610fSDavid Xu &thr_scope, 0, "sys or proc scope scheduling"); 57a8b491c1SJulian Elischer 5824640982SJulian Elischer static int thr_concurrency = 0; 59a8b491c1SJulian Elischer SYSCTL_INT(_kern_threads, OID_AUTO, thr_concurrency, CTLFLAG_RW, 60a8b491c1SJulian Elischer &thr_concurrency, 0, "a concurrency value if not default"); 61a8b491c1SJulian Elischer 62c4bd610fSDavid Xu static int create_thread(struct thread *td, mcontext_t *ctx, 63c4bd610fSDavid Xu void (*start_func)(void *), void *arg, 64c4bd610fSDavid Xu char *stack_base, size_t stack_size, 65c4bd610fSDavid Xu char *tls_base, 66c4bd610fSDavid Xu long *child_tid, long *parent_tid, 67c4bd610fSDavid Xu int flags); 68c4bd610fSDavid Xu 6989bb1cefSJeff Roberson /* 7089bb1cefSJeff Roberson * System call interface. 7189bb1cefSJeff Roberson */ 7289bb1cefSJeff Roberson int 7389bb1cefSJeff Roberson thr_create(struct thread *td, struct thr_create_args *uap) 74cd28f17dSMarcel Moolenaar /* ucontext_t *ctx, long *id, int flags */ 7589bb1cefSJeff Roberson { 7689bb1cefSJeff Roberson ucontext_t ctx; 7789bb1cefSJeff Roberson int error; 7889bb1cefSJeff Roberson 7989bb1cefSJeff Roberson if ((error = copyin(uap->ctx, &ctx, sizeof(ctx)))) 8089bb1cefSJeff Roberson return (error); 8189bb1cefSJeff Roberson 82c4bd610fSDavid Xu error = create_thread(td, &ctx.uc_mcontext, NULL, NULL, 83c4bd610fSDavid Xu NULL, 0, NULL, uap->id, NULL, uap->flags); 84c4bd610fSDavid Xu return (error); 85c4bd610fSDavid Xu } 86c4bd610fSDavid Xu 87c4bd610fSDavid Xu int 88c4bd610fSDavid Xu thr_new(struct thread *td, struct thr_new_args *uap) 89c4bd610fSDavid Xu /* struct thr_param * */ 90c4bd610fSDavid Xu { 91c4bd610fSDavid Xu struct thr_param param; 92c4bd610fSDavid Xu int error; 93c4bd610fSDavid Xu 94c4bd610fSDavid Xu if (uap->param_size < sizeof(param)) 95c4bd610fSDavid Xu return (EINVAL); 96c4bd610fSDavid Xu if ((error = copyin(uap->param, ¶m, sizeof(param)))) 97c4bd610fSDavid Xu return (error); 98c4bd610fSDavid Xu error = create_thread(td, NULL, param.start_func, param.arg, 99c4bd610fSDavid Xu param.stack_base, param.stack_size, param.tls_base, 100c4bd610fSDavid Xu param.child_tid, param.parent_tid, param.flags); 101c4bd610fSDavid Xu return (error); 102c4bd610fSDavid Xu } 103c4bd610fSDavid Xu 104c4bd610fSDavid Xu static int 105c4bd610fSDavid Xu create_thread(struct thread *td, mcontext_t *ctx, 106c4bd610fSDavid Xu void (*start_func)(void *), void *arg, 107c4bd610fSDavid Xu char *stack_base, size_t stack_size, 108c4bd610fSDavid Xu char *tls_base, 109c4bd610fSDavid Xu long *child_tid, long *parent_tid, 110c4bd610fSDavid Xu int flags) 111c4bd610fSDavid Xu { 112c4bd610fSDavid Xu stack_t stack; 113c4bd610fSDavid Xu struct thread *newtd; 114c4bd610fSDavid Xu struct ksegrp *kg, *newkg; 115c4bd610fSDavid Xu struct proc *p; 116c4bd610fSDavid Xu long id; 117c4bd610fSDavid Xu int error, scope_sys, linkkg; 118c4bd610fSDavid Xu 119c4bd610fSDavid Xu error = 0; 120c4bd610fSDavid Xu p = td->td_proc; 121c4bd610fSDavid Xu kg = td->td_ksegrp; 122c4bd610fSDavid Xu 123c4bd610fSDavid Xu /* Have race condition but it is cheap. */ 124ed062c8dSJulian Elischer if ((p->p_numksegrps >= max_groups_per_proc) || 125ed062c8dSJulian Elischer (p->p_numthreads >= max_threads_per_proc)) { 126ed062c8dSJulian Elischer return (EPROCLIM); 127ed062c8dSJulian Elischer } 128195f5806SDavid Xu 129c4bd610fSDavid Xu /* Check PTHREAD_SCOPE_SYSTEM */ 130c4bd610fSDavid Xu scope_sys = (flags & THR_SYSTEM_SCOPE) != 0; 131c4bd610fSDavid Xu 132c4bd610fSDavid Xu /* sysctl overrides user's flag */ 133c4bd610fSDavid Xu if (thr_scope == 1) 134c4bd610fSDavid Xu scope_sys = 0; 135c4bd610fSDavid Xu else if (thr_scope == 2) 136c4bd610fSDavid Xu scope_sys = 1; 137c4bd610fSDavid Xu 138ed062c8dSJulian Elischer /* Initialize our td and new ksegrp.. */ 139ed062c8dSJulian Elischer newtd = thread_alloc(); 140c4bd610fSDavid Xu 14189bb1cefSJeff Roberson /* 142c4bd610fSDavid Xu * Try the copyout as soon as we allocate the td so we don't 143c4bd610fSDavid Xu * have to tear things down in a failure case below. 144c4bd610fSDavid Xu * Here we copy out tid to two places, one for child and one 145c4bd610fSDavid Xu * for parent, because pthread can create a detached thread, 146c4bd610fSDavid Xu * if parent wants to safely access child tid, it has to provide 147c4bd610fSDavid Xu * its storage, because child thread may exit quickly and 148c4bd610fSDavid Xu * memory is freed before parent thread can access it. 14989bb1cefSJeff Roberson */ 150ed062c8dSJulian Elischer id = newtd->td_tid; 151c4bd610fSDavid Xu if ((child_tid != NULL && 152c4bd610fSDavid Xu (error = copyout(&id, child_tid, sizeof(long)))) || 153c4bd610fSDavid Xu (parent_tid != NULL && 154c4bd610fSDavid Xu (error = copyout(&id, parent_tid, sizeof(long))))) { 155ed062c8dSJulian Elischer thread_free(newtd); 15689bb1cefSJeff Roberson return (error); 15789bb1cefSJeff Roberson } 158ed062c8dSJulian Elischer bzero(&newtd->td_startzero, 1596db36923SDavid Schultz __rangeof(struct thread, td_startzero, td_endzero)); 160ed062c8dSJulian Elischer bcopy(&td->td_startcopy, &newtd->td_startcopy, 1616db36923SDavid Schultz __rangeof(struct thread, td_startcopy, td_endcopy)); 162c4bd610fSDavid Xu newtd->td_proc = td->td_proc; 163c4bd610fSDavid Xu newtd->td_ucred = crhold(td->td_ucred); 16489bb1cefSJeff Roberson 165c4bd610fSDavid Xu cpu_set_upcall(newtd, td); 166c4bd610fSDavid Xu 167c4bd610fSDavid Xu if (ctx != NULL) { /* old way to set user context */ 168c4bd610fSDavid Xu error = set_mcontext(newtd, ctx); 169c4bd610fSDavid Xu if (error != 0) { 170c4bd610fSDavid Xu thread_free(newtd); 171c4bd610fSDavid Xu crfree(td->td_ucred); 172c4bd610fSDavid Xu return (error); 173c4bd610fSDavid Xu } 174c4bd610fSDavid Xu } else { 175c4bd610fSDavid Xu /* Set up our machine context. */ 176c4bd610fSDavid Xu stack.ss_sp = stack_base; 177c4bd610fSDavid Xu stack.ss_size = stack_size; 178c4bd610fSDavid Xu /* Set upcall address to user thread entry function. */ 179c4bd610fSDavid Xu cpu_set_upcall_kse(newtd, start_func, arg, &stack); 180c4bd610fSDavid Xu /* Setup user TLS address and TLS pointer register. */ 181740fd64dSDavid Xu error = cpu_set_user_tls(newtd, tls_base); 182740fd64dSDavid Xu if (error != 0) { 183740fd64dSDavid Xu thread_free(newtd); 184740fd64dSDavid Xu crfree(td->td_ucred); 185740fd64dSDavid Xu return (error); 186740fd64dSDavid Xu } 187c4bd610fSDavid Xu } 188c4bd610fSDavid Xu 189c4bd610fSDavid Xu if ((td->td_proc->p_flag & P_HADTHREADS) == 0) { 190c4bd610fSDavid Xu /* Treat initial thread as it has PTHREAD_SCOPE_PROCESS. */ 191c4bd610fSDavid Xu p->p_procscopegrp = kg; 192c4bd610fSDavid Xu mtx_lock_spin(&sched_lock); 193c4bd610fSDavid Xu sched_set_concurrency(kg, 194c4bd610fSDavid Xu thr_concurrency ? thr_concurrency : (2*mp_ncpus)); 195c4bd610fSDavid Xu mtx_unlock_spin(&sched_lock); 196c4bd610fSDavid Xu } 197c4bd610fSDavid Xu 198c4bd610fSDavid Xu linkkg = 0; 199195f5806SDavid Xu if (scope_sys) { 200c4bd610fSDavid Xu linkkg = 1; 201c4bd610fSDavid Xu newkg = ksegrp_alloc(); 202ed062c8dSJulian Elischer bzero(&newkg->kg_startzero, 2036db36923SDavid Schultz __rangeof(struct ksegrp, kg_startzero, kg_endzero)); 204ed062c8dSJulian Elischer bcopy(&kg->kg_startcopy, &newkg->kg_startcopy, 2056db36923SDavid Schultz __rangeof(struct ksegrp, kg_startcopy, kg_endcopy)); 206a8b491c1SJulian Elischer sched_init_concurrency(newkg); 207c4bd610fSDavid Xu PROC_LOCK(td->td_proc); 208a8b491c1SJulian Elischer } else { 209c4bd610fSDavid Xu /* 210c4bd610fSDavid Xu * Try to create a KSE group which will be shared 211c4bd610fSDavid Xu * by all PTHREAD_SCOPE_PROCESS threads. 212c4bd610fSDavid Xu */ 213c4bd610fSDavid Xu retry: 214c4bd610fSDavid Xu PROC_LOCK(td->td_proc); 215c4bd610fSDavid Xu if ((newkg = p->p_procscopegrp) == NULL) { 216c4bd610fSDavid Xu PROC_UNLOCK(p); 217c4bd610fSDavid Xu newkg = ksegrp_alloc(); 218c4bd610fSDavid Xu bzero(&newkg->kg_startzero, 219c4bd610fSDavid Xu __rangeof(struct ksegrp, kg_startzero, kg_endzero)); 220c4bd610fSDavid Xu bcopy(&kg->kg_startcopy, &newkg->kg_startcopy, 221c4bd610fSDavid Xu __rangeof(struct ksegrp, kg_startcopy, kg_endcopy)); 222c4bd610fSDavid Xu PROC_LOCK(p); 223c4bd610fSDavid Xu if (p->p_procscopegrp == NULL) { 224c4bd610fSDavid Xu p->p_procscopegrp = newkg; 225c4bd610fSDavid Xu sched_init_concurrency(newkg); 226c4bd610fSDavid Xu sched_set_concurrency(newkg, 227a8b491c1SJulian Elischer thr_concurrency ? thr_concurrency : (2*mp_ncpus)); 228c4bd610fSDavid Xu linkkg = 1; 229c4bd610fSDavid Xu } else { 230c4bd610fSDavid Xu PROC_UNLOCK(p); 231c4bd610fSDavid Xu ksegrp_free(newkg); 232c4bd610fSDavid Xu goto retry; 233c4bd610fSDavid Xu } 234a8b491c1SJulian Elischer } 235a8b491c1SJulian Elischer } 236a8b491c1SJulian Elischer 237ed062c8dSJulian Elischer td->td_proc->p_flag |= P_HADTHREADS; 238ed062c8dSJulian Elischer newtd->td_sigmask = td->td_sigmask; 23989bb1cefSJeff Roberson mtx_lock_spin(&sched_lock); 240c4bd610fSDavid Xu if (linkkg) 241ed062c8dSJulian Elischer ksegrp_link(newkg, p); 242ed062c8dSJulian Elischer thread_link(newtd, newkg); 243ed062c8dSJulian Elischer PROC_UNLOCK(p); 24489bb1cefSJeff Roberson 245ed062c8dSJulian Elischer /* let the scheduler know about these things. */ 246c4bd610fSDavid Xu if (linkkg) 247ed062c8dSJulian Elischer sched_fork_ksegrp(td, newkg); 248ed062c8dSJulian Elischer sched_fork_thread(td, newtd); 249ed062c8dSJulian Elischer TD_SET_CAN_RUN(newtd); 250c4bd610fSDavid Xu /* if ((flags & THR_SUSPENDED) == 0) */ 251ed062c8dSJulian Elischer setrunqueue(newtd, SRQ_BORING); 25289bb1cefSJeff Roberson mtx_unlock_spin(&sched_lock); 25389bb1cefSJeff Roberson 25489bb1cefSJeff Roberson return (error); 25589bb1cefSJeff Roberson } 25689bb1cefSJeff Roberson 25789bb1cefSJeff Roberson int 25889bb1cefSJeff Roberson thr_self(struct thread *td, struct thr_self_args *uap) 259cd28f17dSMarcel Moolenaar /* long *id */ 26089bb1cefSJeff Roberson { 261cd28f17dSMarcel Moolenaar long id; 26289bb1cefSJeff Roberson int error; 26389bb1cefSJeff Roberson 264cd28f17dSMarcel Moolenaar id = td->td_tid; 265cd28f17dSMarcel Moolenaar if ((error = copyout(&id, uap->id, sizeof(long)))) 26689bb1cefSJeff Roberson return (error); 26789bb1cefSJeff Roberson 26889bb1cefSJeff Roberson return (0); 26989bb1cefSJeff Roberson } 27089bb1cefSJeff Roberson 27189bb1cefSJeff Roberson int 27289bb1cefSJeff Roberson thr_exit(struct thread *td, struct thr_exit_args *uap) 273401901acSMike Makonnen /* long *state */ 27489bb1cefSJeff Roberson { 27589bb1cefSJeff Roberson struct proc *p; 27689bb1cefSJeff Roberson 27789bb1cefSJeff Roberson p = td->td_proc; 27889bb1cefSJeff Roberson 279401901acSMike Makonnen /* Signal userland that it can free the stack. */ 2804938faa6SDavid Xu if ((void *)uap->state != NULL) { 281401901acSMike Makonnen suword((void *)uap->state, 1); 2824938faa6SDavid Xu kern_umtx_wake(td, uap->state, INT_MAX); 2834938faa6SDavid Xu } 284401901acSMike Makonnen 28589bb1cefSJeff Roberson PROC_LOCK(p); 2869104847fSDavid Xu sigqueue_flush(&td->td_sigqueue); 28789bb1cefSJeff Roberson mtx_lock_spin(&sched_lock); 28889bb1cefSJeff Roberson 28989bb1cefSJeff Roberson /* 290ed062c8dSJulian Elischer * Shutting down last thread in the proc. This will actually 291ed062c8dSJulian Elischer * call exit() in the trampoline when it returns. 29289bb1cefSJeff Roberson */ 293ed062c8dSJulian Elischer if (p->p_numthreads != 1) { 294ed062c8dSJulian Elischer thread_exit(); 295ed062c8dSJulian Elischer /* NOTREACHED */ 296ed062c8dSJulian Elischer } 29789bb1cefSJeff Roberson mtx_unlock_spin(&sched_lock); 298ed062c8dSJulian Elischer PROC_UNLOCK(p); 29989bb1cefSJeff Roberson return (0); 30089bb1cefSJeff Roberson } 30189bb1cefSJeff Roberson 30289bb1cefSJeff Roberson int 30389bb1cefSJeff Roberson thr_kill(struct thread *td, struct thr_kill_args *uap) 304cd28f17dSMarcel Moolenaar /* long id, int sig */ 30589bb1cefSJeff Roberson { 30689bb1cefSJeff Roberson struct thread *ttd; 30789bb1cefSJeff Roberson struct proc *p; 30889bb1cefSJeff Roberson int error; 30989bb1cefSJeff Roberson 31089bb1cefSJeff Roberson p = td->td_proc; 31189bb1cefSJeff Roberson error = 0; 31289bb1cefSJeff Roberson PROC_LOCK(p); 31371cfaac0SMike Makonnen FOREACH_THREAD_IN_PROC(p, ttd) { 314cd28f17dSMarcel Moolenaar if (ttd->td_tid == uap->id) 31589bb1cefSJeff Roberson break; 31671cfaac0SMike Makonnen } 31789bb1cefSJeff Roberson if (ttd == NULL) { 31889bb1cefSJeff Roberson error = ESRCH; 31989bb1cefSJeff Roberson goto out; 32089bb1cefSJeff Roberson } 32189bb1cefSJeff Roberson if (uap->sig == 0) 32289bb1cefSJeff Roberson goto out; 32389bb1cefSJeff Roberson if (!_SIG_VALID(uap->sig)) { 32489bb1cefSJeff Roberson error = EINVAL; 32589bb1cefSJeff Roberson goto out; 32689bb1cefSJeff Roberson } 3279104847fSDavid Xu tdsignal(ttd, uap->sig, NULL, SIGTARGET_TD); 32889bb1cefSJeff Roberson out: 32989bb1cefSJeff Roberson PROC_UNLOCK(p); 33089bb1cefSJeff Roberson return (error); 33189bb1cefSJeff Roberson } 3321713a516SMike Makonnen 3331713a516SMike Makonnen int 3341713a516SMike Makonnen thr_suspend(struct thread *td, struct thr_suspend_args *uap) 3351713a516SMike Makonnen /* const struct timespec *timeout */ 3361713a516SMike Makonnen { 3371713a516SMike Makonnen struct timespec ts; 3381713a516SMike Makonnen struct timeval tv; 3391713a516SMike Makonnen int error; 3401713a516SMike Makonnen int hz; 3411713a516SMike Makonnen 3421713a516SMike Makonnen hz = 0; 3431713a516SMike Makonnen error = 0; 3441713a516SMike Makonnen if (uap->timeout != NULL) { 3451713a516SMike Makonnen error = copyin((const void *)uap->timeout, (void *)&ts, 3461713a516SMike Makonnen sizeof(struct timespec)); 3471713a516SMike Makonnen if (error != 0) 3481713a516SMike Makonnen return (error); 3491713a516SMike Makonnen if (ts.tv_nsec < 0 || ts.tv_nsec > 1000000000) 3501713a516SMike Makonnen return (EINVAL); 3511713a516SMike Makonnen if (ts.tv_sec == 0 && ts.tv_nsec == 0) 3521713a516SMike Makonnen return (ETIMEDOUT); 3531713a516SMike Makonnen TIMESPEC_TO_TIMEVAL(&tv, &ts); 3541713a516SMike Makonnen hz = tvtohz(&tv); 3551713a516SMike Makonnen } 3561713a516SMike Makonnen PROC_LOCK(td->td_proc); 357c21e3b38SMike Makonnen if ((td->td_flags & TDF_THRWAKEUP) == 0) 3581713a516SMike Makonnen error = msleep((void *)td, &td->td_proc->p_mtx, 3591713a516SMike Makonnen td->td_priority | PCATCH, "lthr", hz); 360c1df5a1aSDavid Xu if (td->td_flags & TDF_THRWAKEUP) { 3611713a516SMike Makonnen mtx_lock_spin(&sched_lock); 3621713a516SMike Makonnen td->td_flags &= ~TDF_THRWAKEUP; 3631713a516SMike Makonnen mtx_unlock_spin(&sched_lock); 3641713a516SMike Makonnen PROC_UNLOCK(td->td_proc); 365c1df5a1aSDavid Xu return (0); 366c1df5a1aSDavid Xu } 367c1df5a1aSDavid Xu PROC_UNLOCK(td->td_proc); 368c1df5a1aSDavid Xu if (error == EWOULDBLOCK) 369c1df5a1aSDavid Xu error = ETIMEDOUT; 370c1df5a1aSDavid Xu else if (error == ERESTART) { 371c1df5a1aSDavid Xu if (hz != 0) 372c1df5a1aSDavid Xu error = EINTR; 373c1df5a1aSDavid Xu } 374c1df5a1aSDavid Xu return (error); 3751713a516SMike Makonnen } 3761713a516SMike Makonnen 3771713a516SMike Makonnen int 3781713a516SMike Makonnen thr_wake(struct thread *td, struct thr_wake_args *uap) 379cd28f17dSMarcel Moolenaar /* long id */ 3801713a516SMike Makonnen { 381cd28f17dSMarcel Moolenaar struct thread *ttd; 3821713a516SMike Makonnen 383b9fb5d42SMike Makonnen PROC_LOCK(td->td_proc); 384b9fb5d42SMike Makonnen FOREACH_THREAD_IN_PROC(td->td_proc, ttd) { 385cd28f17dSMarcel Moolenaar if (ttd->td_tid == uap->id) 3861713a516SMike Makonnen break; 3871713a516SMike Makonnen } 3881713a516SMike Makonnen if (ttd == NULL) { 389b9fb5d42SMike Makonnen PROC_UNLOCK(td->td_proc); 3901713a516SMike Makonnen return (ESRCH); 3911713a516SMike Makonnen } 3921713a516SMike Makonnen mtx_lock_spin(&sched_lock); 393cd28f17dSMarcel Moolenaar ttd->td_flags |= TDF_THRWAKEUP; 3941713a516SMike Makonnen mtx_unlock_spin(&sched_lock); 395c1df5a1aSDavid Xu wakeup((void *)ttd); 396b9fb5d42SMike Makonnen PROC_UNLOCK(td->td_proc); 3971713a516SMike Makonnen return (0); 3981713a516SMike Makonnen } 399