xref: /freebsd/sys/kern/ksched.c (revision 7899f917b1c0ea178f1d2be0cfb452086d079d23)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1996, 1997
5  *	HD Associates, Inc.  All rights reserved.
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  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by HD Associates, Inc
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /* ksched: Soft real time scheduling based on "rtprio". */
36 
37 #include <sys/cdefs.h>
38 #include "opt_posix.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/lock.h>
43 #include <sys/sysctl.h>
44 #include <sys/kernel.h>
45 #include <sys/mutex.h>
46 #include <sys/proc.h>
47 #include <sys/posix4.h>
48 #include <sys/resource.h>
49 #include <sys/sched.h>
50 
51 FEATURE(kposix_priority_scheduling, "POSIX P1003.1B realtime extensions");
52 
53 /* ksched: Real-time extension to support POSIX priority scheduling. */
54 
55 struct ksched {
56 	struct timespec rr_interval;
57 };
58 
59 int
60 ksched_attach(struct ksched **p)
61 {
62 	struct ksched *ksched;
63 
64 	ksched = malloc(sizeof(*ksched), M_P31B, M_WAITOK);
65 	ksched->rr_interval.tv_sec = 0;
66 	ksched->rr_interval.tv_nsec = 1000000000L / hz * sched_rr_interval();
67 	*p = ksched;
68 	return (0);
69 }
70 
71 int
72 ksched_detach(struct ksched *ks)
73 {
74 
75 	free(ks, M_P31B);
76 	return (0);
77 }
78 
79 /*
80  * XXX About priorities
81  *
82  *	POSIX 1003.1b requires that numerically higher priorities be of
83  *	higher priority.  It also permits sched_setparam to be
84  *	implementation defined for SCHED_OTHER.  I don't like
85  *	the notion of inverted priorites for normal processes when
86  *      you can use "setpriority" for that.
87  *
88  */
89 
90 /* Macros to convert between the unix (lower numerically is higher priority)
91  * and POSIX 1003.1b (higher numerically is higher priority)
92  */
93 
94 #define p4prio_to_rtpprio(P) (RTP_PRIO_MAX - (P))
95 #define rtpprio_to_p4prio(P) (RTP_PRIO_MAX - (P))
96 
97 #define p4prio_to_tsprio(P) ((PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE) - (P))
98 #define tsprio_to_p4prio(P) ((PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE) - (P))
99 
100 /* These improve readability a bit for me:
101  */
102 #define P1B_PRIO_MIN rtpprio_to_p4prio(RTP_PRIO_MAX)
103 #define P1B_PRIO_MAX rtpprio_to_p4prio(RTP_PRIO_MIN)
104 
105 static __inline int
106 getscheduler(struct ksched *ksched, struct thread *td, int *policy)
107 {
108 	struct rtprio rtp;
109 	int e;
110 
111 	e = 0;
112 	pri_to_rtp(td, &rtp);
113 	switch (rtp.type) {
114 	case RTP_PRIO_FIFO:
115 		*policy = SCHED_FIFO;
116 		break;
117 	case RTP_PRIO_REALTIME:
118 		*policy = SCHED_RR;
119 		break;
120 	default:
121 		*policy = SCHED_OTHER;
122 		break;
123 	}
124 	return (e);
125 }
126 
127 int
128 ksched_setparam(struct ksched *ksched,
129     struct thread *td, const struct sched_param *param)
130 {
131 	int e, policy;
132 
133 	e = getscheduler(ksched, td, &policy);
134 	if (e == 0)
135 		e = ksched_setscheduler(ksched, td, policy, param);
136 	return (e);
137 }
138 
139 int
140 ksched_getparam(struct ksched *ksched, struct thread *td,
141     struct sched_param *param)
142 {
143 	struct rtprio rtp;
144 
145 	pri_to_rtp(td, &rtp);
146 	if (RTP_PRIO_IS_REALTIME(rtp.type))
147 		param->sched_priority = rtpprio_to_p4prio(rtp.prio);
148 	else {
149 		if (PRI_MIN_TIMESHARE < rtp.prio)
150 			/*
151 		 	 * The interactive score has it to min realtime
152 			 * so we must show max (64 most likely).
153 			 */
154 			param->sched_priority = PRI_MAX_TIMESHARE -
155 			    PRI_MIN_TIMESHARE;
156 		else
157 			param->sched_priority = tsprio_to_p4prio(rtp.prio);
158 	}
159 	return (0);
160 }
161 
162 /*
163  * XXX The priority and scheduler modifications should
164  *     be moved into published interfaces in kern/kern_sync.
165  *
166  * The permissions to modify process p were checked in "p31b_proc()".
167  *
168  */
169 int
170 ksched_setscheduler(struct ksched *ksched, struct thread *td, int policy,
171     const struct sched_param *param)
172 {
173 	struct rtprio rtp;
174 	int e;
175 
176 	e = 0;
177 	switch(policy) {
178 	case SCHED_RR:
179 	case SCHED_FIFO:
180 		if (param->sched_priority >= P1B_PRIO_MIN &&
181 		    param->sched_priority <= P1B_PRIO_MAX) {
182 			rtp.prio = p4prio_to_rtpprio(param->sched_priority);
183 			rtp.type = (policy == SCHED_FIFO) ? RTP_PRIO_FIFO :
184 			    RTP_PRIO_REALTIME;
185 			rtp_to_pri(&rtp, td);
186 		} else {
187 			e = EINVAL;
188 		}
189 		break;
190 	case SCHED_OTHER:
191 		if (param->sched_priority >= 0 && param->sched_priority <=
192 		    (PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE)) {
193 			rtp.type = RTP_PRIO_NORMAL;
194 			rtp.prio = p4prio_to_tsprio(param->sched_priority);
195 			rtp_to_pri(&rtp, td);
196 		} else {
197 			e = EINVAL;
198 		}
199 		break;
200 	default:
201 		e = EINVAL;
202 		break;
203 	}
204 	return (e);
205 }
206 
207 int
208 ksched_getscheduler(struct ksched *ksched, struct thread *td, int *policy)
209 {
210 
211 	return (getscheduler(ksched, td, policy));
212 }
213 
214 /* ksched_yield: Yield the CPU. */
215 int
216 ksched_yield(struct ksched *ksched)
217 {
218 
219 	sched_relinquish(curthread);
220 	return (0);
221 }
222 
223 int
224 ksched_get_priority_max(struct ksched *ksched, int policy, int *prio)
225 {
226 	int e;
227 
228 	e = 0;
229 	switch (policy)	{
230 	case SCHED_FIFO:
231 	case SCHED_RR:
232 		*prio = P1B_PRIO_MAX;
233 		break;
234 	case SCHED_OTHER:
235 		*prio = PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE;
236 		break;
237 	default:
238 		e = EINVAL;
239 		break;
240 	}
241 	return (e);
242 }
243 
244 int
245 ksched_get_priority_min(struct ksched *ksched, int policy, int *prio)
246 {
247 	int e;
248 
249 	e = 0;
250 	switch (policy)	{
251 	case SCHED_FIFO:
252 	case SCHED_RR:
253 		*prio = P1B_PRIO_MIN;
254 		break;
255 	case SCHED_OTHER:
256 		*prio = 0;
257 		break;
258 	default:
259 		e = EINVAL;
260 		break;
261 	}
262 	return (e);
263 }
264 
265 int
266 ksched_rr_get_interval(struct ksched *ksched, struct thread *td,
267     struct timespec *timespec)
268 {
269 
270 	*timespec = ksched->rr_interval;
271 	return (0);
272 }
273