xref: /freebsd/sys/kern/ksched.c (revision acd3428b7d3e94cef0e1881c868cb4b131d4ff41)
1 /*-
2  * Copyright (c) 1996, 1997
3  *	HD Associates, Inc.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by HD Associates, Inc
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /* ksched: Soft real time scheduling based on "rtprio".
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include "opt_posix.h"
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/proc.h>
46 #include <sys/resource.h>
47 #include <sys/sched.h>
48 
49 #include <posix4/posix4.h>
50 
51 /* ksched: Real-time extension to support POSIX priority scheduling.
52  */
53 
54 struct ksched {
55 	struct timespec rr_interval;
56 };
57 
58 int
59 ksched_attach(struct ksched **p)
60 {
61 	struct ksched *ksched= p31b_malloc(sizeof(*ksched));
62 
63 	ksched->rr_interval.tv_sec = 0;
64 	ksched->rr_interval.tv_nsec = 1000000000L / sched_rr_interval();
65 
66 	*p = ksched;
67 	return 0;
68 }
69 
70 int
71 ksched_detach(struct ksched *ks)
72 {
73 	p31b_free(ks);
74 
75 	return 0;
76 }
77 
78 /*
79  * XXX About priorities
80  *
81  *	POSIX 1003.1b requires that numerically higher priorities be of
82  *	higher priority.  It also permits sched_setparam to be
83  *	implementation defined for SCHED_OTHER.  I don't like
84  *	the notion of inverted priorites for normal processes when
85  *  you can use "setpriority" for that.
86  *
87  *	I'm rejecting sched_setparam for SCHED_OTHER with EINVAL.
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 /* These improve readability a bit for me:
98  */
99 #define P1B_PRIO_MIN rtpprio_to_p4prio(RTP_PRIO_MAX)
100 #define P1B_PRIO_MAX rtpprio_to_p4prio(RTP_PRIO_MIN)
101 
102 static __inline int
103 getscheduler(struct ksched *ksched, struct thread *td, int *policy)
104 {
105 	struct rtprio rtp;
106 	int e = 0;
107 
108 	mtx_lock_spin(&sched_lock);
109 #ifdef KSE
110 	pri_to_rtp(td->td_ksegrp, &rtp);
111 #else
112 	pri_to_rtp(td, &rtp);
113 #endif
114 	mtx_unlock_spin(&sched_lock);
115 	switch (rtp.type)
116 	{
117 		case RTP_PRIO_FIFO:
118 		*policy = SCHED_FIFO;
119 		break;
120 
121 		case RTP_PRIO_REALTIME:
122 		*policy = SCHED_RR;
123 		break;
124 
125 		default:
126 		*policy = SCHED_OTHER;
127 		break;
128 	}
129 
130 	return e;
131 }
132 
133 int
134 ksched_setparam(struct ksched *ksched,
135     struct thread *td, const struct sched_param *param)
136 {
137 	int policy;
138 	int e;
139 
140 	e = getscheduler(ksched, td, &policy);
141 
142 	if (e == 0)
143 	{
144 		if (policy == SCHED_OTHER)
145 			e = EINVAL;
146 		else
147 			e = ksched_setscheduler(ksched, td, policy, param);
148 	}
149 
150 	return e;
151 }
152 
153 int
154 ksched_getparam(struct ksched *ksched,
155     struct thread *td, struct sched_param *param)
156 {
157 	struct rtprio rtp;
158 
159 	mtx_lock_spin(&sched_lock);
160 #ifdef KSE
161 	pri_to_rtp(td->td_ksegrp, &rtp);
162 #else
163 	pri_to_rtp(td, &rtp);
164 #endif
165 	mtx_unlock_spin(&sched_lock);
166 	if (RTP_PRIO_IS_REALTIME(rtp.type))
167 		param->sched_priority = rtpprio_to_p4prio(rtp.prio);
168 
169 	return 0;
170 }
171 
172 /*
173  * XXX The priority and scheduler modifications should
174  *     be moved into published interfaces in kern/kern_sync.
175  *
176  * The permissions to modify process p were checked in "p31b_proc()".
177  *
178  */
179 int
180 ksched_setscheduler(struct ksched *ksched,
181     struct thread *td, int policy, const struct sched_param *param)
182 {
183 	int e = 0;
184 	struct rtprio rtp;
185 #ifdef KSE
186 	struct ksegrp *kg = td->td_ksegrp;
187 #endif
188 
189 	switch(policy)
190 	{
191 		case SCHED_RR:
192 		case SCHED_FIFO:
193 
194 		if (param->sched_priority >= P1B_PRIO_MIN &&
195 		    param->sched_priority <= P1B_PRIO_MAX)
196 		{
197 			rtp.prio = p4prio_to_rtpprio(param->sched_priority);
198 			rtp.type = (policy == SCHED_FIFO)
199 				? RTP_PRIO_FIFO : RTP_PRIO_REALTIME;
200 
201 			mtx_lock_spin(&sched_lock);
202 #ifdef KSE
203 			rtp_to_pri(&rtp, kg);
204 			FOREACH_THREAD_IN_GROUP(kg, td) { /* XXXKSE */
205 				if (TD_IS_RUNNING(td)) {
206 					td->td_flags |= TDF_NEEDRESCHED;
207 				} else if (TD_ON_RUNQ(td)) {
208 					if (td->td_priority > kg->kg_user_pri) {
209 						sched_prio(td, kg->kg_user_pri);
210 					}
211 				}
212 			}
213 #else
214 			rtp_to_pri(&rtp, td);
215 #endif
216 			mtx_unlock_spin(&sched_lock);
217 		}
218 		else
219 			e = EPERM;
220 
221 
222 		break;
223 
224 		case SCHED_OTHER:
225 		{
226 			rtp.type = RTP_PRIO_NORMAL;
227 			rtp.prio = p4prio_to_rtpprio(param->sched_priority);
228 			mtx_lock_spin(&sched_lock);
229 #ifdef KSE
230 			rtp_to_pri(&rtp, kg);
231 
232 			/* XXX Simply revert to whatever we had for last
233 			 *     normal scheduler priorities.
234 			 *     This puts a requirement
235 			 *     on the scheduling code: You must leave the
236 			 *     scheduling info alone.
237 			 */
238 			FOREACH_THREAD_IN_GROUP(kg, td) {
239 				if (TD_IS_RUNNING(td)) {
240 					td->td_flags |= TDF_NEEDRESCHED;
241 				} else if (TD_ON_RUNQ(td)) {
242 					if (td->td_priority > kg->kg_user_pri) {
243 						sched_prio(td, kg->kg_user_pri);
244 					}
245 				}
246 
247 			}
248 #else
249 			rtp_to_pri(&rtp, td);
250 #endif
251 			mtx_unlock_spin(&sched_lock);
252 		}
253 		break;
254 
255 		default:
256 			e = EINVAL;
257 			break;
258 	}
259 
260 	return e;
261 }
262 
263 int
264 ksched_getscheduler(struct ksched *ksched, struct thread *td, int *policy)
265 {
266 	return getscheduler(ksched, td, policy);
267 }
268 
269 /* ksched_yield: Yield the CPU.
270  */
271 int
272 ksched_yield(struct ksched *ksched)
273 {
274 	sched_relinquish(curthread);
275 	return 0;
276 }
277 
278 int
279 ksched_get_priority_max(struct ksched *ksched, int policy, int *prio)
280 {
281 	int e = 0;
282 
283 	switch (policy)
284 	{
285 		case SCHED_FIFO:
286 		case SCHED_RR:
287 		*prio = RTP_PRIO_MAX;
288 		break;
289 
290 		case SCHED_OTHER:
291 		*prio = PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE;
292 		break;
293 
294 		default:
295 		e = EINVAL;
296 	}
297 
298 	return e;
299 }
300 
301 int
302 ksched_get_priority_min(struct ksched *ksched, int policy, int *prio)
303 {
304 	int e = 0;
305 
306 	switch (policy)
307 	{
308 		case SCHED_FIFO:
309 		case SCHED_RR:
310 		*prio = P1B_PRIO_MIN;
311 		break;
312 
313 		case SCHED_OTHER:
314 		*prio = 0;
315 		break;
316 
317 		default:
318 		e = EINVAL;
319 	}
320 
321 	return e;
322 }
323 
324 int
325 ksched_rr_get_interval(struct ksched *ksched,
326    struct thread *td, struct timespec *timespec)
327 {
328 	*timespec = ksched->rr_interval;
329 
330 	return 0;
331 }
332