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