xref: /freebsd/sys/kern/ksched.c (revision f9218d3d4fd34f082473b3a021c6d4d109fb47cf)
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  * $FreeBSD$
33  */
34 
35 /* ksched: Soft real time scheduling based on "rtprio".
36  */
37 
38 #include "opt_posix.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/proc.h>
45 #include <sys/resource.h>
46 #include <sys/sched.h>
47 
48 #include <posix4/posix4.h>
49 
50 /* ksched: Real-time extension to support POSIX priority scheduling.
51  */
52 
53 struct ksched {
54 	struct timespec rr_interval;
55 };
56 
57 int ksched_attach(struct ksched **p)
58 {
59 	struct ksched *ksched= p31b_malloc(sizeof(*ksched));
60 
61 	ksched->rr_interval.tv_sec = 0;
62 	ksched->rr_interval.tv_nsec = 1000000000L / sched_rr_interval();
63 
64 	*p = ksched;
65 	return 0;
66 }
67 
68 int ksched_detach(struct ksched *ks)
69 {
70 	p31b_free(ks);
71 
72 	return 0;
73 }
74 
75 /*
76  * XXX About priorities
77  *
78  *	POSIX 1003.1b requires that numerically higher priorities be of
79  *	higher priority.  It also permits sched_setparam to be
80  *	implementation defined for SCHED_OTHER.  I don't like
81  *	the notion of inverted priorites for normal processes when
82  *  you can use "setpriority" for that.
83  *
84  *	I'm rejecting sched_setparam for SCHED_OTHER with EINVAL.
85  */
86 
87 /* Macros to convert between the unix (lower numerically is higher priority)
88  * and POSIX 1003.1b (higher numerically is higher priority)
89  */
90 
91 #define p4prio_to_rtpprio(P) (RTP_PRIO_MAX - (P))
92 #define rtpprio_to_p4prio(P) (RTP_PRIO_MAX - (P))
93 
94 /* These improve readability a bit for me:
95  */
96 #define P1B_PRIO_MIN rtpprio_to_p4prio(RTP_PRIO_MAX)
97 #define P1B_PRIO_MAX rtpprio_to_p4prio(RTP_PRIO_MIN)
98 
99 static __inline int
100 getscheduler(register_t *ret, struct ksched *ksched, struct thread *td)
101 {
102 	struct rtprio rtp;
103 	int e = 0;
104 
105 	mtx_lock_spin(&sched_lock);
106 	pri_to_rtp(td->td_ksegrp, &rtp);
107 	mtx_unlock_spin(&sched_lock);
108 	switch (rtp.type)
109 	{
110 		case RTP_PRIO_FIFO:
111 		*ret = SCHED_FIFO;
112 		break;
113 
114 		case RTP_PRIO_REALTIME:
115 		*ret = SCHED_RR;
116 		break;
117 
118 		default:
119 		*ret = SCHED_OTHER;
120 		break;
121 	}
122 
123 	return e;
124 }
125 
126 int ksched_setparam(register_t *ret, struct ksched *ksched,
127 	struct thread *td, const struct sched_param *param)
128 {
129 	register_t policy;
130 	int e;
131 
132 	e = getscheduler(&policy, ksched, td);
133 
134 	if (e == 0)
135 	{
136 		if (policy == SCHED_OTHER)
137 			e = EINVAL;
138 		else
139 			e = ksched_setscheduler(ret, ksched, td, policy, param);
140 	}
141 
142 	return e;
143 }
144 
145 int ksched_getparam(register_t *ret, struct ksched *ksched,
146 	struct thread *td, struct sched_param *param)
147 {
148 	struct rtprio rtp;
149 
150 	mtx_lock_spin(&sched_lock);
151 	pri_to_rtp(td->td_ksegrp, &rtp);
152 	mtx_unlock_spin(&sched_lock);
153 	if (RTP_PRIO_IS_REALTIME(rtp.type))
154 		param->sched_priority = rtpprio_to_p4prio(rtp.prio);
155 
156 	return 0;
157 }
158 
159 /*
160  * XXX The priority and scheduler modifications should
161  *     be moved into published interfaces in kern/kern_sync.
162  *
163  * The permissions to modify process p were checked in "p31b_proc()".
164  *
165  */
166 int ksched_setscheduler(register_t *ret, struct ksched *ksched,
167 	struct thread *td, int policy, const struct sched_param *param)
168 {
169 	int e = 0;
170 	struct rtprio rtp;
171 	struct ksegrp *kg = td->td_ksegrp;
172 
173 	switch(policy)
174 	{
175 		case SCHED_RR:
176 		case SCHED_FIFO:
177 
178 		if (param->sched_priority >= P1B_PRIO_MIN &&
179 		param->sched_priority <= P1B_PRIO_MAX)
180 		{
181 			rtp.prio = p4prio_to_rtpprio(param->sched_priority);
182 			rtp.type = (policy == SCHED_FIFO)
183 				? RTP_PRIO_FIFO : RTP_PRIO_REALTIME;
184 
185 			mtx_lock_spin(&sched_lock);
186 			rtp_to_pri(&rtp, kg);
187 			FOREACH_THREAD_IN_GROUP(kg, td) { /* XXXKSE */
188 				if (TD_IS_RUNNING(td)) {
189 					td->td_flags |= TDF_NEEDRESCHED;
190 				} else if (TD_ON_RUNQ(td)) {
191 					if (td->td_priority > kg->kg_user_pri) {
192 						sched_prio(td, kg->kg_user_pri);
193 					}
194 				}
195 			}
196 			mtx_unlock_spin(&sched_lock);
197 		}
198 		else
199 			e = EPERM;
200 
201 
202 		break;
203 
204 		case SCHED_OTHER:
205 		{
206 			rtp.type = RTP_PRIO_NORMAL;
207 			rtp.prio = p4prio_to_rtpprio(param->sched_priority);
208 			mtx_lock_spin(&sched_lock);
209 			rtp_to_pri(&rtp, kg);
210 
211 			/* XXX Simply revert to whatever we had for last
212 			 *     normal scheduler priorities.
213 			 *     This puts a requirement
214 			 *     on the scheduling code: You must leave the
215 			 *     scheduling info alone.
216 			 */
217 			FOREACH_THREAD_IN_GROUP(kg, td) {
218 				if (TD_IS_RUNNING(td)) {
219 					td->td_flags |= TDF_NEEDRESCHED;
220 				} else if (TD_ON_RUNQ(td)) {
221 					if (td->td_priority > kg->kg_user_pri) {
222 						sched_prio(td, kg->kg_user_pri);
223 					}
224 				}
225 
226 			}
227 			mtx_unlock_spin(&sched_lock);
228 		}
229 		break;
230 	}
231 
232 	return e;
233 }
234 
235 int ksched_getscheduler(register_t *ret, struct ksched *ksched, struct thread *td)
236 {
237 	return getscheduler(ret, ksched, td);
238 }
239 
240 /* ksched_yield: Yield the CPU.
241  */
242 int ksched_yield(register_t *ret, struct ksched *ksched)
243 {
244 	mtx_lock_spin(&sched_lock);
245 	curthread->td_flags |= TDF_NEEDRESCHED;
246 	mtx_unlock_spin(&sched_lock);
247 	return 0;
248 }
249 
250 int ksched_get_priority_max(register_t*ret, struct ksched *ksched, int policy)
251 {
252 	int e = 0;
253 
254 	switch (policy)
255 	{
256 		case SCHED_FIFO:
257 		case SCHED_RR:
258 		*ret = RTP_PRIO_MAX;
259 		break;
260 
261 		case SCHED_OTHER:
262 		*ret =  PRIO_MAX;
263 		break;
264 
265 		default:
266 		e = EINVAL;
267 	}
268 
269 	return e;
270 }
271 
272 int ksched_get_priority_min(register_t *ret, struct ksched *ksched, int policy)
273 {
274 	int e = 0;
275 
276 	switch (policy)
277 	{
278 		case SCHED_FIFO:
279 		case SCHED_RR:
280 		*ret = P1B_PRIO_MIN;
281 		break;
282 
283 		case SCHED_OTHER:
284 		*ret =  PRIO_MIN;
285 		break;
286 
287 		default:
288 		e = EINVAL;
289 	}
290 
291 	return e;
292 }
293 
294 int ksched_rr_get_interval(register_t *ret, struct ksched *ksched,
295 	struct thread *td, struct timespec *timespec)
296 {
297 	*timespec = ksched->rr_interval;
298 
299 	return 0;
300 }
301