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