xref: /illumos-gate/usr/src/lib/libc/port/rt/sched.c (revision e8d3efa1c56e5f2b5368600a2baeb7b1d54a07f8)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2026 Oxide Computer Company
29  */
30 
31 #include "lint.h"
32 #include "thr_uberdata.h"
33 #include <sched.h>
34 #include <spawn.h>
35 #include <stdbool.h>
36 #include <sys/tspriocntl.h>
37 #include <sys/rtpriocntl.h>
38 #include <sys/fxpriocntl.h>
39 #include <sys/spawn_impl.h>
40 
41 /*
42  * The following array is used for caching information
43  * for priocntl scheduling classes.
44  */
45 static pcclass_t sched_class[] = {
46 	{0, SCHED_OTHER, 0, 0, {-1, "TS",  0}},
47 	{0, SCHED_FIFO,	 0, 0, {-1, "RT",  0}},
48 	{0, SCHED_RR,	 0, 0, {-1, "RT",  0}},
49 	{0, SCHED_SYS,	 0, 0, {0,  "SYS", 0}},
50 	{0, SCHED_IA,	 0, 0, {-1, "IA",  0}},
51 	{0, SCHED_FSS,	 0, 0, {-1, "FSS", 0}},
52 	{0, SCHED_FX,	 0, 0, {-1, "FX",  0}},
53 	/*
54 	 * Allow unknown (to us) scheduling classes.
55 	 * The kernel allows space for exactly 10 scheduling classes
56 	 * (see the definitions of 'sclass' and 'nclass' in the kernel).
57 	 * We need that number of available slots here.
58 	 * If the kernel space is changed, this has to change too.
59 	 */
60 	{0, -1,		 0, 0, {-1, "",	   0}},
61 	{0, -1,		 0, 0, {-1, "",	   0}},
62 	{0, -1,		 0, 0, {-1, "",	   0}},
63 	{0, -1,		 0, 0, {-1, "",	   0}},
64 	{0, -1,		 0, 0, {-1, "",	   0}},
65 	{0, -1,		 0, 0, {-1, "",	   0}},
66 	{0, -1,		 0, 0, {-1, "",	   0}},
67 	{0, -1,		 0, 0, {-1, "",	   0}},
68 	{0, -1,		 0, 0, {-1, "",	   0}},
69 	{0, -1,		 0, 0, {-1, "",	   0}},
70 };
71 
72 #define	NPOLICY	(sizeof (sched_class) / sizeof (pcclass_t))
73 
74 #if _SCHED_NEXT != SCHED_FX + 1
75 #error "fatal: _SCHED_NEXT != SCHED_FX + 1"
76 #endif
77 
78 static mutex_t class_lock = DEFAULTMUTEX;	/* protects sched_class[] */
79 
80 /*
81  * Helper function for get_info_by_policy(), below.
82  * Don't let a manufactured policy number duplicate
83  * the class of one of our base policy numbers.
84  */
85 static int
86 is_base_class(const char *clname)
87 {
88 	const pcclass_t	*pccp;
89 	int		policy;
90 
91 	for (policy = 0, pccp = sched_class;
92 	    policy < _SCHED_NEXT;
93 	    policy++, pccp++) {
94 		if (strcmp(clname, pccp->pcc_info.pc_clname) == 0)
95 			return (1);
96 	}
97 	return (0);
98 }
99 
100 /*
101  * Cache priocntl information on scheduling class by policy.
102  */
103 const pcclass_t *
104 get_info_by_policy(int policy)
105 {
106 	pcclass_t *pccp = &sched_class[policy];
107 	pcpri_t pcpri;
108 	pri_t prio;
109 	int base = 0;
110 
111 	if ((uint_t)policy >= NPOLICY || pccp->pcc_state < 0) {
112 		errno = EINVAL;
113 		return (NULL);
114 	}
115 
116 	if (pccp->pcc_state > 0)
117 		return (pccp);
118 
119 	lmutex_lock(&class_lock);
120 
121 	/* get class info (the system class is known to have class-id == 0) */
122 	if (pccp->pcc_policy == -1) {
123 		/* policy number not defined in <sched.h> */
124 		ASSERT(policy >= _SCHED_NEXT);
125 		pccp->pcc_info.pc_cid = policy - _SCHED_NEXT;
126 		if (priocntl(0, 0, PC_GETCLINFO, &pccp->pcc_info) == -1 ||
127 		    (base = is_base_class(pccp->pcc_info.pc_clname)) != 0) {
128 			pccp->pcc_info.pc_clname[0] = '\0';
129 			pccp->pcc_info.pc_cid = -1;
130 			/*
131 			 * If we duplicated a base class, permanently
132 			 * disable this policy entry.  Else allow for
133 			 * dynamic loading of scheduling classes.
134 			 */
135 			if (base) {
136 				membar_producer();
137 				pccp->pcc_state = -1;
138 			}
139 			errno = EINVAL;
140 			lmutex_unlock(&class_lock);
141 			return (NULL);
142 		}
143 		pccp->pcc_policy = policy;
144 	} else if (policy != SCHED_SYS &&
145 	    priocntl(0, 0, PC_GETCID, &pccp->pcc_info) == -1) {
146 		membar_producer();
147 		pccp->pcc_state = -1;
148 		errno = EINVAL;
149 		lmutex_unlock(&class_lock);
150 		return (NULL);
151 	}
152 
153 	switch (policy) {
154 	case SCHED_OTHER:
155 		prio = ((tsinfo_t *)pccp->pcc_info.pc_clinfo)->ts_maxupri;
156 		pccp->pcc_primin = -prio;
157 		pccp->pcc_primax = prio;
158 		break;
159 	case SCHED_FIFO:
160 	case SCHED_RR:
161 		prio = ((rtinfo_t *)pccp->pcc_info.pc_clinfo)->rt_maxpri;
162 		pccp->pcc_primin = 0;
163 		pccp->pcc_primax = prio;
164 		break;
165 	default:
166 		/*
167 		 * All other policy numbers, including policy numbers
168 		 * not defined in <sched.h>.
169 		 */
170 		pcpri.pc_cid = pccp->pcc_info.pc_cid;
171 		if (priocntl(0, 0, PC_GETPRIRANGE, &pcpri) == 0) {
172 			pccp->pcc_primin = pcpri.pc_clpmin;
173 			pccp->pcc_primax = pcpri.pc_clpmax;
174 		}
175 		break;
176 	}
177 
178 	membar_producer();
179 	pccp->pcc_state = 1;
180 	lmutex_unlock(&class_lock);
181 	return (pccp);
182 }
183 
184 const pcclass_t *
185 get_info_by_class(id_t classid)
186 {
187 	pcinfo_t	pcinfo;
188 	pcclass_t	*pccp;
189 	int		policy;
190 
191 	if (classid < 0) {
192 		errno = EINVAL;
193 		return (NULL);
194 	}
195 
196 	/* determine if we already know this classid */
197 	for (policy = 0, pccp = sched_class;
198 	    policy < NPOLICY;
199 	    policy++, pccp++) {
200 		if (pccp->pcc_state > 0 && pccp->pcc_info.pc_cid == classid)
201 			return (pccp);
202 	}
203 
204 	pcinfo.pc_cid = classid;
205 	if (priocntl(0, 0, PC_GETCLINFO, &pcinfo) == -1) {
206 		if (classid == 0)	/* no kernel info for sys class */
207 			return (get_info_by_policy(SCHED_SYS));
208 		return (NULL);
209 	}
210 
211 	for (policy = 0, pccp = sched_class;
212 	    policy < NPOLICY;
213 	    policy++, pccp++) {
214 		if (pccp->pcc_state == 0 &&
215 		    strcmp(pcinfo.pc_clname, pccp->pcc_info.pc_clname) == 0)
216 			return (get_info_by_policy(pccp->pcc_policy));
217 	}
218 
219 	/*
220 	 * We have encountered an unknown (to us) scheduling class.
221 	 * Manufacture a policy number for it.  Hopefully we still
222 	 * have room in the sched_class[] table.
223 	 */
224 	policy = _SCHED_NEXT + classid;
225 	if (policy >= NPOLICY) {
226 		errno = EINVAL;
227 		return (NULL);
228 	}
229 	lmutex_lock(&class_lock);
230 	pccp = &sched_class[policy];
231 	pccp->pcc_policy = policy;
232 	(void) strlcpy(pccp->pcc_info.pc_clname, pcinfo.pc_clname, PC_CLNMSZ);
233 	lmutex_unlock(&class_lock);
234 	return (get_info_by_policy(pccp->pcc_policy));
235 }
236 
237 /*
238  * Helper function: get process or lwp current scheduling policy.
239  */
240 static const pcclass_t *
241 get_parms(idtype_t idtype, id_t id, pcparms_t *pcparmp)
242 {
243 	pcparmp->pc_cid = PC_CLNULL;
244 	if (priocntl(idtype, id, PC_GETPARMS, pcparmp) == -1)
245 		return (NULL);
246 	return (get_info_by_class(pcparmp->pc_cid));
247 }
248 
249 /*
250  * Helper function for set_priority() and spawn_sched_resolve(), below.
251  * Resolve a policy and priority into the form to be applied to the
252  * target.
253  */
254 static int
255 resolve_priority(int policy, int prio, int settq, pcparms_t *pcparmp,
256     pcprio_t *pcpriop)
257 {
258 	switch (policy) {
259 	case SCHED_OTHER:
260 	{
261 		tsparms_t *tsp = (tsparms_t *)pcparmp->pc_clparms;
262 
263 		tsp->ts_uprilim = prio;
264 		tsp->ts_upri = prio;
265 		return (PC_SETPARMS);
266 	}
267 	case SCHED_FIFO:
268 	case SCHED_RR:
269 	{
270 		rtparms_t *rtp = (rtparms_t *)pcparmp->pc_clparms;
271 
272 		rtp->rt_tqnsecs = settq?
273 		    (policy == SCHED_FIFO? RT_TQINF : RT_TQDEF) :
274 		    RT_NOCHANGE;
275 		rtp->rt_pri = prio;
276 		return (PC_SETPARMS);
277 	}
278 	default:
279 	{
280 		/*
281 		 * Class-independent method for setting the priority.
282 		 */
283 		pcpriop->pc_op = PC_SETPRIO;
284 		pcpriop->pc_cid = pcparmp->pc_cid;
285 		pcpriop->pc_val = prio;
286 		return (PC_DOPRIO);
287 	}
288 	}
289 }
290 
291 /*
292  * Helper function for setprio() and setparam(), below.
293  */
294 static int
295 set_priority(idtype_t idtype, id_t id, int policy, int prio,
296     pcparms_t *pcparmp, int settq)
297 {
298 	pcprio_t pcprio;
299 	caddr_t arg;
300 	int cmd, rv;
301 
302 	cmd = resolve_priority(policy, prio, settq, pcparmp, &pcprio);
303 	arg = (cmd == PC_SETPARMS) ? (caddr_t)pcparmp : (caddr_t)&pcprio;
304 
305 	do {
306 		rv = priocntl(idtype, id, cmd, arg);
307 	} while (rv == -1 && errno == ENOMEM);
308 
309 	return (rv);
310 }
311 
312 /*
313  * Utility function, private to libc, used by sched_setparam()
314  * and posix_spawn().  Because it is called by the vfork() child of
315  * posix_spawn(), we must not call any functions exported from libc.
316  */
317 id_t
318 setprio(idtype_t idtype, id_t id, int prio, int *policyp)
319 {
320 	pcparms_t	pcparm;
321 	int		policy;
322 	const pcclass_t	*pccp;
323 
324 	if ((pccp = get_parms(idtype, id, &pcparm)) == NULL)
325 		return (-1);
326 	if (prio < pccp->pcc_primin || prio > pccp->pcc_primax) {
327 		errno = EINVAL;
328 		return (-1);
329 	}
330 
331 	policy = pccp->pcc_policy;
332 	if (policyp != NULL &&
333 	    (policy == SCHED_FIFO || policy == SCHED_RR)) {
334 		rtparms_t *rtp = (rtparms_t *)pcparm.pc_clparms;
335 		policy = (rtp->rt_tqnsecs == RT_TQINF? SCHED_FIFO : SCHED_RR);
336 	}
337 
338 	if (set_priority(idtype, id, policy, prio, &pcparm, 0) == -1)
339 		return (-1);
340 	if (policyp != NULL)
341 		*policyp = policy;
342 	return (pccp->pcc_info.pc_cid);
343 }
344 
345 int
346 sched_setparam(pid_t pid, const struct sched_param *param)
347 {
348 	if (pid < 0) {
349 		errno = ESRCH;
350 		return (-1);
351 	}
352 	if (pid == 0)
353 		pid = P_MYID;
354 
355 	if (setprio(P_PID, pid, param->sched_priority, NULL) == -1)
356 		return (-1);
357 	return (0);
358 }
359 
360 id_t
361 getparam(idtype_t idtype, id_t id, int *policyp, struct sched_param *param)
362 {
363 	pcparms_t pcparm;
364 	const pcclass_t *pccp;
365 	int policy;
366 	int priority;
367 
368 	if ((pccp = get_parms(idtype, id, &pcparm)) == NULL)
369 		return (-1);
370 
371 	switch (policy = pccp->pcc_policy) {
372 	case SCHED_OTHER:
373 	{
374 		tsparms_t *tsp = (tsparms_t *)pcparm.pc_clparms;
375 		priority = tsp->ts_upri;
376 		break;
377 	}
378 	case SCHED_FIFO:
379 	case SCHED_RR:
380 	{
381 		rtparms_t *rtp = (rtparms_t *)pcparm.pc_clparms;
382 		priority = rtp->rt_pri;
383 		policy = (rtp->rt_tqnsecs == RT_TQINF? SCHED_FIFO : SCHED_RR);
384 		break;
385 	}
386 	default:
387 	{
388 		/*
389 		 * Class-independent method for getting the priority.
390 		 */
391 		pcprio_t pcprio;
392 
393 		pcprio.pc_op = PC_GETPRIO;
394 		pcprio.pc_cid = 0;
395 		pcprio.pc_val = 0;
396 		if (priocntl(idtype, id, PC_DOPRIO, &pcprio) == 0)
397 			priority = pcprio.pc_val;
398 		else
399 			priority = 0;
400 		break;
401 	}
402 	}
403 
404 	*policyp = policy;
405 	(void) memset(param, 0, sizeof (*param));
406 	param->sched_priority = priority;
407 
408 	return (pcparm.pc_cid);
409 }
410 
411 int
412 sched_getparam(pid_t pid, struct sched_param *param)
413 {
414 	int policy;
415 
416 	if (pid < 0) {
417 		errno = ESRCH;
418 		return (-1);
419 	}
420 	if (pid == 0)
421 		pid = P_MYID;
422 
423 	if (getparam(P_PID, pid, &policy, param) == -1)
424 		return (-1);
425 	return (0);
426 }
427 
428 /*
429  * Utility function, private to libc, used by sched_setscheduler()
430  * and posix_spawn().  Because it is called by the vfork() child of
431  * posix_spawn(), we must not call any functions exported from libc.
432  */
433 id_t
434 setparam(idtype_t idtype, id_t id, int policy, int prio)
435 {
436 	pcparms_t	pcparm;
437 	const pcclass_t	*pccp;
438 
439 	if (policy == SCHED_SYS ||
440 	    (pccp = get_info_by_policy(policy)) == NULL ||
441 	    prio < pccp->pcc_primin || prio > pccp->pcc_primax) {
442 		errno = EINVAL;
443 		return (-1);
444 	}
445 
446 	pcparm.pc_cid = pccp->pcc_info.pc_cid;
447 	if (set_priority(idtype, id, policy, prio, &pcparm, 1) == -1)
448 		return (-1);
449 	return (pccp->pcc_info.pc_cid);
450 }
451 
452 /*
453  * Utility function, private to libc, used by posix_spawn() when the
454  * scheduler attribute flags are in effect. It performs the same validation and
455  * resolution that setparam() or setprio() above would, but the target of the
456  * change (the spawn(2) child) does not yet exist so the result is handed back
457  * ready for passing to the kernel.
458  */
459 int
460 spawn_sched_resolve(int psflags, int policy, int prio, kspawn_sched_t *ks)
461 {
462 	bool setsched = (psflags & POSIX_SPAWN_SETSCHEDULER) != 0;
463 	pcparms_t *pcparmp = &ks->ksched_parms;
464 	const pcclass_t *pccp;
465 	pcprio_t pcprio;
466 
467 	(void) memset(ks, 0, sizeof (*ks));
468 
469 	if (setsched) {
470 		if (policy == SCHED_SYS ||
471 		    (pccp = get_info_by_policy(policy)) == NULL)
472 			return (EINVAL);
473 		pcparmp->pc_cid = pccp->pcc_info.pc_cid;
474 	} else {
475 		/*
476 		 * The priority change is applied within the scheduling
477 		 * class that the child inherits from this process.
478 		 */
479 		if ((pccp = get_parms(P_LWPID, P_MYID, pcparmp)) == NULL)
480 			return (errno);
481 		policy = pccp->pcc_policy;
482 	}
483 
484 	if (prio < pccp->pcc_primin || prio > pccp->pcc_primax)
485 		return (EINVAL);
486 
487 	if (resolve_priority(policy, prio, setsched, pcparmp,
488 	    &pcprio) == PC_SETPARMS) {
489 		ks->ksched_op = KSCHED_PARMS;
490 	} else {
491 		ks->ksched_op = KSCHED_PRIO;
492 		ks->ksched_prio = pcprio;
493 	}
494 
495 	return (0);
496 }
497 
498 int
499 sched_setscheduler(pid_t pid, int policy, const struct sched_param *param)
500 {
501 	pri_t		prio = param->sched_priority;
502 	int		oldpolicy;
503 
504 	if ((oldpolicy = sched_getscheduler(pid)) < 0)
505 		return (-1);
506 
507 	if (pid == 0)
508 		pid = P_MYID;
509 
510 	if (setparam(P_PID, pid, policy, prio) == -1)
511 		return (-1);
512 
513 	return (oldpolicy);
514 }
515 
516 int
517 sched_getscheduler(pid_t pid)
518 {
519 	pcparms_t	pcparm;
520 	const pcclass_t	*pccp;
521 	int		policy;
522 
523 	if (pid < 0) {
524 		errno = ESRCH;
525 		return (-1);
526 	}
527 	if (pid == 0)
528 		pid = P_MYID;
529 
530 	if ((pccp = get_parms(P_PID, pid, &pcparm)) == NULL)
531 		return (-1);
532 
533 	if ((policy = pccp->pcc_policy) == SCHED_FIFO || policy == SCHED_RR) {
534 		policy =
535 		    (((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == RT_TQINF?
536 		    SCHED_FIFO : SCHED_RR);
537 	}
538 
539 	return (policy);
540 }
541 
542 int
543 sched_yield(void)
544 {
545 	yield();
546 	return (0);
547 }
548 
549 int
550 sched_get_priority_max(int policy)
551 {
552 	const pcclass_t *pccp;
553 
554 	if ((pccp = get_info_by_policy(policy)) != NULL)
555 		return (pccp->pcc_primax);
556 	errno = EINVAL;
557 	return (-1);
558 }
559 
560 int
561 sched_get_priority_min(int policy)
562 {
563 	const pcclass_t *pccp;
564 
565 	if ((pccp = get_info_by_policy(policy)) != NULL)
566 		return (pccp->pcc_primin);
567 	errno = EINVAL;
568 	return (-1);
569 }
570 
571 int
572 sched_rr_get_interval(pid_t pid, timespec_t *interval)
573 {
574 	pcparms_t pcparm;
575 	const pcclass_t *pccp;
576 
577 	if (pid < 0) {
578 		errno = ESRCH;
579 		return (-1);
580 	}
581 	if (pid == 0)
582 		pid = P_MYID;
583 
584 	if ((pccp = get_parms(P_PID, pid, &pcparm)) == NULL)
585 		return (-1);
586 
587 	/*
588 	 * At the moment, we have no class-independent method to fetch
589 	 * the process/lwp time quantum.  Since SUSv3 does not restrict
590 	 * this operation to the real-time class, we return an indefinite
591 	 * quantum (tv_sec == 0 and tv_nsec == 0) for scheduling policies
592 	 * for which this information isn't available.
593 	 */
594 	interval->tv_sec = 0;
595 	interval->tv_nsec = 0;
596 
597 	switch (pccp->pcc_policy) {
598 	case SCHED_FIFO:
599 	case SCHED_RR:
600 		{
601 			rtparms_t *rtp = (rtparms_t *)pcparm.pc_clparms;
602 			if (rtp->rt_tqnsecs != RT_TQINF) {
603 				interval->tv_sec = rtp->rt_tqsecs;
604 				interval->tv_nsec = rtp->rt_tqnsecs;
605 			}
606 		}
607 		break;
608 	case SCHED_FX:
609 		{
610 			fxparms_t *fxp = (fxparms_t *)pcparm.pc_clparms;
611 			if (fxp->fx_tqnsecs != FX_TQINF) {
612 				interval->tv_sec = fxp->fx_tqsecs;
613 				interval->tv_nsec = fxp->fx_tqnsecs;
614 			}
615 		}
616 		break;
617 	}
618 
619 	return (0);
620 }
621 
622 /*
623  * Initialize or update ul_policy, ul_cid, and ul_pri.
624  */
625 void
626 update_sched(ulwp_t *self)
627 {
628 	volatile sc_shared_t *scp;
629 	pcparms_t pcparm;
630 	pcprio_t pcprio;
631 	const pcclass_t *pccp;
632 	int priority;
633 	int policy;
634 
635 	ASSERT(self == curthread);
636 
637 	enter_critical(self);
638 
639 	if ((scp = self->ul_schedctl) == NULL &&
640 	    (scp = setup_schedctl()) == NULL) {		/* can't happen? */
641 		if (self->ul_policy < 0) {
642 			self->ul_cid = 0;
643 			self->ul_pri = 0;
644 			membar_producer();
645 			self->ul_policy = SCHED_OTHER;
646 		}
647 		exit_critical(self);
648 		return;
649 	}
650 
651 	if (self->ul_policy >= 0 &&
652 	    self->ul_cid == scp->sc_cid &&
653 	    (self->ul_pri == scp->sc_cpri ||
654 	    (self->ul_epri > 0 && self->ul_epri == scp->sc_cpri))) {
655 		exit_critical(self);
656 		return;
657 	}
658 
659 	pccp = get_parms(P_LWPID, P_MYID, &pcparm);
660 	if (pccp == NULL) {		/* can't happen? */
661 		self->ul_cid = scp->sc_cid;
662 		self->ul_pri = scp->sc_cpri;
663 		membar_producer();
664 		self->ul_policy = SCHED_OTHER;
665 		exit_critical(self);
666 		return;
667 	}
668 
669 	switch (policy = pccp->pcc_policy) {
670 	case SCHED_OTHER:
671 		priority = ((tsparms_t *)pcparm.pc_clparms)->ts_upri;
672 		break;
673 	case SCHED_FIFO:
674 	case SCHED_RR:
675 		self->ul_rtclassid = pccp->pcc_info.pc_cid;
676 		priority = ((rtparms_t *)pcparm.pc_clparms)->rt_pri;
677 		policy =
678 		    ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == RT_TQINF?
679 		    SCHED_FIFO : SCHED_RR;
680 		break;
681 	default:
682 		/*
683 		 * Class-independent method for getting the priority.
684 		 */
685 		pcprio.pc_op = PC_GETPRIO;
686 		pcprio.pc_cid = 0;
687 		pcprio.pc_val = 0;
688 		if (priocntl(P_LWPID, P_MYID, PC_DOPRIO, &pcprio) == 0)
689 			priority = pcprio.pc_val;
690 		else
691 			priority = 0;
692 	}
693 
694 	self->ul_cid = pcparm.pc_cid;
695 	self->ul_pri = priority;
696 	membar_producer();
697 	self->ul_policy = policy;
698 
699 	exit_critical(self);
700 }
701