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 pri_to_rtp(td->td_ksegrp, &rtp); 110 mtx_unlock_spin(&sched_lock); 111 switch (rtp.type) 112 { 113 case RTP_PRIO_FIFO: 114 *policy = SCHED_FIFO; 115 break; 116 117 case RTP_PRIO_REALTIME: 118 *policy = SCHED_RR; 119 break; 120 121 default: 122 *policy = SCHED_OTHER; 123 break; 124 } 125 126 return e; 127 } 128 129 int 130 ksched_setparam(struct ksched *ksched, 131 struct thread *td, const struct sched_param *param) 132 { 133 int policy; 134 int e; 135 136 e = getscheduler(ksched, td, &policy); 137 138 if (e == 0) 139 { 140 if (policy == SCHED_OTHER) 141 e = EINVAL; 142 else 143 e = ksched_setscheduler(ksched, td, policy, param); 144 } 145 146 return e; 147 } 148 149 int 150 ksched_getparam(struct ksched *ksched, 151 struct thread *td, struct sched_param *param) 152 { 153 struct rtprio rtp; 154 155 mtx_lock_spin(&sched_lock); 156 pri_to_rtp(td->td_ksegrp, &rtp); 157 mtx_unlock_spin(&sched_lock); 158 if (RTP_PRIO_IS_REALTIME(rtp.type)) 159 param->sched_priority = rtpprio_to_p4prio(rtp.prio); 160 161 return 0; 162 } 163 164 /* 165 * XXX The priority and scheduler modifications should 166 * be moved into published interfaces in kern/kern_sync. 167 * 168 * The permissions to modify process p were checked in "p31b_proc()". 169 * 170 */ 171 int 172 ksched_setscheduler(struct ksched *ksched, 173 struct thread *td, int policy, const struct sched_param *param) 174 { 175 int e = 0; 176 struct rtprio rtp; 177 struct ksegrp *kg = td->td_ksegrp; 178 179 switch(policy) 180 { 181 case SCHED_RR: 182 case SCHED_FIFO: 183 184 if (param->sched_priority >= P1B_PRIO_MIN && 185 param->sched_priority <= P1B_PRIO_MAX) 186 { 187 rtp.prio = p4prio_to_rtpprio(param->sched_priority); 188 rtp.type = (policy == SCHED_FIFO) 189 ? RTP_PRIO_FIFO : RTP_PRIO_REALTIME; 190 191 mtx_lock_spin(&sched_lock); 192 rtp_to_pri(&rtp, kg); 193 FOREACH_THREAD_IN_GROUP(kg, td) { /* XXXKSE */ 194 if (TD_IS_RUNNING(td)) { 195 td->td_flags |= TDF_NEEDRESCHED; 196 } else if (TD_ON_RUNQ(td)) { 197 if (td->td_priority > kg->kg_user_pri) { 198 sched_prio(td, kg->kg_user_pri); 199 } 200 } 201 } 202 mtx_unlock_spin(&sched_lock); 203 } 204 else 205 e = EPERM; 206 207 208 break; 209 210 case SCHED_OTHER: 211 { 212 rtp.type = RTP_PRIO_NORMAL; 213 rtp.prio = p4prio_to_rtpprio(param->sched_priority); 214 mtx_lock_spin(&sched_lock); 215 rtp_to_pri(&rtp, kg); 216 217 /* XXX Simply revert to whatever we had for last 218 * normal scheduler priorities. 219 * This puts a requirement 220 * on the scheduling code: You must leave the 221 * scheduling info alone. 222 */ 223 FOREACH_THREAD_IN_GROUP(kg, td) { 224 if (TD_IS_RUNNING(td)) { 225 td->td_flags |= TDF_NEEDRESCHED; 226 } else if (TD_ON_RUNQ(td)) { 227 if (td->td_priority > kg->kg_user_pri) { 228 sched_prio(td, kg->kg_user_pri); 229 } 230 } 231 232 } 233 mtx_unlock_spin(&sched_lock); 234 } 235 break; 236 237 default: 238 e = EINVAL; 239 break; 240 } 241 242 return e; 243 } 244 245 int 246 ksched_getscheduler(struct ksched *ksched, struct thread *td, int *policy) 247 { 248 return getscheduler(ksched, td, policy); 249 } 250 251 /* ksched_yield: Yield the CPU. 252 */ 253 int 254 ksched_yield(struct ksched *ksched) 255 { 256 sched_relinquish(curthread); 257 return 0; 258 } 259 260 int 261 ksched_get_priority_max(struct ksched *ksched, int policy, int *prio) 262 { 263 int e = 0; 264 265 switch (policy) 266 { 267 case SCHED_FIFO: 268 case SCHED_RR: 269 *prio = RTP_PRIO_MAX; 270 break; 271 272 case SCHED_OTHER: 273 *prio = PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE; 274 break; 275 276 default: 277 e = EINVAL; 278 } 279 280 return e; 281 } 282 283 int 284 ksched_get_priority_min(struct ksched *ksched, int policy, int *prio) 285 { 286 int e = 0; 287 288 switch (policy) 289 { 290 case SCHED_FIFO: 291 case SCHED_RR: 292 *prio = P1B_PRIO_MIN; 293 break; 294 295 case SCHED_OTHER: 296 *prio = 0; 297 break; 298 299 default: 300 e = EINVAL; 301 } 302 303 return e; 304 } 305 306 int 307 ksched_rr_get_interval(struct ksched *ksched, 308 struct thread *td, struct timespec *timespec) 309 { 310 *timespec = ksched->rr_interval; 311 312 return 0; 313 } 314