189bb1cefSJeff Roberson /* 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> 4589bb1cefSJeff Roberson 4689bb1cefSJeff Roberson #include <machine/frame.h> 4789bb1cefSJeff Roberson 48ed062c8dSJulian Elischer extern int max_threads_per_proc; 49ed062c8dSJulian Elischer extern int max_groups_per_proc; 50ed062c8dSJulian Elischer 51a8b491c1SJulian Elischer SYSCTL_DECL(_kern_threads); 52a8b491c1SJulian Elischer static int thr_scope_sys = 0; 53a8b491c1SJulian Elischer SYSCTL_INT(_kern_threads, OID_AUTO, thr_scope_sys, CTLFLAG_RW, 54a8b491c1SJulian Elischer &thr_scope_sys, 0, "sys or proc scope scheduling"); 55a8b491c1SJulian Elischer 5624640982SJulian Elischer static int thr_concurrency = 0; 57a8b491c1SJulian Elischer SYSCTL_INT(_kern_threads, OID_AUTO, thr_concurrency, CTLFLAG_RW, 58a8b491c1SJulian Elischer &thr_concurrency, 0, "a concurrency value if not default"); 59a8b491c1SJulian Elischer 6089bb1cefSJeff Roberson /* 6189bb1cefSJeff Roberson * Back end support functions. 6289bb1cefSJeff Roberson */ 6389bb1cefSJeff Roberson 6489bb1cefSJeff Roberson #define RANGEOF(type, start, end) (offsetof(type, end) - offsetof(type, start)) 6589bb1cefSJeff Roberson 6689bb1cefSJeff Roberson /* 6789bb1cefSJeff Roberson * System call interface. 6889bb1cefSJeff Roberson */ 6989bb1cefSJeff Roberson int 7089bb1cefSJeff Roberson thr_create(struct thread *td, struct thr_create_args *uap) 71cd28f17dSMarcel Moolenaar /* ucontext_t *ctx, long *id, int flags */ 7289bb1cefSJeff Roberson { 73ed062c8dSJulian Elischer struct thread *newtd; 7489bb1cefSJeff Roberson ucontext_t ctx; 75cd28f17dSMarcel Moolenaar long id; 7689bb1cefSJeff Roberson int error; 77ed062c8dSJulian Elischer struct ksegrp *kg, *newkg; 78ed062c8dSJulian Elischer struct proc *p; 79195f5806SDavid Xu int scope_sys; 8089bb1cefSJeff Roberson 81ed062c8dSJulian Elischer p = td->td_proc; 82ed062c8dSJulian Elischer kg = td->td_ksegrp; 8389bb1cefSJeff Roberson if ((error = copyin(uap->ctx, &ctx, sizeof(ctx)))) 8489bb1cefSJeff Roberson return (error); 8589bb1cefSJeff Roberson 86ed062c8dSJulian Elischer /* Have race condition but it is cheap */ 87ed062c8dSJulian Elischer if ((p->p_numksegrps >= max_groups_per_proc) || 88ed062c8dSJulian Elischer (p->p_numthreads >= max_threads_per_proc)) { 89ed062c8dSJulian Elischer return (EPROCLIM); 90ed062c8dSJulian Elischer } 91195f5806SDavid Xu 92195f5806SDavid Xu scope_sys = thr_scope_sys; 93ed062c8dSJulian Elischer /* Initialize our td and new ksegrp.. */ 94ed062c8dSJulian Elischer newtd = thread_alloc(); 95195f5806SDavid Xu if (scope_sys) 96ed062c8dSJulian Elischer newkg = ksegrp_alloc(); 97a8b491c1SJulian Elischer else 98a8b491c1SJulian Elischer newkg = kg; 9989bb1cefSJeff Roberson /* 10089bb1cefSJeff Roberson * Try the copyout as soon as we allocate the td so we don't have to 10189bb1cefSJeff Roberson * tear things down in a failure case below. 10289bb1cefSJeff Roberson */ 103ed062c8dSJulian Elischer id = newtd->td_tid; 104cd28f17dSMarcel Moolenaar if ((error = copyout(&id, uap->id, sizeof(long)))) { 105195f5806SDavid Xu if (scope_sys) 106ed062c8dSJulian Elischer ksegrp_free(newkg); 107ed062c8dSJulian Elischer thread_free(newtd); 10889bb1cefSJeff Roberson return (error); 10989bb1cefSJeff Roberson } 11089bb1cefSJeff Roberson 111ed062c8dSJulian Elischer bzero(&newtd->td_startzero, 11289bb1cefSJeff Roberson (unsigned) RANGEOF(struct thread, td_startzero, td_endzero)); 113ed062c8dSJulian Elischer bcopy(&td->td_startcopy, &newtd->td_startcopy, 11489bb1cefSJeff Roberson (unsigned) RANGEOF(struct thread, td_startcopy, td_endcopy)); 11589bb1cefSJeff Roberson 116195f5806SDavid Xu if (scope_sys) { 117ed062c8dSJulian Elischer bzero(&newkg->kg_startzero, 118ed062c8dSJulian Elischer (unsigned)RANGEOF(struct ksegrp, kg_startzero, kg_endzero)); 119ed062c8dSJulian Elischer bcopy(&kg->kg_startcopy, &newkg->kg_startcopy, 120ed062c8dSJulian Elischer (unsigned)RANGEOF(struct ksegrp, kg_startcopy, kg_endcopy)); 121a8b491c1SJulian Elischer } 12289bb1cefSJeff Roberson 123ed062c8dSJulian Elischer newtd->td_proc = td->td_proc; 124ed062c8dSJulian Elischer newtd->td_ucred = crhold(td->td_ucred); 12589bb1cefSJeff Roberson 12689bb1cefSJeff Roberson /* Set up our machine context. */ 127ed062c8dSJulian Elischer cpu_set_upcall(newtd, td); 128ed062c8dSJulian Elischer error = set_mcontext(newtd, &ctx.uc_mcontext); 12989bb1cefSJeff Roberson if (error != 0) { 130195f5806SDavid Xu if (scope_sys) 131ed062c8dSJulian Elischer ksegrp_free(newkg); 132ed062c8dSJulian Elischer thread_free(newtd); 133ed062c8dSJulian Elischer crfree(td->td_ucred); 13489bb1cefSJeff Roberson goto out; 13589bb1cefSJeff Roberson } 13689bb1cefSJeff Roberson 13789bb1cefSJeff Roberson /* Link the thread and kse into the ksegrp and make it runnable. */ 138ed062c8dSJulian Elischer PROC_LOCK(td->td_proc); 139195f5806SDavid Xu if (scope_sys) { 140a8b491c1SJulian Elischer sched_init_concurrency(newkg); 141a8b491c1SJulian Elischer } else { 142a8b491c1SJulian Elischer if ((td->td_proc->p_flag & P_HADTHREADS) == 0) { 143a8b491c1SJulian Elischer sched_set_concurrency(kg, 144a8b491c1SJulian Elischer thr_concurrency ? thr_concurrency : (2*mp_ncpus)); 145a8b491c1SJulian Elischer } 146a8b491c1SJulian Elischer } 147a8b491c1SJulian Elischer 148ed062c8dSJulian Elischer td->td_proc->p_flag |= P_HADTHREADS; 149ed062c8dSJulian Elischer newtd->td_sigmask = td->td_sigmask; 15089bb1cefSJeff Roberson mtx_lock_spin(&sched_lock); 151195f5806SDavid Xu if (scope_sys) 152ed062c8dSJulian Elischer ksegrp_link(newkg, p); 153ed062c8dSJulian Elischer thread_link(newtd, newkg); 154ed062c8dSJulian Elischer mtx_unlock_spin(&sched_lock); 155ed062c8dSJulian Elischer PROC_UNLOCK(p); 15689bb1cefSJeff Roberson 157ed062c8dSJulian Elischer /* let the scheduler know about these things. */ 158ed062c8dSJulian Elischer mtx_lock_spin(&sched_lock); 159195f5806SDavid Xu if (scope_sys) 160ed062c8dSJulian Elischer sched_fork_ksegrp(td, newkg); 161ed062c8dSJulian Elischer sched_fork_thread(td, newtd); 16289bb1cefSJeff Roberson 163ed062c8dSJulian Elischer TD_SET_CAN_RUN(newtd); 16489bb1cefSJeff Roberson if ((uap->flags & THR_SUSPENDED) == 0) 165ed062c8dSJulian Elischer setrunqueue(newtd, SRQ_BORING); 16689bb1cefSJeff Roberson 16789bb1cefSJeff Roberson mtx_unlock_spin(&sched_lock); 16889bb1cefSJeff Roberson 16989bb1cefSJeff Roberson out: 17089bb1cefSJeff Roberson return (error); 17189bb1cefSJeff Roberson } 17289bb1cefSJeff Roberson 17389bb1cefSJeff Roberson int 17489bb1cefSJeff Roberson thr_self(struct thread *td, struct thr_self_args *uap) 175cd28f17dSMarcel Moolenaar /* long *id */ 17689bb1cefSJeff Roberson { 177cd28f17dSMarcel Moolenaar long id; 17889bb1cefSJeff Roberson int error; 17989bb1cefSJeff Roberson 180cd28f17dSMarcel Moolenaar id = td->td_tid; 181cd28f17dSMarcel Moolenaar if ((error = copyout(&id, uap->id, sizeof(long)))) 18289bb1cefSJeff Roberson return (error); 18389bb1cefSJeff Roberson 18489bb1cefSJeff Roberson return (0); 18589bb1cefSJeff Roberson } 18689bb1cefSJeff Roberson 18789bb1cefSJeff Roberson int 18889bb1cefSJeff Roberson thr_exit(struct thread *td, struct thr_exit_args *uap) 189401901acSMike Makonnen /* long *state */ 19089bb1cefSJeff Roberson { 19189bb1cefSJeff Roberson struct proc *p; 19289bb1cefSJeff Roberson 19389bb1cefSJeff Roberson p = td->td_proc; 19489bb1cefSJeff Roberson 195401901acSMike Makonnen /* Signal userland that it can free the stack. */ 196401901acSMike Makonnen if ((void *)uap->state != NULL) 197401901acSMike Makonnen suword((void *)uap->state, 1); 198401901acSMike Makonnen 19989bb1cefSJeff Roberson PROC_LOCK(p); 20089bb1cefSJeff Roberson mtx_lock_spin(&sched_lock); 20189bb1cefSJeff Roberson 20289bb1cefSJeff Roberson /* 203ed062c8dSJulian Elischer * Shutting down last thread in the proc. This will actually 204ed062c8dSJulian Elischer * call exit() in the trampoline when it returns. 20589bb1cefSJeff Roberson */ 206ed062c8dSJulian Elischer if (p->p_numthreads != 1) { 207ed062c8dSJulian Elischer thread_exit(); 208ed062c8dSJulian Elischer /* NOTREACHED */ 209ed062c8dSJulian Elischer } 21089bb1cefSJeff Roberson mtx_unlock_spin(&sched_lock); 211ed062c8dSJulian Elischer PROC_UNLOCK(p); 21289bb1cefSJeff Roberson return (0); 21389bb1cefSJeff Roberson } 21489bb1cefSJeff Roberson 21589bb1cefSJeff Roberson int 21689bb1cefSJeff Roberson thr_kill(struct thread *td, struct thr_kill_args *uap) 217cd28f17dSMarcel Moolenaar /* long id, int sig */ 21889bb1cefSJeff Roberson { 21989bb1cefSJeff Roberson struct thread *ttd; 22089bb1cefSJeff Roberson struct proc *p; 22189bb1cefSJeff Roberson int error; 22289bb1cefSJeff Roberson 22389bb1cefSJeff Roberson p = td->td_proc; 22489bb1cefSJeff Roberson error = 0; 22589bb1cefSJeff Roberson PROC_LOCK(p); 22671cfaac0SMike Makonnen FOREACH_THREAD_IN_PROC(p, ttd) { 227cd28f17dSMarcel Moolenaar if (ttd->td_tid == uap->id) 22889bb1cefSJeff Roberson break; 22971cfaac0SMike Makonnen } 23089bb1cefSJeff Roberson if (ttd == NULL) { 23189bb1cefSJeff Roberson error = ESRCH; 23289bb1cefSJeff Roberson goto out; 23389bb1cefSJeff Roberson } 23489bb1cefSJeff Roberson if (uap->sig == 0) 23589bb1cefSJeff Roberson goto out; 23689bb1cefSJeff Roberson if (!_SIG_VALID(uap->sig)) { 23789bb1cefSJeff Roberson error = EINVAL; 23889bb1cefSJeff Roberson goto out; 23989bb1cefSJeff Roberson } 240c197abc4SMike Makonnen tdsignal(ttd, uap->sig, SIGTARGET_TD); 24189bb1cefSJeff Roberson out: 24289bb1cefSJeff Roberson PROC_UNLOCK(p); 24389bb1cefSJeff Roberson return (error); 24489bb1cefSJeff Roberson } 2451713a516SMike Makonnen 2461713a516SMike Makonnen int 2471713a516SMike Makonnen thr_suspend(struct thread *td, struct thr_suspend_args *uap) 2481713a516SMike Makonnen /* const struct timespec *timeout */ 2491713a516SMike Makonnen { 2501713a516SMike Makonnen struct timespec ts; 2511713a516SMike Makonnen struct timeval tv; 2521713a516SMike Makonnen int error; 2531713a516SMike Makonnen int hz; 2541713a516SMike Makonnen 2551713a516SMike Makonnen hz = 0; 2561713a516SMike Makonnen error = 0; 2571713a516SMike Makonnen if (uap->timeout != NULL) { 2581713a516SMike Makonnen error = copyin((const void *)uap->timeout, (void *)&ts, 2591713a516SMike Makonnen sizeof(struct timespec)); 2601713a516SMike Makonnen if (error != 0) 2611713a516SMike Makonnen return (error); 2621713a516SMike Makonnen if (ts.tv_nsec < 0 || ts.tv_nsec > 1000000000) 2631713a516SMike Makonnen return (EINVAL); 2641713a516SMike Makonnen if (ts.tv_sec == 0 && ts.tv_nsec == 0) 2651713a516SMike Makonnen return (ETIMEDOUT); 2661713a516SMike Makonnen TIMESPEC_TO_TIMEVAL(&tv, &ts); 2671713a516SMike Makonnen hz = tvtohz(&tv); 2681713a516SMike Makonnen } 2691713a516SMike Makonnen PROC_LOCK(td->td_proc); 270c21e3b38SMike Makonnen if ((td->td_flags & TDF_THRWAKEUP) == 0) 2711713a516SMike Makonnen error = msleep((void *)td, &td->td_proc->p_mtx, 2721713a516SMike Makonnen td->td_priority | PCATCH, "lthr", hz); 2731713a516SMike Makonnen mtx_lock_spin(&sched_lock); 2741713a516SMike Makonnen td->td_flags &= ~TDF_THRWAKEUP; 2751713a516SMike Makonnen mtx_unlock_spin(&sched_lock); 2761713a516SMike Makonnen PROC_UNLOCK(td->td_proc); 2771713a516SMike Makonnen return (error == EWOULDBLOCK ? ETIMEDOUT : error); 2781713a516SMike Makonnen } 2791713a516SMike Makonnen 2801713a516SMike Makonnen int 2811713a516SMike Makonnen thr_wake(struct thread *td, struct thr_wake_args *uap) 282cd28f17dSMarcel Moolenaar /* long id */ 2831713a516SMike Makonnen { 284cd28f17dSMarcel Moolenaar struct thread *ttd; 2851713a516SMike Makonnen 286b9fb5d42SMike Makonnen PROC_LOCK(td->td_proc); 287b9fb5d42SMike Makonnen FOREACH_THREAD_IN_PROC(td->td_proc, ttd) { 288cd28f17dSMarcel Moolenaar if (ttd->td_tid == uap->id) 2891713a516SMike Makonnen break; 2901713a516SMike Makonnen } 2911713a516SMike Makonnen if (ttd == NULL) { 292b9fb5d42SMike Makonnen PROC_UNLOCK(td->td_proc); 2931713a516SMike Makonnen return (ESRCH); 2941713a516SMike Makonnen } 2951713a516SMike Makonnen mtx_lock_spin(&sched_lock); 296cd28f17dSMarcel Moolenaar ttd->td_flags |= TDF_THRWAKEUP; 2971713a516SMike Makonnen mtx_unlock_spin(&sched_lock); 298cd28f17dSMarcel Moolenaar wakeup_one((void *)ttd); 299b9fb5d42SMike Makonnen PROC_UNLOCK(td->td_proc); 3001713a516SMike Makonnen return (0); 3011713a516SMike Makonnen } 302