xref: /dragonfly/sys/kern/kern_usched.c (revision 7bc7e232)
1 /*
2  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sergey Glushchenko <deen@smz.com.ua>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sys/kern/kern_usched.c,v 1.9 2007/07/02 17:06:55 dillon Exp $
35  */
36 
37 #include <sys/errno.h>
38 #include <sys/globaldata.h>		/* curthread */
39 #include <sys/proc.h>
40 #include <sys/sysproto.h>		/* struct usched_set_args */
41 #include <sys/systm.h>			/* strcmp() */
42 #include <sys/usched.h>
43 #include <machine/smp.h>
44 
45 static TAILQ_HEAD(, usched) usched_list = TAILQ_HEAD_INITIALIZER(usched_list);
46 
47 cpumask_t usched_mastermask = -1;
48 
49 /*
50  * Called from very low level boot code, i386/i386/machdep.c/init386().
51  * We cannot do anything fancy.  no malloc's, no nothing other then
52  * static initialization.
53  */
54 struct usched *
55 usched_init(void)
56 {
57 	const char *defsched;
58 
59 	defsched = kgetenv("kern.user_scheduler");
60 
61 	/*
62 	 * Add various userland schedulers to the system.
63 	 */
64 	usched_ctl(&usched_bsd4, USCH_ADD);
65 	usched_ctl(&usched_dummy, USCH_ADD);
66 	if (defsched == NULL )
67 		return(&usched_bsd4);
68 	if (strcmp(defsched, "bsd4") == 0)
69 		return(&usched_bsd4);
70 	kprintf("WARNING: Running dummy userland scheduler\n");
71 	return(&usched_dummy);
72 }
73 
74 /*
75  * USCHED_CTL
76  *
77  * SYNOPSIS:
78  * 	Add/remove usched to/from list.
79  *
80  * ARGUMENTS:
81  * 	usched - pointer to target scheduler
82  * 	action - addition or removal ?
83  *
84  * RETURN VALUES:
85  * 	0 - success
86  * 	EINVAL - error
87  */
88 int
89 usched_ctl(struct usched *usched, int action)
90 {
91 	struct usched *item;	/* temporaly for TAILQ processing */
92 	int error = 0;
93 
94 	switch(action) {
95 	case USCH_ADD:
96 		/*
97 		 * Make sure it isn't already on the list
98 		 */
99 #ifdef INVARIANTS
100 		TAILQ_FOREACH(item, &usched_list, entry) {
101 			KKASSERT(item != usched);
102 		}
103 #endif
104 		/*
105 		 * Optional callback to the scheduler before we officially
106 		 * add it to the list.
107 		 */
108 		if (usched->usched_register)
109 			usched->usched_register();
110 		TAILQ_INSERT_TAIL(&usched_list, usched, entry);
111 		break;
112 	case USCH_REM:
113 		/*
114 		 * Do not allow the default scheduler to be removed
115 		 */
116 		if (strcmp(usched->name, "bsd4") == 0) {
117 			error = EINVAL;
118 			break;
119 		}
120 		TAILQ_FOREACH(item, &usched_list, entry) {
121 			if (item == usched)
122 				break;
123 		}
124 		if (item) {
125 			if (item->usched_unregister)
126 				item->usched_unregister();
127 			TAILQ_REMOVE(&usched_list, item, entry);
128 		} else {
129 			error = EINVAL;
130 		}
131 		break;
132 	default:
133 		error = EINVAL;
134 		break;
135 	}
136 	return (error);
137 }
138 
139 /*
140  * USCHED_SET(syscall)
141  *
142  * SYNOPSIS:
143  * 	Setting up a proc's usched.
144  *
145  * ARGUMENTS:
146  *	pid	-
147  *	cmd	-
148  * 	data	-
149  *	bytes	-
150  * RETURN VALUES:
151  * 	0 - success
152  * 	EINVAL - error
153  */
154 int
155 sys_usched_set(struct usched_set_args *uap)
156 {
157 	struct proc *p = curthread->td_proc;
158 	struct usched *item;	/* temporaly for TAILQ processing */
159 	int error;
160 	char buffer[NAME_LENGTH];
161 	cpumask_t mask;
162 	struct lwp *lp;
163 	int cpuid;
164 
165 	if ((error = suser(curthread)) != 0)
166 		return (error);
167 
168 	if (uap->pid != 0 && uap->pid != curthread->td_proc->p_pid)
169 		return (EINVAL);
170 
171 	lp = curthread->td_lwp;
172 	switch (uap->cmd) {
173 	case USCHED_SET_SCHEDULER:
174 		if ((error = copyinstr(uap->data, buffer, sizeof(buffer),
175 			NULL)) != 0)
176 			return (error);
177 		TAILQ_FOREACH(item, &usched_list, entry) {
178 			if ((strcmp(item->name, buffer) == 0))
179 				break;
180 		}
181 
182 		/*
183 		 * If the scheduler for a process is being changed, disassociate
184 		 * the old scheduler before switching to the new one.
185 		 *
186 		 * XXX we might have to add an additional ABI call to do a 'full
187 		 * disassociation' and another ABI call to do a 'full
188 		 * reassociation'
189 		 */
190 		/* XXX lwp have to deal with multiple lwps here */
191 		if (p->p_nthreads != 1)
192 			return (EINVAL);
193 		if (item && item != p->p_usched) {
194 			/* XXX lwp */
195 			p->p_usched->release_curproc(ONLY_LWP_IN_PROC(p));
196 			p->p_usched = item;
197 		} else if (item == NULL) {
198 			error = EINVAL;
199 		}
200 		break;
201 	case USCHED_SET_CPU:
202 		if (uap->bytes != sizeof(int))
203 			return (EINVAL);
204 		error = copyin(uap->data, &cpuid, sizeof(int));
205 		if (error)
206 			break;
207 		if (cpuid < 0 || cpuid >= ncpus) {
208 			error = EFBIG;
209 			break;
210 		}
211 		if ((smp_active_mask & (1 << cpuid)) == 0) {
212 			error = EINVAL;
213 			break;
214 		}
215 		lp->lwp_cpumask = 1 << cpuid;
216 		if (cpuid != mycpu->gd_cpuid)
217 			lwkt_migratecpu(cpuid);
218 		break;
219 	case USCHED_ADD_CPU:
220 		if (uap->bytes != sizeof(int))
221 			return (EINVAL);
222 		error = copyin(uap->data, &cpuid, sizeof(int));
223 		if (error)
224 			break;
225 		if (cpuid < 0 || cpuid >= ncpus) {
226 			error = EFBIG;
227 			break;
228 		}
229 		if (!(smp_active_mask & (1 << cpuid))) {
230 			error = EINVAL;
231 			break;
232 		}
233 		lp->lwp_cpumask |= 1 << cpuid;
234 		break;
235 	case USCHED_DEL_CPU:
236 		if (uap->bytes != sizeof(int))
237 			return (EINVAL);
238 		error = copyin(uap->data, &cpuid, sizeof(int));
239 		if (error)
240 			break;
241 		if (cpuid < 0 || cpuid >= ncpus) {
242 			error = EFBIG;
243 			break;
244 		}
245 		lp = curthread->td_lwp;
246 		mask = lp->lwp_cpumask & smp_active_mask & ~(1 << cpuid);
247 		if (mask == 0)
248 			error = EPERM;
249 		else {
250 			lp->lwp_cpumask &= ~(1 << cpuid);
251 			if ((lp->lwp_cpumask & mycpu->gd_cpumask) == 0) {
252 				cpuid = bsfl(lp->lwp_cpumask & smp_active_mask);
253 				lwkt_migratecpu(cpuid);
254 			}
255 		}
256 	default:
257 		error = EINVAL;
258 		break;
259 	}
260 	return (error);
261 }
262 
263