xref: /freebsd/sys/kern/kern_thr.c (revision a8b491c121bc27e671efda1f96cf2ca3fa2d4aa9)
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 
56a8b491c1SJulian Elischer static int thr_concurency = 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;
7989bb1cefSJeff Roberson 
80ed062c8dSJulian Elischer 	p = td->td_proc;
81ed062c8dSJulian Elischer 	kg = td->td_ksegrp;
8289bb1cefSJeff Roberson 	if ((error = copyin(uap->ctx, &ctx, sizeof(ctx))))
8389bb1cefSJeff Roberson 		return (error);
8489bb1cefSJeff Roberson 
85ed062c8dSJulian Elischer 	/* Have race condition but it is cheap */
86ed062c8dSJulian Elischer 	if ((p->p_numksegrps >= max_groups_per_proc) ||
87ed062c8dSJulian Elischer 	    (p->p_numthreads >= max_threads_per_proc)) {
88ed062c8dSJulian Elischer 		return (EPROCLIM);
89ed062c8dSJulian Elischer 	}
90ed062c8dSJulian Elischer 	/* Initialize our td and new ksegrp.. */
91ed062c8dSJulian Elischer 	newtd = thread_alloc();
92a8b491c1SJulian Elischer 	if (thr_scope_sys)
93ed062c8dSJulian Elischer 		newkg = ksegrp_alloc();
94a8b491c1SJulian Elischer 	else
95a8b491c1SJulian Elischer 		newkg = kg;
9689bb1cefSJeff Roberson 	/*
9789bb1cefSJeff Roberson 	 * Try the copyout as soon as we allocate the td so we don't have to
9889bb1cefSJeff Roberson 	 * tear things down in a failure case below.
9989bb1cefSJeff Roberson 	 */
100ed062c8dSJulian Elischer 	id = newtd->td_tid;
101cd28f17dSMarcel Moolenaar 	if ((error = copyout(&id, uap->id, sizeof(long)))) {
102a8b491c1SJulian Elischer 		if (thr_scope_sys)
103ed062c8dSJulian Elischer 			ksegrp_free(newkg);
104ed062c8dSJulian Elischer 		thread_free(newtd);
10589bb1cefSJeff Roberson 		return (error);
10689bb1cefSJeff Roberson 	}
10789bb1cefSJeff Roberson 
108ed062c8dSJulian Elischer 	bzero(&newtd->td_startzero,
10989bb1cefSJeff Roberson 	    (unsigned) RANGEOF(struct thread, td_startzero, td_endzero));
110ed062c8dSJulian Elischer 	bcopy(&td->td_startcopy, &newtd->td_startcopy,
11189bb1cefSJeff Roberson 	    (unsigned) RANGEOF(struct thread, td_startcopy, td_endcopy));
11289bb1cefSJeff Roberson 
113a8b491c1SJulian Elischer 	if (thr_scope_sys) {
114ed062c8dSJulian Elischer 		bzero(&newkg->kg_startzero,
115ed062c8dSJulian Elischer 		    (unsigned)RANGEOF(struct ksegrp, kg_startzero, kg_endzero));
116ed062c8dSJulian Elischer 		bcopy(&kg->kg_startcopy, &newkg->kg_startcopy,
117ed062c8dSJulian Elischer 		    (unsigned)RANGEOF(struct ksegrp, kg_startcopy, kg_endcopy));
118a8b491c1SJulian Elischer 	}
11989bb1cefSJeff Roberson 
120ed062c8dSJulian Elischer 	newtd->td_proc = td->td_proc;
121ed062c8dSJulian Elischer 	newtd->td_ucred = crhold(td->td_ucred);
12289bb1cefSJeff Roberson 
12389bb1cefSJeff Roberson 	/* Set up our machine context. */
124ed062c8dSJulian Elischer 	cpu_set_upcall(newtd, td);
125ed062c8dSJulian Elischer 	error = set_mcontext(newtd, &ctx.uc_mcontext);
12689bb1cefSJeff Roberson 	if (error != 0) {
127a8b491c1SJulian Elischer 		if (thr_scope_sys)
128ed062c8dSJulian Elischer 			ksegrp_free(newkg);
129ed062c8dSJulian Elischer 		thread_free(newtd);
130ed062c8dSJulian Elischer 		crfree(td->td_ucred);
13189bb1cefSJeff Roberson 		goto out;
13289bb1cefSJeff Roberson 	}
13389bb1cefSJeff Roberson 
13489bb1cefSJeff Roberson 	/* Link the thread and kse into the ksegrp and make it runnable. */
135ed062c8dSJulian Elischer 	PROC_LOCK(td->td_proc);
136a8b491c1SJulian Elischer 	if (thr_scope_sys) {
137a8b491c1SJulian Elischer 			sched_init_concurrency(newkg);
138a8b491c1SJulian Elischer 	} else {
139a8b491c1SJulian Elischer 		if ((td->td_proc->p_flag & P_HADTHREADS) == 0) {
140a8b491c1SJulian Elischer 			sched_set_concurrency(kg,
141a8b491c1SJulian Elischer 			    thr_concurrency ? thr_concurrency : (2*mp_ncpus));
142a8b491c1SJulian Elischer 		}
143a8b491c1SJulian Elischer 	}
144a8b491c1SJulian Elischer 
145ed062c8dSJulian Elischer 	td->td_proc->p_flag |= P_HADTHREADS;
146ed062c8dSJulian Elischer 	newtd->td_sigmask = td->td_sigmask;
14789bb1cefSJeff Roberson 	mtx_lock_spin(&sched_lock);
148a8b491c1SJulian Elischer 	if (thr_scope_sys)
149ed062c8dSJulian Elischer 		ksegrp_link(newkg, p);
150ed062c8dSJulian Elischer 	thread_link(newtd, newkg);
151ed062c8dSJulian Elischer 	mtx_unlock_spin(&sched_lock);
152ed062c8dSJulian Elischer 	PROC_UNLOCK(p);
15389bb1cefSJeff Roberson 
154ed062c8dSJulian Elischer 	/* let the scheduler know about these things. */
155ed062c8dSJulian Elischer 	mtx_lock_spin(&sched_lock);
156a8b491c1SJulian Elischer 	if (thr_scope_sys)
157ed062c8dSJulian Elischer 		sched_fork_ksegrp(td, newkg);
158ed062c8dSJulian Elischer 	sched_fork_thread(td, newtd);
15989bb1cefSJeff Roberson 
160ed062c8dSJulian Elischer 	TD_SET_CAN_RUN(newtd);
16189bb1cefSJeff Roberson 	if ((uap->flags & THR_SUSPENDED) == 0)
162ed062c8dSJulian Elischer 		setrunqueue(newtd, SRQ_BORING);
16389bb1cefSJeff Roberson 
16489bb1cefSJeff Roberson 	mtx_unlock_spin(&sched_lock);
16589bb1cefSJeff Roberson 
16689bb1cefSJeff Roberson out:
16789bb1cefSJeff Roberson 	return (error);
16889bb1cefSJeff Roberson }
16989bb1cefSJeff Roberson 
17089bb1cefSJeff Roberson int
17189bb1cefSJeff Roberson thr_self(struct thread *td, struct thr_self_args *uap)
172cd28f17dSMarcel Moolenaar     /* long *id */
17389bb1cefSJeff Roberson {
174cd28f17dSMarcel Moolenaar 	long id;
17589bb1cefSJeff Roberson 	int error;
17689bb1cefSJeff Roberson 
177cd28f17dSMarcel Moolenaar 	id = td->td_tid;
178cd28f17dSMarcel Moolenaar 	if ((error = copyout(&id, uap->id, sizeof(long))))
17989bb1cefSJeff Roberson 		return (error);
18089bb1cefSJeff Roberson 
18189bb1cefSJeff Roberson 	return (0);
18289bb1cefSJeff Roberson }
18389bb1cefSJeff Roberson 
18489bb1cefSJeff Roberson int
18589bb1cefSJeff Roberson thr_exit(struct thread *td, struct thr_exit_args *uap)
18689bb1cefSJeff Roberson     /* NULL */
18789bb1cefSJeff Roberson {
18889bb1cefSJeff Roberson 	struct proc *p;
18989bb1cefSJeff Roberson 
19089bb1cefSJeff Roberson 	p = td->td_proc;
19189bb1cefSJeff Roberson 
19289bb1cefSJeff Roberson 	PROC_LOCK(p);
19389bb1cefSJeff Roberson 	mtx_lock_spin(&sched_lock);
19489bb1cefSJeff Roberson 
19589bb1cefSJeff Roberson 	/*
196ed062c8dSJulian Elischer 	 * Shutting down last thread in the proc.  This will actually
197ed062c8dSJulian Elischer 	 * call exit() in the trampoline when it returns.
19889bb1cefSJeff Roberson 	 */
199ed062c8dSJulian Elischer 	if (p->p_numthreads != 1) {
200ed062c8dSJulian Elischer 		thread_exit();
201ed062c8dSJulian Elischer 		/* NOTREACHED */
202ed062c8dSJulian Elischer 	}
20389bb1cefSJeff Roberson 	mtx_unlock_spin(&sched_lock);
204ed062c8dSJulian Elischer 	PROC_UNLOCK(p);
20589bb1cefSJeff Roberson 	return (0);
20689bb1cefSJeff Roberson }
20789bb1cefSJeff Roberson 
20889bb1cefSJeff Roberson int
20989bb1cefSJeff Roberson thr_kill(struct thread *td, struct thr_kill_args *uap)
210cd28f17dSMarcel Moolenaar     /* long id, int sig */
21189bb1cefSJeff Roberson {
21289bb1cefSJeff Roberson 	struct thread *ttd;
21389bb1cefSJeff Roberson 	struct proc *p;
21489bb1cefSJeff Roberson 	int error;
21589bb1cefSJeff Roberson 
21689bb1cefSJeff Roberson 	p = td->td_proc;
21789bb1cefSJeff Roberson 	error = 0;
21889bb1cefSJeff Roberson 	PROC_LOCK(p);
21971cfaac0SMike Makonnen 	FOREACH_THREAD_IN_PROC(p, ttd) {
220cd28f17dSMarcel Moolenaar 		if (ttd->td_tid == uap->id)
22189bb1cefSJeff Roberson 			break;
22271cfaac0SMike Makonnen 	}
22389bb1cefSJeff Roberson 	if (ttd == NULL) {
22489bb1cefSJeff Roberson 		error = ESRCH;
22589bb1cefSJeff Roberson 		goto out;
22689bb1cefSJeff Roberson 	}
22789bb1cefSJeff Roberson 	if (uap->sig == 0)
22889bb1cefSJeff Roberson 		goto out;
22989bb1cefSJeff Roberson 	if (!_SIG_VALID(uap->sig)) {
23089bb1cefSJeff Roberson 		error = EINVAL;
23189bb1cefSJeff Roberson 		goto out;
23289bb1cefSJeff Roberson 	}
233c197abc4SMike Makonnen 	tdsignal(ttd, uap->sig, SIGTARGET_TD);
23489bb1cefSJeff Roberson out:
23589bb1cefSJeff Roberson 	PROC_UNLOCK(p);
23689bb1cefSJeff Roberson 	return (error);
23789bb1cefSJeff Roberson }
2381713a516SMike Makonnen 
2391713a516SMike Makonnen int
2401713a516SMike Makonnen thr_suspend(struct thread *td, struct thr_suspend_args *uap)
2411713a516SMike Makonnen 	/* const struct timespec *timeout */
2421713a516SMike Makonnen {
2431713a516SMike Makonnen 	struct timespec ts;
2441713a516SMike Makonnen 	struct timeval	tv;
2451713a516SMike Makonnen 	int error;
2461713a516SMike Makonnen 	int hz;
2471713a516SMike Makonnen 
2481713a516SMike Makonnen 	hz = 0;
2491713a516SMike Makonnen 	error = 0;
2501713a516SMike Makonnen 	if (uap->timeout != NULL) {
2511713a516SMike Makonnen 		error = copyin((const void *)uap->timeout, (void *)&ts,
2521713a516SMike Makonnen 		    sizeof(struct timespec));
2531713a516SMike Makonnen 		if (error != 0)
2541713a516SMike Makonnen 			return (error);
2551713a516SMike Makonnen 		if (ts.tv_nsec < 0 || ts.tv_nsec > 1000000000)
2561713a516SMike Makonnen 			return (EINVAL);
2571713a516SMike Makonnen 		if (ts.tv_sec == 0 && ts.tv_nsec == 0)
2581713a516SMike Makonnen 			return (ETIMEDOUT);
2591713a516SMike Makonnen 		TIMESPEC_TO_TIMEVAL(&tv, &ts);
2601713a516SMike Makonnen 		hz = tvtohz(&tv);
2611713a516SMike Makonnen 	}
2621713a516SMike Makonnen 	PROC_LOCK(td->td_proc);
263c21e3b38SMike Makonnen 	if ((td->td_flags & TDF_THRWAKEUP) == 0)
2641713a516SMike Makonnen 		error = msleep((void *)td, &td->td_proc->p_mtx,
2651713a516SMike Makonnen 		    td->td_priority | PCATCH, "lthr", hz);
2661713a516SMike Makonnen 	mtx_lock_spin(&sched_lock);
2671713a516SMike Makonnen 	td->td_flags &= ~TDF_THRWAKEUP;
2681713a516SMike Makonnen 	mtx_unlock_spin(&sched_lock);
2691713a516SMike Makonnen 	PROC_UNLOCK(td->td_proc);
2701713a516SMike Makonnen 	return (error == EWOULDBLOCK ? ETIMEDOUT : error);
2711713a516SMike Makonnen }
2721713a516SMike Makonnen 
2731713a516SMike Makonnen int
2741713a516SMike Makonnen thr_wake(struct thread *td, struct thr_wake_args *uap)
275cd28f17dSMarcel Moolenaar 	/* long id */
2761713a516SMike Makonnen {
277cd28f17dSMarcel Moolenaar 	struct thread *ttd;
2781713a516SMike Makonnen 
279b9fb5d42SMike Makonnen 	PROC_LOCK(td->td_proc);
280b9fb5d42SMike Makonnen 	FOREACH_THREAD_IN_PROC(td->td_proc, ttd) {
281cd28f17dSMarcel Moolenaar 		if (ttd->td_tid == uap->id)
2821713a516SMike Makonnen 			break;
2831713a516SMike Makonnen 	}
2841713a516SMike Makonnen 	if (ttd == NULL) {
285b9fb5d42SMike Makonnen 		PROC_UNLOCK(td->td_proc);
2861713a516SMike Makonnen 		return (ESRCH);
2871713a516SMike Makonnen 	}
2881713a516SMike Makonnen 	mtx_lock_spin(&sched_lock);
289cd28f17dSMarcel Moolenaar 	ttd->td_flags |= TDF_THRWAKEUP;
2901713a516SMike Makonnen 	mtx_unlock_spin(&sched_lock);
291cd28f17dSMarcel Moolenaar 	wakeup_one((void *)ttd);
292b9fb5d42SMike Makonnen 	PROC_UNLOCK(td->td_proc);
2931713a516SMike Makonnen 	return (0);
2941713a516SMike Makonnen }
295