xref: /illumos-gate/usr/src/uts/common/disp/priocntl.c (revision 48bbca816818409505a6e214d0911fda44e622e3)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/sysmacros.h>
34 #include <sys/signal.h>
35 #include <sys/pcb.h>
36 #include <sys/user.h>
37 #include <sys/systm.h>
38 #include <sys/sysinfo.h>
39 #include <sys/var.h>
40 #include <sys/errno.h>
41 #include <sys/cred.h>
42 #include <sys/proc.h>
43 #include <sys/procset.h>
44 #include <sys/debug.h>
45 #include <sys/inline.h>
46 #include <sys/priocntl.h>
47 #include <sys/disp.h>
48 #include <sys/class.h>
49 #include <sys/modctl.h>
50 #include <sys/t_lock.h>
51 #include <sys/uadmin.h>
52 #include <sys/cmn_err.h>
53 #include <sys/policy.h>
54 #include <sys/schedctl.h>
55 
56 /*
57  * Structure used to pass arguments to the proccmp() function.
58  * The arguments must be passed in a structure because proccmp()
59  * is called indirectly through the dotoprocs() function which
60  * will only pass through a single one word argument.
61  */
62 struct pcmpargs {
63 	id_t	*pcmp_cidp;
64 	int	*pcmp_cntp;
65 	kthread_t **pcmp_retthreadp;
66 };
67 
68 /*
69  * Structure used to pass arguments to the setparms() function
70  * which is called indirectly through dotoprocs().
71  */
72 struct stprmargs {
73 	struct pcparms	*stp_parmsp;	/* pointer to parameters */
74 	int		stp_error;	/* some errors returned here */
75 };
76 
77 #if defined(_SYSCALL32_IMPL) && _LONG_LONG_ALIGNMENT_32 == 4
78 /*
79  * A vaparm_t is an int followed by a long long -- this packs differently
80  * between the 64-bit kernel ABI and the 32-bit user ABI.
81  */
82 static int
copyin_vaparms32(caddr_t arg,pc_vaparms_t * vap,uio_seg_t seg)83 copyin_vaparms32(caddr_t arg, pc_vaparms_t *vap, uio_seg_t seg)
84 {
85 	pc_vaparms32_t vaparms32;
86 	pc_vaparm32_t *src;
87 	pc_vaparm_t *dst;
88 	uint_t cnt;
89 
90 	ASSERT(get_udatamodel() == DATAMODEL_ILP32);
91 
92 	if ((seg == UIO_USERSPACE ? copyin : kcopy)(arg, &vaparms32,
93 	    sizeof (vaparms32)))
94 		return (EFAULT);
95 
96 	vap->pc_vaparmscnt = vaparms32.pc_vaparmscnt;
97 	if ((cnt = vaparms32.pc_vaparmscnt) > PC_VAPARMCNT)
98 		cnt = PC_VAPARMCNT;
99 	for (src = vaparms32.pc_parms, dst = vap->pc_parms;
100 	    cnt--; src++, dst++) {
101 		dst->pc_key = src->pc_key;
102 		dst->pc_parm = src->pc_parm;
103 	}
104 	return (0);
105 }
106 
107 #define	COPYIN_VAPARMS(arg, vap, size, seg)	\
108 	(get_udatamodel() == DATAMODEL_NATIVE ?	\
109 	(*copyinfn)(arg, vap, size) : copyin_vaparms32(arg, vap, seg))
110 
111 #else
112 
113 #define	COPYIN_VAPARMS(arg, vap, size, seg)	(*copyinfn)(arg, vap, size)
114 
115 #endif
116 
117 static int donice(procset_t *, pcnice_t *);
118 static int doprio(procset_t *, pcprio_t *);
119 static int proccmp(proc_t *, struct pcmpargs *);
120 static int setparms(proc_t *, struct stprmargs *);
121 extern int threadcmp(struct pcmpargs *, kthread_t *);
122 
123 /*
124  * The priocntl system call.
125  */
126 long
priocntl_common(int pc_version,procset_t * psp,int cmd,caddr_t arg,caddr_t arg2,uio_seg_t seg)127 priocntl_common(int pc_version, procset_t *psp, int cmd, caddr_t arg,
128     caddr_t arg2, uio_seg_t seg)
129 {
130 	pcinfo_t		pcinfo;
131 	pcparms_t		pcparms;
132 	pcnice_t		pcnice;
133 	pcprio_t		pcprio;
134 	pcadmin_t		pcadmin;
135 	pcpri_t			pcpri;
136 	procset_t		procset;
137 	struct stprmargs	stprmargs;
138 	struct pcmpargs		pcmpargs;
139 	pc_vaparms_t		vaparms;
140 	char			clname[PC_CLNMSZ];
141 	char			*outstr;
142 	int			count;
143 	kthread_t		*retthreadp;
144 	proc_t			*initpp;
145 	int			clnullflag;
146 	int			error = 0;
147 	int			error1 = 0;
148 	int			rv = 0;
149 	pid_t			saved_pid;
150 	id_t			classid;
151 	int			size;
152 	int (*copyinfn)(const void *, void *, size_t);
153 	int (*copyoutfn)(const void *, void *, size_t);
154 
155 	/*
156 	 * First just check the version number. Right now there is only
157 	 * one version we know about and support.  If we get some other
158 	 * version number from the application it may be that the
159 	 * application was built with some future version and is trying
160 	 * to run on an old release of the system (that's us).  In any
161 	 * case if we don't recognize the version number all we can do is
162 	 * return error.
163 	 */
164 	if (pc_version != PC_VERSION)
165 		return (set_errno(EINVAL));
166 
167 	if (seg == UIO_USERSPACE) {
168 		copyinfn = copyin;
169 		copyoutfn = copyout;
170 	} else {
171 		copyinfn = kcopy;
172 		copyoutfn = kcopy;
173 	}
174 
175 	switch (cmd) {
176 	case PC_GETCID:
177 		/*
178 		 * If the arg pointer is NULL, the user just wants to
179 		 * know the number of classes. If non-NULL, the pointer
180 		 * should point to a valid user pcinfo buffer.  In the
181 		 * dynamic world we need to return the number of loaded
182 		 * classes, not the max number of available classes that
183 		 * can be loaded.
184 		 */
185 		if (arg == NULL) {
186 			rv = loaded_classes;
187 			break;
188 		} else {
189 			if ((*copyinfn)(arg, &pcinfo, sizeof (pcinfo)))
190 				return (set_errno(EFAULT));
191 		}
192 
193 		pcinfo.pc_clname[PC_CLNMSZ-1] = '\0';
194 
195 		/*
196 		 * Get the class ID corresponding to user supplied name.
197 		 */
198 		error = getcid(pcinfo.pc_clname, &pcinfo.pc_cid);
199 		if (error)
200 			return (set_errno(error));
201 
202 		/*
203 		 * Can't get info about the sys class.
204 		 */
205 		if (pcinfo.pc_cid == 0)
206 			return (set_errno(EINVAL));
207 
208 		/*
209 		 * Get the class specific information.
210 		 * we MUST make sure that the class has not already
211 		 * been unloaded before we try the CL_GETCLINFO.
212 		 * If it has then we need to load it.
213 		 */
214 		error =
215 		    scheduler_load(pcinfo.pc_clname, &sclass[pcinfo.pc_cid]);
216 		if (error)
217 			return (set_errno(error));
218 		error = CL_GETCLINFO(&sclass[pcinfo.pc_cid], pcinfo.pc_clinfo);
219 		if (error)
220 			return (set_errno(error));
221 
222 		if ((*copyoutfn)(&pcinfo, arg, sizeof (pcinfo)))
223 			return (set_errno(EFAULT));
224 
225 		rv = loaded_classes;
226 
227 		break;
228 
229 	case PC_GETCLINFO:
230 		/*
231 		 * If the arg pointer is NULL, the user just wants to know
232 		 * the number of classes. If non-NULL, the pointer should
233 		 * point to a valid user pcinfo buffer.
234 		 */
235 		if (arg == NULL) {
236 			rv = loaded_classes;
237 			break;
238 		} else {
239 			if ((*copyinfn)(arg, &pcinfo, sizeof (pcinfo)))
240 				return (set_errno(EFAULT));
241 		}
242 
243 		if (pcinfo.pc_cid >= loaded_classes || pcinfo.pc_cid < 1)
244 			return (set_errno(EINVAL));
245 
246 		(void) strncpy(pcinfo.pc_clname, sclass[pcinfo.pc_cid].cl_name,
247 		    PC_CLNMSZ);
248 
249 		/*
250 		 * Get the class specific information.  we MUST make sure
251 		 * that the class has not already been unloaded before we
252 		 * try the CL_GETCLINFO.  If it has then we need to load
253 		 * it.
254 		 */
255 		error =
256 		    scheduler_load(pcinfo.pc_clname, &sclass[pcinfo.pc_cid]);
257 		if (error)
258 			return (set_errno(error));
259 		error = CL_GETCLINFO(&sclass[pcinfo.pc_cid], pcinfo.pc_clinfo);
260 		if (error)
261 			return (set_errno(error));
262 
263 		if ((*copyoutfn)(&pcinfo, arg, sizeof (pcinfo)))
264 			return (set_errno(EFAULT));
265 
266 		rv = loaded_classes;
267 		break;
268 
269 	case PC_SETPARMS:
270 	case PC_SETXPARMS:
271 		/*
272 		 * First check the validity of the parameters we got from
273 		 * the user.  We don't do any permissions checking here
274 		 * because it's done on a per thread basis by parmsset().
275 		 */
276 		if (cmd == PC_SETPARMS) {
277 			if ((*copyinfn)(arg, &pcparms, sizeof (pcparms)))
278 				return (set_errno(EFAULT));
279 
280 			error = parmsin(&pcparms, NULL);
281 		} else {
282 			if ((*copyinfn)(arg, clname, PC_CLNMSZ) ||
283 			    COPYIN_VAPARMS(arg2, &vaparms, sizeof (vaparms),
284 			    seg))
285 				return (set_errno(EFAULT));
286 			clname[PC_CLNMSZ-1] = '\0';
287 
288 			if (getcid(clname, &pcparms.pc_cid))
289 				return (set_errno(EINVAL));
290 
291 			error = parmsin(&pcparms, &vaparms);
292 		}
293 
294 		if (error)
295 			return (set_errno(error));
296 
297 		/*
298 		 * Get the procset from the user.
299 		 */
300 		if ((*copyinfn)(psp, &procset, sizeof (procset)))
301 			return (set_errno(EFAULT));
302 
303 		/*
304 		 * For performance we do a quick check here to catch
305 		 * common cases where the current thread is the only one
306 		 * in the set.  In such cases we can call parmsset()
307 		 * directly, avoiding the relatively lengthy path through
308 		 * dotoprocs().  The underlying classes expect pidlock to
309 		 * be held.
310 		 */
311 		if (cur_inset_only(&procset) == B_TRUE) {
312 			/* do a single LWP */
313 			if ((procset.p_lidtype == P_LWPID) ||
314 			    (procset.p_ridtype == P_LWPID)) {
315 				mutex_enter(&pidlock);
316 				mutex_enter(&curproc->p_lock);
317 				error = parmsset(&pcparms, curthread);
318 				mutex_exit(&curproc->p_lock);
319 				mutex_exit(&pidlock);
320 			} else {
321 				/* do the entire process otherwise */
322 				stprmargs.stp_parmsp = &pcparms;
323 				stprmargs.stp_error = 0;
324 				mutex_enter(&pidlock);
325 				error = setparms(curproc, &stprmargs);
326 				mutex_exit(&pidlock);
327 				if (error == 0 && stprmargs.stp_error != 0)
328 					error = stprmargs.stp_error;
329 			}
330 			if (error)
331 				return (set_errno(error));
332 		} else {
333 			stprmargs.stp_parmsp = &pcparms;
334 			stprmargs.stp_error = 0;
335 
336 			error1 = error = ESRCH;
337 
338 			/*
339 			 * The dotoprocs() call below will cause
340 			 * setparms() to be called for each thread in the
341 			 * specified procset. setparms() will in turn
342 			 * call parmsset() (which does the real work).
343 			 */
344 			if ((procset.p_lidtype != P_LWPID) ||
345 			    (procset.p_ridtype != P_LWPID)) {
346 				error1 = dotoprocs(&procset, setparms,
347 				    (char *)&stprmargs);
348 			}
349 
350 			/*
351 			 * take care of the case when any of the
352 			 * operands happen to be LWP's
353 			 */
354 
355 			if ((procset.p_lidtype == P_LWPID) ||
356 			    (procset.p_ridtype == P_LWPID)) {
357 				error = dotolwp(&procset, parmsset,
358 				    (char *)&pcparms);
359 				/*
360 				 * Dotolwp() returns with p_lock held.
361 				 * This is required for the GETPARMS case
362 				 * below. So, here we just release the
363 				 * p_lock.
364 				 */
365 				if (MUTEX_HELD(&curproc->p_lock))
366 					mutex_exit(&curproc->p_lock);
367 			}
368 
369 			/*
370 			 * If setparms() encounters a permissions error
371 			 * for one or more of the threads it returns
372 			 * EPERM in stp_error so dotoprocs() will
373 			 * continue through the thread set.  If
374 			 * dotoprocs() returned an error above, it was
375 			 * more serious than permissions and dotoprocs
376 			 * quit when the error was encountered.  We
377 			 * return the more serious error if there was
378 			 * one, otherwise we return EPERM if we got that
379 			 * back.
380 			 */
381 			if (error1 != ESRCH)
382 				error = error1;
383 			if (error == 0 && stprmargs.stp_error != 0)
384 				error = stprmargs.stp_error;
385 		}
386 		break;
387 
388 	case PC_GETPARMS:
389 	case PC_GETXPARMS:
390 		if (cmd == PC_GETPARMS) {
391 			if ((*copyinfn)(arg, &pcparms, sizeof (pcparms)))
392 				return (set_errno(EFAULT));
393 		} else {
394 			if (arg != NULL) {
395 				if ((*copyinfn)(arg, clname, PC_CLNMSZ))
396 					return (set_errno(EFAULT));
397 
398 				clname[PC_CLNMSZ-1] = '\0';
399 
400 				if (getcid(clname, &pcparms.pc_cid))
401 					return (set_errno(EINVAL));
402 			} else
403 				pcparms.pc_cid = PC_CLNULL;
404 
405 			if (COPYIN_VAPARMS(arg2, &vaparms, sizeof (vaparms),
406 			    seg))
407 				return (set_errno(EFAULT));
408 		}
409 
410 		if (pcparms.pc_cid >= loaded_classes ||
411 		    (pcparms.pc_cid < 1 && pcparms.pc_cid != PC_CLNULL))
412 			return (set_errno(EINVAL));
413 
414 		if ((*copyinfn)(psp, &procset, sizeof (procset)))
415 			return (set_errno(EFAULT));
416 
417 		/*
418 		 * Check to see if the current thread is the only one
419 		 * in the set. If not we must go through the whole set
420 		 * to select a thread.
421 		 */
422 		if (cur_inset_only(&procset) == B_TRUE) {
423 			/* do a single LWP */
424 			if ((procset.p_lidtype == P_LWPID) ||
425 			    (procset.p_ridtype == P_LWPID)) {
426 				if (pcparms.pc_cid != PC_CLNULL &&
427 				    pcparms.pc_cid != curthread->t_cid) {
428 					/*
429 					 * Specified thread not in
430 					 * specified class.
431 					 */
432 					return (set_errno(ESRCH));
433 				} else {
434 					mutex_enter(&curproc->p_lock);
435 					retthreadp = curthread;
436 				}
437 			} else {
438 				count = 0;
439 				retthreadp = NULL;
440 				pcmpargs.pcmp_cidp = &pcparms.pc_cid;
441 				pcmpargs.pcmp_cntp = &count;
442 				pcmpargs.pcmp_retthreadp = &retthreadp;
443 				/*
444 				 * Specified thread not in specified class.
445 				 */
446 				if (pcparms.pc_cid != PC_CLNULL &&
447 				    pcparms.pc_cid != curthread->t_cid)
448 					return (set_errno(ESRCH));
449 				error = proccmp(curproc, &pcmpargs);
450 				if (error) {
451 					if (retthreadp != NULL)
452 						mutex_exit(&(curproc->p_lock));
453 					return (set_errno(error));
454 				}
455 			}
456 		} else {
457 			/*
458 			 * get initpp early to avoid lock ordering problems
459 			 * (we cannot get pidlock while holding any p_lock).
460 			 */
461 			mutex_enter(&pidlock);
462 			initpp = prfind(P_INITPID);
463 			mutex_exit(&pidlock);
464 
465 			/*
466 			 * Select the thread (from the set) whose
467 			 * parameters we are going to return.  First we
468 			 * set up some locations for return values, then
469 			 * we call proccmp() indirectly through
470 			 * dotoprocs().  proccmp() will call a class
471 			 * specific routine which actually does the
472 			 * selection.  To understand how this works take
473 			 * a careful look at the code below, the
474 			 * dotoprocs() function, the proccmp() function,
475 			 * and the class specific cl_proccmp() functions.
476 			 */
477 			if (pcparms.pc_cid == PC_CLNULL)
478 				clnullflag = 1;
479 			else
480 				clnullflag = 0;
481 			count = 0;
482 			retthreadp = NULL;
483 			pcmpargs.pcmp_cidp = &pcparms.pc_cid;
484 			pcmpargs.pcmp_cntp = &count;
485 			pcmpargs.pcmp_retthreadp = &retthreadp;
486 			error1 = error = ESRCH;
487 
488 			if ((procset.p_lidtype != P_LWPID) ||
489 			    (procset.p_ridtype != P_LWPID)) {
490 				error1 = dotoprocs(&procset, proccmp,
491 				    (char *)&pcmpargs);
492 			}
493 
494 			/*
495 			 * take care of combination of LWP and process
496 			 * set case in a procset
497 			 */
498 			if ((procset.p_lidtype == P_LWPID) ||
499 			    (procset.p_ridtype == P_LWPID)) {
500 				error = dotolwp(&procset, threadcmp,
501 				    (char *)&pcmpargs);
502 			}
503 
504 			/*
505 			 * Both proccmp() and threadcmp() return with the
506 			 * p_lock held for the ttoproc(retthreadp). This
507 			 * is required to make sure that the process we
508 			 * chose as the winner doesn't go away
509 			 * i.e. retthreadp has to be a valid pointer.
510 			 *
511 			 * The case below can only happen if the thread
512 			 * with the highest priority was not in your
513 			 * process.  In that case, dotolwp will return
514 			 * holding p_lock for both your process as well
515 			 * as the process in which retthreadp is a
516 			 * thread.
517 			 */
518 			if ((retthreadp != NULL) &&
519 			    (ttoproc(retthreadp) != curproc) &&
520 			    MUTEX_HELD(&(curproc)->p_lock))
521 				mutex_exit(&(curproc)->p_lock);
522 
523 			ASSERT(retthreadp == NULL ||
524 			    MUTEX_HELD(&(ttoproc(retthreadp)->p_lock)));
525 			if (error1 != ESRCH)
526 				error = error1;
527 			if (error) {
528 				if (retthreadp != NULL)
529 				    /* CSTYLED */
530 				    mutex_exit(&(ttoproc(retthreadp)->p_lock));
531 				ASSERT(MUTEX_NOT_HELD(&(curproc)->p_lock));
532 				return (set_errno(error));
533 			}
534 			/*
535 			 * dotoprocs() ignores the init process if it is
536 			 * in the set, unless it was the only process found.
537 			 * Since we are getting parameters here rather than
538 			 * setting them, we want to make sure init is not
539 			 * excluded if it is in the set.
540 			 */
541 			if (initpp != NULL && retthreadp != NULL &&
542 			    ttoproc(retthreadp) != initpp) {
543 				mutex_enter(&initpp->p_lock);
544 				if (procinset(initpp, &procset)) {
545 					mutex_exit(&initpp->p_lock);
546 					(void) proccmp(initpp, &pcmpargs);
547 				} else {
548 					mutex_exit(&initpp->p_lock);
549 				}
550 			}
551 
552 			/*
553 			 * If dotoprocs returned success it found at least
554 			 * one thread in the set.  If proccmp() failed to
555 			 * select a thread it is because the user specified
556 			 * a class and none of the threads in the set
557 			 * belonged to that class, or because the process
558 			 * specified was in the middle of exiting and had
559 			 * cleared its thread list.
560 			 */
561 			if (retthreadp == NULL) {
562 				/*
563 				 * Might be here and still holding p_lock
564 				 * if we did a dotolwp on an lwp that
565 				 * existed but was in the wrong class.
566 				 */
567 				if (MUTEX_HELD(&(curproc)->p_lock))
568 					mutex_exit(&(curproc)->p_lock);
569 				return (set_errno(ESRCH));
570 			}
571 
572 			/*
573 			 * User can only use PC_CLNULL with one thread in set.
574 			 */
575 			if (clnullflag && count > 1) {
576 				if (retthreadp != NULL)
577 					mutex_exit(
578 					    &(ttoproc(retthreadp)->p_lock));
579 				ASSERT(MUTEX_NOT_HELD(&(curproc)->p_lock));
580 				return (set_errno(EINVAL));
581 			}
582 		}
583 
584 		ASSERT(retthreadp == NULL ||
585 		    MUTEX_HELD(&(ttoproc(retthreadp)->p_lock)));
586 		/*
587 		 * It is possible to have retthreadp == NULL. Proccmp()
588 		 * in the rare case (p_tlist == NULL) could return without
589 		 * setting a value for retthreadp.
590 		 */
591 		if (retthreadp == NULL) {
592 			ASSERT(MUTEX_NOT_HELD(&(curproc)->p_lock));
593 			return (set_errno(ESRCH));
594 		}
595 		/*
596 		 * We've selected a thread so now get the parameters.
597 		 */
598 		parmsget(retthreadp, &pcparms);
599 
600 		/*
601 		 * Prepare to return parameters to the user
602 		 */
603 		error = parmsout(&pcparms,
604 		    (cmd == PC_GETPARMS ? NULL : &vaparms));
605 
606 		/*
607 		 * Save pid of selected thread before dropping p_lock.
608 		 */
609 		saved_pid = ttoproc(retthreadp)->p_pid;
610 		mutex_exit(&(ttoproc(retthreadp)->p_lock));
611 		ASSERT(MUTEX_NOT_HELD(&curproc->p_lock));
612 
613 		if (error)
614 			return (set_errno(error));
615 
616 		if (cmd == PC_GETPARMS) {
617 			if ((*copyoutfn)(&pcparms, arg, sizeof (pcparms)))
618 				return (set_errno(EFAULT));
619 		} else if ((error = vaparmsout(arg, &pcparms, &vaparms,
620 		    seg)) != 0)
621 			return (set_errno(error));
622 
623 		/*
624 		 * And finally, return the pid of the selected thread.
625 		 */
626 		rv = saved_pid;
627 		break;
628 
629 	case PC_ADMIN:
630 		if (get_udatamodel() == DATAMODEL_NATIVE) {
631 			if ((*copyinfn)(arg, &pcadmin, sizeof (pcadmin_t)))
632 				return (set_errno(EFAULT));
633 #ifdef _SYSCALL32_IMPL
634 		} else {
635 			/* pcadmin struct from ILP32 callers */
636 			pcadmin32_t pcadmin32;
637 
638 			if ((*copyinfn)(arg, &pcadmin32, sizeof (pcadmin32_t)))
639 				return (set_errno(EFAULT));
640 			pcadmin.pc_cid = pcadmin32.pc_cid;
641 			pcadmin.pc_cladmin = (caddr_t)(uintptr_t)
642 			    pcadmin32.pc_cladmin;
643 #endif /* _SYSCALL32_IMPL */
644 		}
645 
646 		if (pcadmin.pc_cid >= loaded_classes ||
647 		    pcadmin.pc_cid < 1)
648 			return (set_errno(EINVAL));
649 
650 		/*
651 		 * Have the class do whatever the user is requesting.
652 		 */
653 		mutex_enter(&ualock);
654 		error = CL_ADMIN(&sclass[pcadmin.pc_cid], pcadmin.pc_cladmin,
655 		    CRED());
656 		mutex_exit(&ualock);
657 		break;
658 
659 	case PC_GETPRIRANGE:
660 		if ((*copyinfn)(arg, &pcpri, sizeof (pcpri_t)))
661 			return (set_errno(EFAULT));
662 
663 		if (pcpri.pc_cid >= loaded_classes || pcpri.pc_cid < 0)
664 			return (set_errno(EINVAL));
665 
666 		error = CL_GETCLPRI(&sclass[pcpri.pc_cid], &pcpri);
667 		if (!error) {
668 			if ((*copyoutfn)(&pcpri, arg, sizeof (pcpri)))
669 				return (set_errno(EFAULT));
670 		}
671 		break;
672 
673 	case PC_DONICE:
674 		/*
675 		 * Get pcnice and procset structures from the user.
676 		 */
677 		if ((*copyinfn)(arg, &pcnice, sizeof (pcnice)) ||
678 		    (*copyinfn)(psp, &procset, sizeof (procset)))
679 			return (set_errno(EFAULT));
680 
681 		error = donice(&procset, &pcnice);
682 
683 		if (!error && (pcnice.pc_op == PC_GETNICE)) {
684 			if ((*copyoutfn)(&pcnice, arg, sizeof (pcnice)))
685 				return (set_errno(EFAULT));
686 		}
687 		break;
688 
689 	case PC_DOPRIO:
690 		/*
691 		 * Get pcprio and procset structures from the user.
692 		 */
693 		if ((*copyinfn)(arg, &pcprio, sizeof (pcprio)) ||
694 		    (*copyinfn)(psp, &procset, sizeof (procset)))
695 			return (set_errno(EFAULT));
696 
697 		error = doprio(&procset, &pcprio);
698 
699 		if (!error && (pcprio.pc_op == PC_GETPRIO)) {
700 			if ((*copyoutfn)(&pcprio, arg, sizeof (pcprio)))
701 				return (set_errno(EFAULT));
702 		}
703 		break;
704 
705 	case PC_SETDFLCL:
706 		if (secpolicy_dispadm(CRED()) != 0)
707 			return (set_errno(EPERM));
708 
709 		if (copyin(arg, (caddr_t)clname, PC_CLNMSZ) != 0)
710 			return (set_errno(EFAULT));
711 		clname[PC_CLNMSZ-1] = '\0';
712 
713 		if (getcid(clname, &classid) != 0)
714 			return (set_errno(EINVAL));
715 		if (CLASS_KERNEL(classid))
716 			return (set_errno(EINVAL));
717 		defaultcid = classid;
718 		ASSERT(defaultcid > 0 && defaultcid < loaded_classes);
719 		break;
720 
721 	case PC_GETDFLCL:
722 		mutex_enter(&class_lock);
723 
724 		if (defaultcid >= loaded_classes)
725 			outstr = "";
726 		else
727 			outstr = sclass[defaultcid].cl_name;
728 		size = strlen(outstr) + 1;
729 		if (arg != NULL)
730 			if ((*copyoutfn)(outstr, arg, size) != 0)
731 				error = EFAULT;
732 
733 		mutex_exit(&class_lock);
734 		break;
735 
736 	default:
737 		error = EINVAL;
738 		break;
739 	}
740 	return (error ? (set_errno(error)) : rv);
741 }
742 
743 long
priocntlsys(int pc_version,procset_t * psp,int cmd,caddr_t arg,caddr_t arg2)744 priocntlsys(int pc_version, procset_t *psp, int cmd, caddr_t arg, caddr_t arg2)
745 {
746 	return (priocntl_common(pc_version, psp, cmd, arg, arg2,
747 	    UIO_USERSPACE));
748 }
749 
750 /*
751  * The proccmp() function is part of the implementation of the
752  * PC_GETPARMS command of the priocntl system call.  This function works
753  * with the system call code and with the class specific cl_globpri()
754  * function to select one thread from a specified procset based on class
755  * specific criteria. proccmp() is called indirectly from the priocntl
756  * code through the dotoprocs function.  Basic strategy is dotoprocs()
757  * calls us once for each thread in the set.  We in turn call the class
758  * specific function to compare the current thread from dotoprocs to the
759  * "best" (according to the class criteria) found so far.  We keep the
760  * "best" thread in *pcmp_retthreadp.
761  */
762 static int
proccmp(proc_t * pp,struct pcmpargs * argp)763 proccmp(proc_t *pp, struct pcmpargs *argp)
764 {
765 	kthread_t	*tx;
766 	kthread_t	*ty;
767 	int		last_pri = -1;
768 	int		tx_pri;
769 	int		found = 0;
770 
771 	mutex_enter(&pp->p_lock);
772 
773 	if (pp->p_tlist == NULL) {
774 		mutex_exit(&pp->p_lock);
775 		return (0);
776 	}
777 	(*argp->pcmp_cntp)++;	/* Increment count of procs in the set */
778 
779 	if (*argp->pcmp_cidp == PC_CLNULL) {
780 		/*
781 		 * If no cid is specified, then lets just pick the first one.
782 		 * It doesn't matter because if the number of processes in the
783 		 * set are more than 1, then we return EINVAL in priocntlsys.
784 		 */
785 		*argp->pcmp_cidp = pp->p_tlist->t_cid;
786 	}
787 	ty = tx = pp->p_tlist;
788 	do {
789 		if (tx->t_cid == *argp->pcmp_cidp) {
790 			/*
791 			 * We found one which matches the required cid.
792 			 */
793 			found = 1;
794 			if ((tx_pri = CL_GLOBPRI(tx)) > last_pri) {
795 				last_pri = tx_pri;
796 				ty = tx;
797 			}
798 		}
799 	} while ((tx = tx->t_forw) != pp->p_tlist);
800 	if (found) {
801 		if (*argp->pcmp_retthreadp == NULL) {
802 			/*
803 			 * First time through for this set.
804 			 * keep the mutex held. It might be the one!
805 			 */
806 			*argp->pcmp_retthreadp = ty;
807 		} else {
808 			tx = *argp->pcmp_retthreadp;
809 			if (CL_GLOBPRI(ty) <= CL_GLOBPRI(tx)) {
810 				mutex_exit(&pp->p_lock);
811 			} else {
812 				mutex_exit(&(ttoproc(tx)->p_lock));
813 				*argp->pcmp_retthreadp = ty;
814 			}
815 		}
816 	} else {
817 		/*
818 		 * We actually didn't find anything of the same cid in
819 		 * this process.
820 		 */
821 		mutex_exit(&pp->p_lock);
822 	}
823 	return (0);
824 }
825 
826 
827 int
threadcmp(struct pcmpargs * argp,kthread_t * tp)828 threadcmp(struct pcmpargs *argp, kthread_t *tp)
829 {
830 	kthread_t	*tx;
831 	proc_t		*pp;
832 
833 	ASSERT(MUTEX_HELD(&(ttoproc(tp))->p_lock));
834 
835 	(*argp->pcmp_cntp)++;   /* Increment count of procs in the set */
836 	if (*argp->pcmp_cidp == PC_CLNULL) {
837 		/*
838 		 * If no cid is specified, then lets just pick the first one.
839 		 * It doesn't matter because if the number of threads in the
840 		 * set are more than 1, then we return EINVAL in priocntlsys.
841 		 */
842 		*argp->pcmp_cidp = tp->t_cid;
843 	}
844 	if (tp->t_cid == *argp->pcmp_cidp) {
845 		if (*argp->pcmp_retthreadp == NULL) {
846 			/*
847 			 * First time through for this set.
848 			 */
849 			*argp->pcmp_retthreadp = tp;
850 		} else {
851 			tx = *argp->pcmp_retthreadp;
852 			if (CL_GLOBPRI(tp) > CL_GLOBPRI(tx)) {
853 				/*
854 				 * Unlike proccmp(), we don't release the
855 				 * p_lock of the ttoproc(tp) if tp's global
856 				 * priority is less than tx's. We need to go
857 				 * through the entire list before we can do
858 				 * that. The p_lock is released by the caller
859 				 * of dotolwp().
860 				 */
861 				pp = ttoproc(tx);
862 				ASSERT(MUTEX_HELD(&pp->p_lock));
863 				if (pp != curproc) {
864 					mutex_exit(&pp->p_lock);
865 				}
866 				*argp->pcmp_retthreadp = tp;
867 			}
868 		}
869 	}
870 	return (0);
871 }
872 
873 
874 /*
875  * The setparms() function is called indirectly by priocntlsys()
876  * through the dotoprocs() function.  setparms() acts as an
877  * intermediary between dotoprocs() and the parmsset() function,
878  * calling parmsset() for each thread in the set and handling
879  * the error returns on their way back up to dotoprocs().
880  */
881 static int
setparms(proc_t * targpp,struct stprmargs * stprmp)882 setparms(proc_t *targpp, struct stprmargs *stprmp)
883 {
884 	int error = 0;
885 	kthread_t *t;
886 	int err;
887 
888 	mutex_enter(&targpp->p_lock);
889 	if ((t = targpp->p_tlist) == NULL) {
890 		mutex_exit(&targpp->p_lock);
891 		return (0);
892 	}
893 	do {
894 		err = parmsset(stprmp->stp_parmsp, t);
895 		if (error == 0)
896 			error = err;
897 	} while ((t = t->t_forw) != targpp->p_tlist);
898 	mutex_exit(&targpp->p_lock);
899 	if (error) {
900 		if (error == EPERM) {
901 			stprmp->stp_error = EPERM;
902 			return (0);
903 		} else {
904 			return (error);
905 		}
906 	} else
907 		return (0);
908 }
909 
910 int
setthreadnice(pcnice_t * pcnice,kthread_t * tp)911 setthreadnice(pcnice_t *pcnice, kthread_t *tp)
912 {
913 	int error;
914 	int nice;
915 	int inc;
916 	id_t rtcid;
917 
918 	ASSERT(MUTEX_HELD(&pidlock));
919 	ASSERT(MUTEX_HELD(&(ttoproc(tp)->p_lock)));
920 
921 	/*
922 	 * The XPG5 standard requires that any realtime process or thread
923 	 * must be unaffected by a call to setpriority().
924 	 */
925 	error = getcidbyname("RT", &rtcid);
926 	if (error == 0 && tp->t_cid == rtcid) {
927 		if (pcnice->pc_op == PC_SETNICE)
928 			return (0);
929 	}
930 
931 	if ((error = CL_DONICE(tp, CRED(), 0, &nice)) != 0)
932 		return (error);
933 
934 	if (pcnice->pc_op == PC_GETNICE) {
935 		/*
936 		 * If there is no change to priority, we should return the
937 		 * highest priority (lowest numerical value) pertaining to
938 		 * any of the specified threads.
939 		 */
940 		if (nice < pcnice->pc_val)
941 			pcnice->pc_val = nice;
942 	} else {
943 		ASSERT(pcnice->pc_op == PC_SETNICE);
944 		/*
945 		 * Try to change the nice value of the thread.
946 		 */
947 		inc = pcnice->pc_val - nice;
948 
949 		error = CL_DONICE(tp, CRED(), inc, &inc);
950 		schedctl_set_cidpri(tp);
951 	}
952 
953 	return (error);
954 }
955 
956 int
setprocnice(proc_t * pp,pcnice_t * pcnice)957 setprocnice(proc_t *pp, pcnice_t *pcnice)
958 {
959 	kthread_t *tp;
960 	int retval = 0;
961 	int error;
962 
963 	ASSERT(MUTEX_HELD(&pidlock));
964 	mutex_enter(&pp->p_lock);
965 
966 	if ((tp = pp->p_tlist) == NULL) {
967 		mutex_exit(&pp->p_lock);
968 		return (ESRCH);
969 	}
970 
971 	/*
972 	 * Check permissions before changing the nice value.
973 	 */
974 	if (pcnice->pc_op == PC_SETNICE) {
975 		if (!prochasprocperm(pp, curproc, CRED())) {
976 			mutex_exit(&pp->p_lock);
977 			return (EPERM);
978 		}
979 	}
980 
981 	do {
982 		error = setthreadnice(pcnice, tp);
983 		if (error)
984 			retval = error;
985 	} while ((tp = tp->t_forw) != pp->p_tlist);
986 
987 	mutex_exit(&pp->p_lock);
988 	return (retval);
989 }
990 
991 /*
992  * Update the nice value of the specified LWP or set of processes.
993  */
994 static int
donice(procset_t * procset,pcnice_t * pcnice)995 donice(procset_t *procset, pcnice_t *pcnice)
996 {
997 	int err_proc = 0;
998 	int err_thread = 0;
999 	int err = 0;
1000 
1001 	/*
1002 	 * Sanity check.
1003 	 */
1004 	if (pcnice->pc_op != PC_GETNICE && pcnice->pc_op != PC_SETNICE)
1005 		return (EINVAL);
1006 
1007 	/*
1008 	 * If it is PC_GETNICE operation then set pc_val to the largest
1009 	 * possible nice value to help us find the lowest nice value
1010 	 * pertaining to any of the specified processes.
1011 	 */
1012 	if (pcnice->pc_op == PC_GETNICE)
1013 		pcnice->pc_val = NZERO;
1014 
1015 	if (procset->p_lidtype != P_LWPID ||
1016 	    procset->p_ridtype != P_LWPID)
1017 		err_proc = dotoprocs(procset, setprocnice, (char *)pcnice);
1018 
1019 	if (procset->p_lidtype == P_LWPID || procset->p_ridtype == P_LWPID) {
1020 		err_thread = dotolwp(procset, setthreadnice, (char *)pcnice);
1021 		/*
1022 		 * dotolwp() can return with p_lock held.  This is required
1023 		 * for the priocntl GETPARMS case.  So, here we just release
1024 		 * the p_lock.
1025 		 */
1026 		if (MUTEX_HELD(&curproc->p_lock))
1027 			mutex_exit(&curproc->p_lock);
1028 
1029 		/*
1030 		 * If we were called for a single LWP, then ignore ESRCH
1031 		 * returned by the previous dotoprocs() call.
1032 		 */
1033 		if (err_proc == ESRCH)
1034 			err_proc = 0;
1035 	}
1036 
1037 	/*
1038 	 * dotoprocs() ignores the init process if it is in the set, unless
1039 	 * it was the only process found. We want to make sure init is not
1040 	 * excluded if we're going PC_GETNICE operation.
1041 	 */
1042 	if (pcnice->pc_op == PC_GETNICE) {
1043 		proc_t *initpp;
1044 
1045 		mutex_enter(&pidlock);
1046 		if ((initpp = prfind(P_INITPID)) != NULL) {
1047 			mutex_enter(&initpp->p_lock);
1048 			if (procinset(initpp, procset)) {
1049 				mutex_exit(&initpp->p_lock);
1050 				err = setprocnice(initpp, pcnice);
1051 			} else {
1052 				mutex_exit(&initpp->p_lock);
1053 			}
1054 		}
1055 		mutex_exit(&pidlock);
1056 	}
1057 
1058 	/*
1059 	 * We're returning the latest error here that we've got back from
1060 	 * the setthreadnice() or setprocnice(). That is, err_thread and/or
1061 	 * err_proc can be replaced by err.
1062 	 */
1063 	if (!err)
1064 		err = err_thread ? err_thread : err_proc;
1065 
1066 	return (err);
1067 }
1068 
1069 int
setthreadprio(pcprio_t * pcprio,kthread_t * tp)1070 setthreadprio(pcprio_t *pcprio, kthread_t *tp)
1071 {
1072 	int prio = 0;
1073 	int incr;
1074 	int error;
1075 
1076 	ASSERT(MUTEX_HELD(&pidlock));
1077 	ASSERT(MUTEX_HELD(&(ttoproc(tp)->p_lock)));
1078 
1079 	if (pcprio->pc_op == PC_SETPRIO && pcprio->pc_cid != tp->t_cid) {
1080 		/*
1081 		 * Target thread must change to new class.
1082 		 * See comments in parmsset(), from where this code was copied.
1083 		 */
1084 		void *bufp = NULL;
1085 		caddr_t clprocp = (caddr_t)tp->t_cldata;
1086 		id_t oldcid = tp->t_cid;
1087 
1088 		error = CL_CANEXIT(tp, NULL);
1089 		if (error)
1090 			return (error);
1091 		if (CL_ALLOC(&bufp, pcprio->pc_cid, KM_NOSLEEP) != 0)
1092 			return (ENOMEM);
1093 		error = CL_ENTERCLASS(tp, pcprio->pc_cid, NULL, CRED(), bufp);
1094 		if (error) {
1095 			CL_FREE(pcprio->pc_cid, bufp);
1096 			return (error);
1097 		}
1098 		CL_EXITCLASS(oldcid, clprocp);
1099 		schedctl_set_cidpri(tp);
1100 	}
1101 
1102 	if ((error = CL_DOPRIO(tp, CRED(), 0, &prio)) != 0)
1103 		return (error);
1104 
1105 	if (pcprio->pc_op == PC_GETPRIO) {
1106 		/*
1107 		 * If we are not setting the priority, we should return the
1108 		 * highest priority pertaining to any of the specified threads.
1109 		 */
1110 		if (prio > pcprio->pc_val) {
1111 			pcprio->pc_cid = tp->t_cid;
1112 			pcprio->pc_val = prio;
1113 		}
1114 	} else if (prio != pcprio->pc_val) {
1115 		/*
1116 		 * Try to change the priority of the thread.
1117 		 */
1118 		incr = pcprio->pc_val - prio;
1119 		error = CL_DOPRIO(tp, CRED(), incr, &prio);
1120 		schedctl_set_cidpri(tp);
1121 	}
1122 
1123 	return (error);
1124 }
1125 
1126 int
setprocprio(proc_t * pp,pcprio_t * pcprio)1127 setprocprio(proc_t *pp, pcprio_t *pcprio)
1128 {
1129 	kthread_t *tp;
1130 	int retval = 0;
1131 	int error;
1132 
1133 	ASSERT(MUTEX_HELD(&pidlock));
1134 	mutex_enter(&pp->p_lock);
1135 
1136 	if ((tp = pp->p_tlist) == NULL) {
1137 		mutex_exit(&pp->p_lock);
1138 		return (ESRCH);
1139 	}
1140 
1141 	/*
1142 	 * Check permissions before changing the prio value.
1143 	 */
1144 	if (pcprio->pc_op == PC_SETPRIO) {
1145 		if (!prochasprocperm(pp, curproc, CRED())) {
1146 			mutex_exit(&pp->p_lock);
1147 			return (EPERM);
1148 		}
1149 	}
1150 
1151 	do {
1152 		error = setthreadprio(pcprio, tp);
1153 		if (error)
1154 			retval = error;
1155 	} while ((tp = tp->t_forw) != pp->p_tlist);
1156 
1157 	mutex_exit(&pp->p_lock);
1158 	return (retval);
1159 }
1160 
1161 /*
1162  * Set the class and priority of the specified LWP or set of processes.
1163  */
1164 static int
doprio(procset_t * procset,pcprio_t * pcprio)1165 doprio(procset_t *procset, pcprio_t *pcprio)
1166 {
1167 	int err_proc = 0;
1168 	int err_thread = 0;
1169 	int err = 0;
1170 
1171 	/*
1172 	 * Sanity check.
1173 	 */
1174 	if (pcprio->pc_op != PC_GETPRIO && pcprio->pc_op != PC_SETPRIO)
1175 		return (EINVAL);
1176 	if (pcprio->pc_op == PC_SETPRIO &&
1177 	    (pcprio->pc_cid >= loaded_classes || pcprio->pc_cid < 1))
1178 		return (EINVAL);
1179 
1180 	/*
1181 	 * If it is a PC_GETPRIO operation then set pc_val to the smallest
1182 	 * possible prio value to help us find the highest priority
1183 	 * pertaining to any of the specified processes.
1184 	 */
1185 	if (pcprio->pc_op == PC_GETPRIO)
1186 		pcprio->pc_val = SHRT_MIN;
1187 
1188 	if (procset->p_lidtype != P_LWPID ||
1189 	    procset->p_ridtype != P_LWPID)
1190 		err_proc = dotoprocs(procset, setprocprio, (char *)pcprio);
1191 
1192 	if (procset->p_lidtype == P_LWPID || procset->p_ridtype == P_LWPID) {
1193 		err_thread = dotolwp(procset, setthreadprio, (char *)pcprio);
1194 		/*
1195 		 * dotolwp() can return with p_lock held.  This is required
1196 		 * for the priocntl GETPARMS case.  So, here we just release
1197 		 * the p_lock.
1198 		 */
1199 		if (MUTEX_HELD(&curproc->p_lock))
1200 			mutex_exit(&curproc->p_lock);
1201 
1202 		/*
1203 		 * If we were called for a single LWP, then ignore ESRCH
1204 		 * returned by the previous dotoprocs() call.
1205 		 */
1206 		if (err_proc == ESRCH)
1207 			err_proc = 0;
1208 	}
1209 
1210 	/*
1211 	 * dotoprocs() ignores the init process if it is in the set, unless
1212 	 * it was the only process found. We want to make sure init is not
1213 	 * excluded if we're going PC_GETPRIO operation.
1214 	 */
1215 	if (pcprio->pc_op == PC_GETPRIO) {
1216 		proc_t *initpp;
1217 
1218 		mutex_enter(&pidlock);
1219 		if ((initpp = prfind(P_INITPID)) != NULL) {
1220 			mutex_enter(&initpp->p_lock);
1221 			if (procinset(initpp, procset)) {
1222 				mutex_exit(&initpp->p_lock);
1223 				err = setprocprio(initpp, pcprio);
1224 			} else {
1225 				mutex_exit(&initpp->p_lock);
1226 			}
1227 		}
1228 		mutex_exit(&pidlock);
1229 	}
1230 
1231 	/*
1232 	 * We're returning the latest error here that we've got back from
1233 	 * the setthreadprio() or setprocprio(). That is, err_thread and/or
1234 	 * err_proc can be replaced by err.
1235 	 */
1236 	if (!err)
1237 		err = err_thread ? err_thread : err_proc;
1238 
1239 	return (err);
1240 }
1241