class.c revision 2712:f74a135872bc
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/types.h>
29#include <sys/systm.h>
30#include <sys/cmn_err.h>
31#include <sys/class.h>
32#include <sys/kmem.h>
33#include <sys/cred.h>
34#include <sys/proc.h>
35#include <sys/procset.h>
36#include <sys/modctl.h>
37#include <sys/disp.h>
38#include <sys/sysmacros.h>
39
40static int getcidbyname_locked(char *, id_t *);
41
42/*
43 * Allocate a cid given a class name if one is not already allocated.
44 * Returns 0 if the cid was already exists or if the allocation of a new
45 * cid was successful. Nonzero return indicates error.
46 */
47int
48alloc_cid(char *clname, id_t *cidp)
49{
50	sclass_t *clp;
51
52	ASSERT(MUTEX_HELD(&class_lock));
53
54	/*
55	 * If the clname doesn't already have a cid, allocate one.
56	 */
57	if (getcidbyname_locked(clname, cidp) != 0) {
58		/*
59		 * Allocate a class entry and a lock for it.
60		 */
61		for (clp = sclass; clp < &sclass[nclass]; clp++)
62			if (clp->cl_name[0] == '\0' && clp->cl_lock == NULL)
63				break;
64
65		if (clp == &sclass[nclass]) {
66			return (ENOSPC);
67		}
68		*cidp = clp - &sclass[0];
69		clp->cl_lock = kmem_alloc(sizeof (krwlock_t), KM_SLEEP);
70		clp->cl_name = kmem_alloc(strlen(clname) + 1, KM_SLEEP);
71		(void) strcpy(clp->cl_name, clname);
72		rw_init(clp->cl_lock, NULL, RW_DEFAULT, NULL);
73	}
74
75	/*
76	 * At this point, *cidp will contain the index into the class
77	 * array for the given class name.
78	 */
79	return (0);
80}
81
82int
83scheduler_load(char *clname, sclass_t *clp)
84{
85	int rv = 0;
86
87	if (LOADABLE_SCHED(clp)) {
88		rw_enter(clp->cl_lock, RW_READER);
89		if (!SCHED_INSTALLED(clp)) {
90			rw_exit(clp->cl_lock);
91			if (modload("sched", clname) == -1)
92				return (EINVAL);
93			rw_enter(clp->cl_lock, RW_READER);
94			/* did we really load a scheduling class? */
95			if (!SCHED_INSTALLED(clp))
96				rv = EINVAL;
97		}
98		rw_exit(clp->cl_lock);
99	}
100	return (rv);
101}
102
103/*
104 * Get class ID given class name.
105 */
106int
107getcid(char *clname, id_t *cidp)
108{
109	sclass_t *clp;
110	int retval;
111
112	mutex_enter(&class_lock);
113	if ((retval = alloc_cid(clname, cidp)) == 0) {
114		clp = &sclass[*cidp];
115		clp->cl_count++;
116
117		/*
118		 * If it returns zero, it's loaded & locked
119		 * or we found a statically installed scheduler
120		 * module.
121		 * If it returns EINVAL, modload() failed when
122		 * it tried to load the module.
123		 */
124		mutex_exit(&class_lock);
125		retval = scheduler_load(clname, clp);
126		mutex_enter(&class_lock);
127
128		clp->cl_count--;
129		if (retval != 0 && clp->cl_count == 0) {
130			/* last guy out of scheduler_load frees the storage */
131			kmem_free(clp->cl_name, strlen(clname) + 1);
132			kmem_free(clp->cl_lock, sizeof (krwlock_t));
133			clp->cl_name = "";
134			clp->cl_lock = (krwlock_t *)NULL;
135		}
136	}
137	mutex_exit(&class_lock);
138	return (retval);
139
140}
141
142static int
143getcidbyname_locked(char *clname, id_t *cidp)
144{
145	sclass_t *clp;
146
147	ASSERT(MUTEX_HELD(&class_lock));
148
149	if (*clname == NULL)
150		return (EINVAL);
151
152	for (clp = &sclass[0]; clp < &sclass[nclass]; clp++) {
153		if (strcmp(clp->cl_name, clname) == 0) {
154			*cidp = clp - &sclass[0];
155			return (0);
156		}
157	}
158	return (EINVAL);
159}
160
161/*
162 * Lookup a module by name.
163 */
164int
165getcidbyname(char *clname, id_t *cidp)
166{
167	int retval;
168
169	mutex_enter(&class_lock);
170	retval = getcidbyname_locked(clname, cidp);
171	mutex_exit(&class_lock);
172
173	return (retval);
174}
175
176/*
177 * Get the scheduling parameters of the thread pointed to by
178 * tp into the buffer pointed to by parmsp.
179 */
180void
181parmsget(kthread_id_t tp, pcparms_t *parmsp)
182{
183	parmsp->pc_cid = tp->t_cid;
184	CL_PARMSGET(tp, parmsp->pc_clparms);
185}
186
187
188/*
189 * Check the validity of the scheduling parameters in the buffer
190 * pointed to by parmsp.
191 * Note that the format of the parameters may be changed by class
192 * specific code which we call.
193 */
194int
195parmsin(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
196{
197	if (parmsp->pc_cid >= loaded_classes || parmsp->pc_cid < 1)
198		return (EINVAL);
199
200	/*
201	 * Call the class specific routine to validate class
202	 * specific parameters.
203	 * The input parameters are either in a pcparms structure (PC_SETPARMS)
204	 * or in a variable parameter structure (PC_SETXPARMS). In the
205	 * 'PC_SETPARMS' case vaparmsp is a NULL pointer and a CL_PARMSIN()
206	 * routine gets the parameter. Otherwise vaparmsp points to a variable
207	 * parameter structure and a CL_VAPARMSIN() routine gets the parameter.
208	 */
209	if (vaparmsp != NULL)
210		return (CL_VAPARMSIN(&sclass[parmsp->pc_cid],
211		    parmsp->pc_clparms, vaparmsp));
212	else
213		return (CL_PARMSIN(&sclass[parmsp->pc_cid],
214		    parmsp->pc_clparms));
215}
216
217
218/*
219 * Call the class specific code to do the required processing
220 * before the scheduling parameters are copied out to the user.
221 * Note that the format of the parameters may be changed by the
222 * class specific code.
223 */
224int
225parmsout(pcparms_t *parmsp, pc_vaparms_t *vaparmsp)
226{
227	return (CL_PARMSOUT(&sclass[parmsp->pc_cid], parmsp->pc_clparms,
228		vaparmsp));
229}
230
231
232/*
233 * Set the scheduling parameters of the thread pointed to by
234 * targtp to those specified in the pcparms structure pointed
235 * to by parmsp.  If reqtp is non-NULL it points to the thread
236 * that initiated the request for the parameter change and indicates
237 * that our caller wants us to verify that the requesting thread
238 * has the appropriate permissions.
239 */
240int
241parmsset(pcparms_t *parmsp, kthread_id_t targtp)
242{
243	caddr_t	clprocp;
244	int	error;
245	cred_t	*reqpcredp;
246	proc_t	*reqpp = ttoproc(curthread);
247	proc_t	*targpp = ttoproc(targtp);
248	id_t	oldcid;
249
250	ASSERT(MUTEX_HELD(&pidlock));
251	ASSERT(MUTEX_HELD(&targpp->p_lock));
252	if (reqpp != NULL) {
253		mutex_enter(&reqpp->p_crlock);
254		crhold(reqpcredp = reqpp->p_cred);
255		mutex_exit(&reqpp->p_crlock);
256
257		/*
258		 * Check basic permissions.
259		 */
260		if (!prochasprocperm(targpp, reqpp, reqpcredp)) {
261			crfree(reqpcredp);
262			return (EPERM);
263		}
264	} else {
265		reqpcredp = NULL;
266	}
267
268	if (parmsp->pc_cid != targtp->t_cid) {
269		void	*bufp = NULL;
270		/*
271		 * Target thread must change to new class.
272		 */
273		clprocp = (caddr_t)targtp->t_cldata;
274		oldcid  = targtp->t_cid;
275
276		/*
277		 * Purpose: allow scheduling class to veto moves
278		 * to other classes. All the classes, except FSS,
279		 * do nothing except returning 0.
280		 */
281		error = CL_CANEXIT(targtp, reqpcredp);
282		if (error) {
283			/*
284			 * Not allowed to leave the class, so return error.
285			 */
286			crfree(reqpcredp);
287			return (error);
288		} else {
289			/*
290			 * Pre-allocate scheduling class data.
291			 */
292			if (CL_ALLOC(&bufp, parmsp->pc_cid, KM_NOSLEEP) != 0) {
293				error = ENOMEM; /* no memory available */
294				crfree(reqpcredp);
295				return (error);
296			} else {
297				error = CL_ENTERCLASS(targtp, parmsp->pc_cid,
298				    parmsp->pc_clparms, reqpcredp, bufp);
299				crfree(reqpcredp);
300				if (error) {
301					CL_FREE(parmsp->pc_cid, bufp);
302					return (error);
303				}
304			}
305		}
306		CL_EXITCLASS(oldcid, clprocp);
307	} else {
308
309		/*
310		 * Not changing class
311		 */
312		error = CL_PARMSSET(targtp, parmsp->pc_clparms,
313					curthread->t_cid, reqpcredp);
314		crfree(reqpcredp);
315		if (error)
316			return (error);
317	}
318	return (0);
319}
320
321
322/*
323 * Copy all selected class parameters to the user.
324 * The parameters are specified by a key.
325 */
326int
327vaparmsout(char *classp, pcparms_t *prmsp, pc_vaparms_t *vaparmsp,
328    uio_seg_t seg)
329{
330	char	*clname;
331
332	ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
333
334	if (classp != NULL)
335		return (CL_VAPARMSOUT(&sclass[prmsp->pc_cid],
336		    prmsp->pc_clparms, vaparmsp));
337
338	switch (vaparmsp->pc_vaparmscnt) {
339	case 0:
340		return (0);
341	case 1:
342		break;
343	default:
344		return (EINVAL);
345	}
346
347	if (vaparmsp->pc_parms[0].pc_key != PC_KY_CLNAME)
348		return (EINVAL);
349
350	clname = sclass[prmsp->pc_cid].cl_name;
351	if ((seg == UIO_USERSPACE ? copyout : kcopy)(clname,
352	    (void *)(uintptr_t)vaparmsp->pc_parms[0].pc_parm,
353	    MIN(strlen(clname) + 1, PC_CLNMSZ)))
354		return (EFAULT);
355
356	return (0);
357}
358