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