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#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
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/sysctl.h>
44#include <sys/kernel.h>
45#include <sys/mutex.h>
46#include <sys/proc.h>
47#include <sys/posix4.h>
48#include <sys/resource.h>
49#include <sys/sched.h>
50
51FEATURE(kposix_priority_scheduling, "POSIX P1003.1B realtime extensions");
52
53/* ksched: Real-time extension to support POSIX priority scheduling. */
54
55struct ksched {
56	struct timespec rr_interval;
57};
58
59int
60ksched_attach(struct ksched **p)
61{
62	struct ksched *ksched;
63
64	ksched = malloc(sizeof(*ksched), M_P31B, M_WAITOK);
65	ksched->rr_interval.tv_sec = 0;
66	ksched->rr_interval.tv_nsec = 1000000000L / hz * sched_rr_interval();
67	*p = ksched;
68	return (0);
69}
70
71int
72ksched_detach(struct ksched *ks)
73{
74
75	free(ks, M_P31B);
76	return (0);
77}
78
79/*
80 * XXX About priorities
81 *
82 *	POSIX 1003.1b requires that numerically higher priorities be of
83 *	higher priority.  It also permits sched_setparam to be
84 *	implementation defined for SCHED_OTHER.  I don't like
85 *	the notion of inverted priorites for normal processes when
86 *      you can use "setpriority" for that.
87 *
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#define p4prio_to_tsprio(P) ((PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE) - (P))
98#define tsprio_to_p4prio(P) ((PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE) - (P))
99
100/* These improve readability a bit for me:
101 */
102#define P1B_PRIO_MIN rtpprio_to_p4prio(RTP_PRIO_MAX)
103#define P1B_PRIO_MAX rtpprio_to_p4prio(RTP_PRIO_MIN)
104
105static __inline int
106getscheduler(struct ksched *ksched, struct thread *td, int *policy)
107{
108	struct rtprio rtp;
109	int e;
110
111	e = 0;
112	pri_to_rtp(td, &rtp);
113	switch (rtp.type) {
114	case RTP_PRIO_FIFO:
115		*policy = SCHED_FIFO;
116		break;
117	case RTP_PRIO_REALTIME:
118		*policy = SCHED_RR;
119		break;
120	default:
121		*policy = SCHED_OTHER;
122		break;
123	}
124	return (e);
125}
126
127int
128ksched_setparam(struct ksched *ksched,
129    struct thread *td, const struct sched_param *param)
130{
131	int e, policy;
132
133	e = getscheduler(ksched, td, &policy);
134	if (e == 0)
135		e = ksched_setscheduler(ksched, td, policy, param);
136	return (e);
137}
138
139int
140ksched_getparam(struct ksched *ksched, struct thread *td,
141    struct sched_param *param)
142{
143	struct rtprio rtp;
144
145	pri_to_rtp(td, &rtp);
146	if (RTP_PRIO_IS_REALTIME(rtp.type))
147		param->sched_priority = rtpprio_to_p4prio(rtp.prio);
148	else {
149		if (PRI_MIN_TIMESHARE < rtp.prio)
150			/*
151		 	 * The interactive score has it to min realtime
152			 * so we must show max (64 most likely).
153			 */
154			param->sched_priority = PRI_MAX_TIMESHARE -
155			    PRI_MIN_TIMESHARE;
156		else
157			param->sched_priority = tsprio_to_p4prio(rtp.prio);
158	}
159	return (0);
160}
161
162/*
163 * XXX The priority and scheduler modifications should
164 *     be moved into published interfaces in kern/kern_sync.
165 *
166 * The permissions to modify process p were checked in "p31b_proc()".
167 *
168 */
169int
170ksched_setscheduler(struct ksched *ksched, struct thread *td, int policy,
171    const struct sched_param *param)
172{
173	struct rtprio rtp;
174	int e;
175
176	e = 0;
177	switch(policy) {
178	case SCHED_RR:
179	case SCHED_FIFO:
180		if (param->sched_priority >= P1B_PRIO_MIN &&
181		    param->sched_priority <= P1B_PRIO_MAX) {
182			rtp.prio = p4prio_to_rtpprio(param->sched_priority);
183			rtp.type = (policy == SCHED_FIFO) ? RTP_PRIO_FIFO :
184			    RTP_PRIO_REALTIME;
185			rtp_to_pri(&rtp, td);
186		} else {
187			e = EPERM;
188		}
189		break;
190	case SCHED_OTHER:
191		if (param->sched_priority >= 0 && param->sched_priority <=
192		    (PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE)) {
193			rtp.type = RTP_PRIO_NORMAL;
194			rtp.prio = p4prio_to_tsprio(param->sched_priority);
195			rtp_to_pri(&rtp, td);
196		} else {
197			e = EINVAL;
198		}
199		break;
200	default:
201		e = EINVAL;
202		break;
203	}
204	return (e);
205}
206
207int
208ksched_getscheduler(struct ksched *ksched, struct thread *td, int *policy)
209{
210
211	return (getscheduler(ksched, td, policy));
212}
213
214/* ksched_yield: Yield the CPU. */
215int
216ksched_yield(struct ksched *ksched)
217{
218
219	sched_relinquish(curthread);
220	return (0);
221}
222
223int
224ksched_get_priority_max(struct ksched *ksched, int policy, int *prio)
225{
226	int e;
227
228	e = 0;
229	switch (policy)	{
230	case SCHED_FIFO:
231	case SCHED_RR:
232		*prio = P1B_PRIO_MAX;
233		break;
234	case SCHED_OTHER:
235		*prio = PRI_MAX_TIMESHARE - PRI_MIN_TIMESHARE;
236		break;
237	default:
238		e = EINVAL;
239		break;
240	}
241	return (e);
242}
243
244int
245ksched_get_priority_min(struct ksched *ksched, int policy, int *prio)
246{
247	int e;
248
249	e = 0;
250	switch (policy)	{
251	case SCHED_FIFO:
252	case SCHED_RR:
253		*prio = P1B_PRIO_MIN;
254		break;
255	case SCHED_OTHER:
256		*prio = 0;
257		break;
258	default:
259		e = EINVAL;
260		break;
261	}
262	return (e);
263}
264
265int
266ksched_rr_get_interval(struct ksched *ksched, struct thread *td,
267    struct timespec *timespec)
268{
269
270	*timespec = ksched->rr_interval;
271	return (0);
272}
273