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