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 * System call interface. 6289bb1cefSJeff Roberson */ 6389bb1cefSJeff Roberson int 6489bb1cefSJeff Roberson thr_create(struct thread *td, struct thr_create_args *uap) 65cd28f17dSMarcel Moolenaar /* ucontext_t *ctx, long *id, int flags */ 6689bb1cefSJeff Roberson { 67ed062c8dSJulian Elischer struct thread *newtd; 6889bb1cefSJeff Roberson ucontext_t ctx; 69cd28f17dSMarcel Moolenaar long id; 7089bb1cefSJeff Roberson int error; 71ed062c8dSJulian Elischer struct ksegrp *kg, *newkg; 72ed062c8dSJulian Elischer struct proc *p; 73195f5806SDavid Xu int scope_sys; 7489bb1cefSJeff Roberson 75ed062c8dSJulian Elischer p = td->td_proc; 76ed062c8dSJulian Elischer kg = td->td_ksegrp; 7789bb1cefSJeff Roberson if ((error = copyin(uap->ctx, &ctx, sizeof(ctx)))) 7889bb1cefSJeff Roberson return (error); 7989bb1cefSJeff Roberson 80ed062c8dSJulian Elischer /* Have race condition but it is cheap */ 81ed062c8dSJulian Elischer if ((p->p_numksegrps >= max_groups_per_proc) || 82ed062c8dSJulian Elischer (p->p_numthreads >= max_threads_per_proc)) { 83ed062c8dSJulian Elischer return (EPROCLIM); 84ed062c8dSJulian Elischer } 85195f5806SDavid Xu 86195f5806SDavid Xu scope_sys = thr_scope_sys; 87ed062c8dSJulian Elischer /* Initialize our td and new ksegrp.. */ 88ed062c8dSJulian Elischer newtd = thread_alloc(); 89195f5806SDavid Xu if (scope_sys) 90ed062c8dSJulian Elischer newkg = ksegrp_alloc(); 91a8b491c1SJulian Elischer else 92a8b491c1SJulian Elischer newkg = kg; 9389bb1cefSJeff Roberson /* 9489bb1cefSJeff Roberson * Try the copyout as soon as we allocate the td so we don't have to 9589bb1cefSJeff Roberson * tear things down in a failure case below. 9689bb1cefSJeff Roberson */ 97ed062c8dSJulian Elischer id = newtd->td_tid; 98cd28f17dSMarcel Moolenaar if ((error = copyout(&id, uap->id, sizeof(long)))) { 99195f5806SDavid Xu if (scope_sys) 100ed062c8dSJulian Elischer ksegrp_free(newkg); 101ed062c8dSJulian Elischer thread_free(newtd); 10289bb1cefSJeff Roberson return (error); 10389bb1cefSJeff Roberson } 10489bb1cefSJeff Roberson 105ed062c8dSJulian Elischer bzero(&newtd->td_startzero, 1066db36923SDavid Schultz __rangeof(struct thread, td_startzero, td_endzero)); 107ed062c8dSJulian Elischer bcopy(&td->td_startcopy, &newtd->td_startcopy, 1086db36923SDavid Schultz __rangeof(struct thread, td_startcopy, td_endcopy)); 10989bb1cefSJeff Roberson 110195f5806SDavid Xu if (scope_sys) { 111ed062c8dSJulian Elischer bzero(&newkg->kg_startzero, 1126db36923SDavid Schultz __rangeof(struct ksegrp, kg_startzero, kg_endzero)); 113ed062c8dSJulian Elischer bcopy(&kg->kg_startcopy, &newkg->kg_startcopy, 1146db36923SDavid Schultz __rangeof(struct ksegrp, kg_startcopy, kg_endcopy)); 115a8b491c1SJulian Elischer } 11689bb1cefSJeff Roberson 117ed062c8dSJulian Elischer newtd->td_proc = td->td_proc; 118ed062c8dSJulian Elischer newtd->td_ucred = crhold(td->td_ucred); 11989bb1cefSJeff Roberson 12089bb1cefSJeff Roberson /* Set up our machine context. */ 121ed062c8dSJulian Elischer cpu_set_upcall(newtd, td); 122ed062c8dSJulian Elischer error = set_mcontext(newtd, &ctx.uc_mcontext); 12389bb1cefSJeff Roberson if (error != 0) { 124195f5806SDavid Xu if (scope_sys) 125ed062c8dSJulian Elischer ksegrp_free(newkg); 126ed062c8dSJulian Elischer thread_free(newtd); 127ed062c8dSJulian Elischer crfree(td->td_ucred); 12889bb1cefSJeff Roberson goto out; 12989bb1cefSJeff Roberson } 13089bb1cefSJeff Roberson 13189bb1cefSJeff Roberson /* Link the thread and kse into the ksegrp and make it runnable. */ 132ed062c8dSJulian Elischer PROC_LOCK(td->td_proc); 133195f5806SDavid Xu if (scope_sys) { 134a8b491c1SJulian Elischer sched_init_concurrency(newkg); 135a8b491c1SJulian Elischer } else { 136a8b491c1SJulian Elischer if ((td->td_proc->p_flag & P_HADTHREADS) == 0) { 137a8b491c1SJulian Elischer sched_set_concurrency(kg, 138a8b491c1SJulian Elischer thr_concurrency ? thr_concurrency : (2*mp_ncpus)); 139a8b491c1SJulian Elischer } 140a8b491c1SJulian Elischer } 141a8b491c1SJulian Elischer 142ed062c8dSJulian Elischer td->td_proc->p_flag |= P_HADTHREADS; 143ed062c8dSJulian Elischer newtd->td_sigmask = td->td_sigmask; 14489bb1cefSJeff Roberson mtx_lock_spin(&sched_lock); 145195f5806SDavid Xu if (scope_sys) 146ed062c8dSJulian Elischer ksegrp_link(newkg, p); 147ed062c8dSJulian Elischer thread_link(newtd, newkg); 148ed062c8dSJulian Elischer mtx_unlock_spin(&sched_lock); 149ed062c8dSJulian Elischer PROC_UNLOCK(p); 15089bb1cefSJeff Roberson 151ed062c8dSJulian Elischer /* let the scheduler know about these things. */ 152ed062c8dSJulian Elischer mtx_lock_spin(&sched_lock); 153195f5806SDavid Xu if (scope_sys) 154ed062c8dSJulian Elischer sched_fork_ksegrp(td, newkg); 155ed062c8dSJulian Elischer sched_fork_thread(td, newtd); 15689bb1cefSJeff Roberson 157ed062c8dSJulian Elischer TD_SET_CAN_RUN(newtd); 15889bb1cefSJeff Roberson if ((uap->flags & THR_SUSPENDED) == 0) 159ed062c8dSJulian Elischer setrunqueue(newtd, SRQ_BORING); 16089bb1cefSJeff Roberson 16189bb1cefSJeff Roberson mtx_unlock_spin(&sched_lock); 16289bb1cefSJeff Roberson 16389bb1cefSJeff Roberson out: 16489bb1cefSJeff Roberson return (error); 16589bb1cefSJeff Roberson } 16689bb1cefSJeff Roberson 16789bb1cefSJeff Roberson int 16889bb1cefSJeff Roberson thr_self(struct thread *td, struct thr_self_args *uap) 169cd28f17dSMarcel Moolenaar /* long *id */ 17089bb1cefSJeff Roberson { 171cd28f17dSMarcel Moolenaar long id; 17289bb1cefSJeff Roberson int error; 17389bb1cefSJeff Roberson 174cd28f17dSMarcel Moolenaar id = td->td_tid; 175cd28f17dSMarcel Moolenaar if ((error = copyout(&id, uap->id, sizeof(long)))) 17689bb1cefSJeff Roberson return (error); 17789bb1cefSJeff Roberson 17889bb1cefSJeff Roberson return (0); 17989bb1cefSJeff Roberson } 18089bb1cefSJeff Roberson 18189bb1cefSJeff Roberson int 18289bb1cefSJeff Roberson thr_exit(struct thread *td, struct thr_exit_args *uap) 183401901acSMike Makonnen /* long *state */ 18489bb1cefSJeff Roberson { 18589bb1cefSJeff Roberson struct proc *p; 18689bb1cefSJeff Roberson 18789bb1cefSJeff Roberson p = td->td_proc; 18889bb1cefSJeff Roberson 189401901acSMike Makonnen /* Signal userland that it can free the stack. */ 190401901acSMike Makonnen if ((void *)uap->state != NULL) 191401901acSMike Makonnen suword((void *)uap->state, 1); 192401901acSMike Makonnen 19389bb1cefSJeff Roberson PROC_LOCK(p); 19489bb1cefSJeff Roberson mtx_lock_spin(&sched_lock); 19589bb1cefSJeff Roberson 19689bb1cefSJeff Roberson /* 197ed062c8dSJulian Elischer * Shutting down last thread in the proc. This will actually 198ed062c8dSJulian Elischer * call exit() in the trampoline when it returns. 19989bb1cefSJeff Roberson */ 200ed062c8dSJulian Elischer if (p->p_numthreads != 1) { 201ed062c8dSJulian Elischer thread_exit(); 202ed062c8dSJulian Elischer /* NOTREACHED */ 203ed062c8dSJulian Elischer } 20489bb1cefSJeff Roberson mtx_unlock_spin(&sched_lock); 205ed062c8dSJulian Elischer PROC_UNLOCK(p); 20689bb1cefSJeff Roberson return (0); 20789bb1cefSJeff Roberson } 20889bb1cefSJeff Roberson 20989bb1cefSJeff Roberson int 21089bb1cefSJeff Roberson thr_kill(struct thread *td, struct thr_kill_args *uap) 211cd28f17dSMarcel Moolenaar /* long id, int sig */ 21289bb1cefSJeff Roberson { 21389bb1cefSJeff Roberson struct thread *ttd; 21489bb1cefSJeff Roberson struct proc *p; 21589bb1cefSJeff Roberson int error; 21689bb1cefSJeff Roberson 21789bb1cefSJeff Roberson p = td->td_proc; 21889bb1cefSJeff Roberson error = 0; 21989bb1cefSJeff Roberson PROC_LOCK(p); 22071cfaac0SMike Makonnen FOREACH_THREAD_IN_PROC(p, ttd) { 221cd28f17dSMarcel Moolenaar if (ttd->td_tid == uap->id) 22289bb1cefSJeff Roberson break; 22371cfaac0SMike Makonnen } 22489bb1cefSJeff Roberson if (ttd == NULL) { 22589bb1cefSJeff Roberson error = ESRCH; 22689bb1cefSJeff Roberson goto out; 22789bb1cefSJeff Roberson } 22889bb1cefSJeff Roberson if (uap->sig == 0) 22989bb1cefSJeff Roberson goto out; 23089bb1cefSJeff Roberson if (!_SIG_VALID(uap->sig)) { 23189bb1cefSJeff Roberson error = EINVAL; 23289bb1cefSJeff Roberson goto out; 23389bb1cefSJeff Roberson } 234c197abc4SMike Makonnen tdsignal(ttd, uap->sig, SIGTARGET_TD); 23589bb1cefSJeff Roberson out: 23689bb1cefSJeff Roberson PROC_UNLOCK(p); 23789bb1cefSJeff Roberson return (error); 23889bb1cefSJeff Roberson } 2391713a516SMike Makonnen 2401713a516SMike Makonnen int 2411713a516SMike Makonnen thr_suspend(struct thread *td, struct thr_suspend_args *uap) 2421713a516SMike Makonnen /* const struct timespec *timeout */ 2431713a516SMike Makonnen { 2441713a516SMike Makonnen struct timespec ts; 2451713a516SMike Makonnen struct timeval tv; 2461713a516SMike Makonnen int error; 2471713a516SMike Makonnen int hz; 2481713a516SMike Makonnen 2491713a516SMike Makonnen hz = 0; 2501713a516SMike Makonnen error = 0; 2511713a516SMike Makonnen if (uap->timeout != NULL) { 2521713a516SMike Makonnen error = copyin((const void *)uap->timeout, (void *)&ts, 2531713a516SMike Makonnen sizeof(struct timespec)); 2541713a516SMike Makonnen if (error != 0) 2551713a516SMike Makonnen return (error); 2561713a516SMike Makonnen if (ts.tv_nsec < 0 || ts.tv_nsec > 1000000000) 2571713a516SMike Makonnen return (EINVAL); 2581713a516SMike Makonnen if (ts.tv_sec == 0 && ts.tv_nsec == 0) 2591713a516SMike Makonnen return (ETIMEDOUT); 2601713a516SMike Makonnen TIMESPEC_TO_TIMEVAL(&tv, &ts); 2611713a516SMike Makonnen hz = tvtohz(&tv); 2621713a516SMike Makonnen } 2631713a516SMike Makonnen PROC_LOCK(td->td_proc); 264c21e3b38SMike Makonnen if ((td->td_flags & TDF_THRWAKEUP) == 0) 2651713a516SMike Makonnen error = msleep((void *)td, &td->td_proc->p_mtx, 2661713a516SMike Makonnen td->td_priority | PCATCH, "lthr", hz); 2671713a516SMike Makonnen mtx_lock_spin(&sched_lock); 2681713a516SMike Makonnen td->td_flags &= ~TDF_THRWAKEUP; 2691713a516SMike Makonnen mtx_unlock_spin(&sched_lock); 2701713a516SMike Makonnen PROC_UNLOCK(td->td_proc); 2711713a516SMike Makonnen return (error == EWOULDBLOCK ? ETIMEDOUT : error); 2721713a516SMike Makonnen } 2731713a516SMike Makonnen 2741713a516SMike Makonnen int 2751713a516SMike Makonnen thr_wake(struct thread *td, struct thr_wake_args *uap) 276cd28f17dSMarcel Moolenaar /* long id */ 2771713a516SMike Makonnen { 278cd28f17dSMarcel Moolenaar struct thread *ttd; 2791713a516SMike Makonnen 280b9fb5d42SMike Makonnen PROC_LOCK(td->td_proc); 281b9fb5d42SMike Makonnen FOREACH_THREAD_IN_PROC(td->td_proc, ttd) { 282cd28f17dSMarcel Moolenaar if (ttd->td_tid == uap->id) 2831713a516SMike Makonnen break; 2841713a516SMike Makonnen } 2851713a516SMike Makonnen if (ttd == NULL) { 286b9fb5d42SMike Makonnen PROC_UNLOCK(td->td_proc); 2871713a516SMike Makonnen return (ESRCH); 2881713a516SMike Makonnen } 2891713a516SMike Makonnen mtx_lock_spin(&sched_lock); 290cd28f17dSMarcel Moolenaar ttd->td_flags |= TDF_THRWAKEUP; 2911713a516SMike Makonnen mtx_unlock_spin(&sched_lock); 292cd28f17dSMarcel Moolenaar wakeup_one((void *)ttd); 293b9fb5d42SMike Makonnen PROC_UNLOCK(td->td_proc); 2941713a516SMike Makonnen return (0); 2951713a516SMike Makonnen } 296