1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 *
31 */
32
33/* The routines in this module are all obsolete */
34
35#include <mach/boolean.h>
36#include <mach/thread_switch.h>
37#include <ipc/ipc_port.h>
38#include <ipc/ipc_space.h>
39#include <kern/ipc_kobject.h>
40#include <kern/processor.h>
41#include <kern/sched.h>
42#include <kern/sched_prim.h>
43#include <kern/spl.h>
44#include <kern/task.h>
45#include <kern/thread.h>
46#include <mach/policy.h>
47
48#include <kern/syscall_subr.h>
49#include <mach/mach_host_server.h>
50#include <mach/mach_syscalls.h>
51
52#include <kern/misc_protos.h>
53#include <kern/spl.h>
54#include <kern/sched.h>
55#include <kern/sched_prim.h>
56#include <kern/assert.h>
57#include <kern/thread.h>
58#include <mach/mach_host_server.h>
59#include <mach/thread_act_server.h>
60#include <mach/host_priv_server.h>
61
62/*
63 *	thread_policy_common:
64 *
65 *	Set scheduling policy & priority for thread.
66 */
67static kern_return_t
68thread_policy_common(
69	thread_t		thread,
70	integer_t		policy,
71	integer_t		priority)
72{
73	spl_t			s;
74
75	if (	thread == THREAD_NULL		||
76			invalid_policy(policy)		)
77		return(KERN_INVALID_ARGUMENT);
78
79	if (thread->static_param)
80		return (KERN_SUCCESS);
81
82	if ((policy == POLICY_TIMESHARE)
83		&& !SCHED(supports_timeshare_mode)())
84		policy = TH_MODE_FIXED;
85
86	s = splsched();
87	thread_lock(thread);
88
89	if (	(thread->sched_mode != TH_MODE_REALTIME)	&&
90			(thread->saved_mode != TH_MODE_REALTIME)		) {
91		if (!(thread->sched_flags & TH_SFLAG_DEMOTED_MASK)) {
92			boolean_t	oldmode = thread->sched_mode == TH_MODE_TIMESHARE;
93
94			if (policy == POLICY_TIMESHARE && !oldmode) {
95				thread->sched_mode = TH_MODE_TIMESHARE;
96
97				if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN) {
98					sched_share_incr();
99
100					if (thread->max_priority <= MAXPRI_THROTTLE)
101						sched_background_incr();
102				}
103			}
104			else
105			if (policy != POLICY_TIMESHARE && oldmode) {
106				thread->sched_mode = TH_MODE_FIXED;
107
108				if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN) {
109					if (thread->max_priority <= MAXPRI_THROTTLE)
110						sched_background_decr();
111
112					sched_share_decr();
113				}
114			}
115		}
116		else {
117			if (policy == POLICY_TIMESHARE)
118				thread->saved_mode = TH_MODE_TIMESHARE;
119			else
120				thread->saved_mode = TH_MODE_FIXED;
121		}
122
123		if (priority >= thread->max_priority)
124			priority = thread->max_priority - thread->task_priority;
125		else
126		if (priority >= MINPRI_KERNEL)
127			priority -= MINPRI_KERNEL;
128		else
129		if (priority >= MINPRI_RESERVED)
130			priority -= MINPRI_RESERVED;
131		else
132			priority -= BASEPRI_DEFAULT;
133
134		priority += thread->task_priority;
135
136		if (priority > thread->max_priority)
137			priority = thread->max_priority;
138		else
139		if (priority < MINPRI)
140			priority = MINPRI;
141
142		thread->importance = priority - thread->task_priority;
143
144
145		set_priority(thread, priority);
146	}
147
148	thread_unlock(thread);
149	splx(s);
150
151	return (KERN_SUCCESS);
152}
153
154/*
155 *	thread_set_policy
156 *
157 *	Set scheduling policy and parameters, both base and limit, for
158 *	the given thread. Policy can be any policy implemented by the
159 *	processor set, whether enabled or not.
160 */
161kern_return_t
162thread_set_policy(
163	thread_t				thread,
164	processor_set_t			pset,
165	policy_t				policy,
166	policy_base_t			base,
167	mach_msg_type_number_t	base_count,
168	policy_limit_t			limit,
169	mach_msg_type_number_t	limit_count)
170{
171	int 					max, bas;
172	kern_return_t			result = KERN_SUCCESS;
173
174	if (	thread == THREAD_NULL			||
175			pset == PROCESSOR_SET_NULL || pset != &pset0)
176		return (KERN_INVALID_ARGUMENT);
177
178	thread_mtx_lock(thread);
179
180	switch (policy) {
181
182	case POLICY_RR:
183	{
184		policy_rr_base_t		rr_base = (policy_rr_base_t) base;
185		policy_rr_limit_t		rr_limit = (policy_rr_limit_t) limit;
186
187		if (	base_count != POLICY_RR_BASE_COUNT		||
188				limit_count != POLICY_RR_LIMIT_COUNT		) {
189			result = KERN_INVALID_ARGUMENT;
190			break;
191		}
192
193		bas = rr_base->base_priority;
194		max = rr_limit->max_priority;
195		if (invalid_pri(bas) || invalid_pri(max)) {
196			result = KERN_INVALID_ARGUMENT;
197			break;
198		}
199
200		break;
201	}
202
203	case POLICY_FIFO:
204	{
205		policy_fifo_base_t		fifo_base = (policy_fifo_base_t) base;
206		policy_fifo_limit_t		fifo_limit = (policy_fifo_limit_t) limit;
207
208		if (	base_count != POLICY_FIFO_BASE_COUNT	||
209				limit_count != POLICY_FIFO_LIMIT_COUNT)		{
210			result = KERN_INVALID_ARGUMENT;
211			break;
212		}
213
214		bas = fifo_base->base_priority;
215		max = fifo_limit->max_priority;
216		if (invalid_pri(bas) || invalid_pri(max)) {
217			result = KERN_INVALID_ARGUMENT;
218			break;
219		}
220
221		break;
222	}
223
224	case POLICY_TIMESHARE:
225	{
226		policy_timeshare_base_t		ts_base = (policy_timeshare_base_t) base;
227		policy_timeshare_limit_t	ts_limit =
228						(policy_timeshare_limit_t) limit;
229
230		if (	base_count != POLICY_TIMESHARE_BASE_COUNT		||
231				limit_count != POLICY_TIMESHARE_LIMIT_COUNT			) {
232			result = KERN_INVALID_ARGUMENT;
233			break;
234		}
235
236		bas = ts_base->base_priority;
237		max = ts_limit->max_priority;
238		if (invalid_pri(bas) || invalid_pri(max)) {
239			result = KERN_INVALID_ARGUMENT;
240			break;
241		}
242
243		break;
244	}
245
246	default:
247		result = KERN_INVALID_POLICY;
248	}
249
250	if (result != KERN_SUCCESS) {
251		thread_mtx_unlock(thread);
252
253		return (result);
254	}
255
256	result = thread_policy_common(thread, policy, bas);
257
258	thread_mtx_unlock(thread);
259
260	return (result);
261}
262
263
264/*
265 * 	thread_policy
266 *
267 *	Set scheduling policy and parameters, both base and limit, for
268 *	the given thread. Policy must be a policy which is enabled for the
269 *	processor set. Change contained threads if requested.
270 */
271kern_return_t
272thread_policy(
273	thread_t				thread,
274	policy_t				policy,
275	policy_base_t			base,
276	mach_msg_type_number_t	count,
277	boolean_t				set_limit)
278{
279	kern_return_t			result = KERN_SUCCESS;
280	processor_set_t			pset = &pset0;
281	policy_limit_t			limit = NULL;
282	int						limcount = 0;
283	policy_rr_limit_data_t			rr_limit;
284	policy_fifo_limit_data_t		fifo_limit;
285	policy_timeshare_limit_data_t	ts_limit;
286
287	if (thread == THREAD_NULL)
288		return (KERN_INVALID_ARGUMENT);
289
290	thread_mtx_lock(thread);
291
292	if (	invalid_policy(policy)											||
293			((POLICY_TIMESHARE | POLICY_RR | POLICY_FIFO) & policy) == 0	) {
294		thread_mtx_unlock(thread);
295
296		return (KERN_INVALID_POLICY);
297	}
298
299	if (set_limit) {
300		/*
301	 	 * 	Set scheduling limits to base priority.
302		 */
303		switch (policy) {
304
305		case POLICY_RR:
306		{
307			policy_rr_base_t rr_base;
308
309			if (count != POLICY_RR_BASE_COUNT) {
310				result = KERN_INVALID_ARGUMENT;
311				break;
312			}
313
314			limcount = POLICY_RR_LIMIT_COUNT;
315			rr_base = (policy_rr_base_t) base;
316			rr_limit.max_priority = rr_base->base_priority;
317			limit = (policy_limit_t) &rr_limit;
318
319			break;
320		}
321
322		case POLICY_FIFO:
323		{
324			policy_fifo_base_t fifo_base;
325
326			if (count != POLICY_FIFO_BASE_COUNT) {
327				result = KERN_INVALID_ARGUMENT;
328				break;
329			}
330
331			limcount = POLICY_FIFO_LIMIT_COUNT;
332			fifo_base = (policy_fifo_base_t) base;
333			fifo_limit.max_priority = fifo_base->base_priority;
334			limit = (policy_limit_t) &fifo_limit;
335
336			break;
337		}
338
339		case POLICY_TIMESHARE:
340		{
341			policy_timeshare_base_t ts_base;
342
343			if (count != POLICY_TIMESHARE_BASE_COUNT) {
344				result = KERN_INVALID_ARGUMENT;
345				break;
346			}
347
348			limcount = POLICY_TIMESHARE_LIMIT_COUNT;
349			ts_base = (policy_timeshare_base_t) base;
350			ts_limit.max_priority = ts_base->base_priority;
351			limit = (policy_limit_t) &ts_limit;
352
353			break;
354		}
355
356		default:
357			result = KERN_INVALID_POLICY;
358			break;
359		}
360
361	}
362	else {
363		/*
364		 *	Use current scheduling limits. Ensure that the
365		 *	new base priority will not exceed current limits.
366		 */
367		switch (policy) {
368
369		case POLICY_RR:
370		{
371			policy_rr_base_t rr_base;
372
373			if (count != POLICY_RR_BASE_COUNT) {
374				result = KERN_INVALID_ARGUMENT;
375				break;
376			}
377
378			limcount = POLICY_RR_LIMIT_COUNT;
379			rr_base = (policy_rr_base_t) base;
380			if (rr_base->base_priority > thread->max_priority) {
381				result = KERN_POLICY_LIMIT;
382				break;
383			}
384
385			rr_limit.max_priority = thread->max_priority;
386			limit = (policy_limit_t) &rr_limit;
387
388			break;
389		}
390
391		case POLICY_FIFO:
392		{
393			policy_fifo_base_t fifo_base;
394
395			if (count != POLICY_FIFO_BASE_COUNT) {
396				result = KERN_INVALID_ARGUMENT;
397				break;
398			}
399
400			limcount = POLICY_FIFO_LIMIT_COUNT;
401			fifo_base = (policy_fifo_base_t) base;
402			if (fifo_base->base_priority > thread->max_priority) {
403				result = KERN_POLICY_LIMIT;
404				break;
405			}
406
407			fifo_limit.max_priority = thread->max_priority;
408			limit = (policy_limit_t) &fifo_limit;
409
410			break;
411		}
412
413		case POLICY_TIMESHARE:
414		{
415			policy_timeshare_base_t ts_base;
416
417			if (count != POLICY_TIMESHARE_BASE_COUNT) {
418				result = KERN_INVALID_ARGUMENT;
419				break;
420			}
421
422			limcount = POLICY_TIMESHARE_LIMIT_COUNT;
423			ts_base = (policy_timeshare_base_t) base;
424			if (ts_base->base_priority > thread->max_priority) {
425				result = KERN_POLICY_LIMIT;
426				break;
427			}
428
429			ts_limit.max_priority = thread->max_priority;
430			limit = (policy_limit_t) &ts_limit;
431
432			break;
433		}
434
435		default:
436			result = KERN_INVALID_POLICY;
437			break;
438		}
439
440	}
441
442	thread_mtx_unlock(thread);
443
444	if (result == KERN_SUCCESS)
445	    result = thread_set_policy(thread, pset,
446					 policy, base, count, limit, limcount);
447
448	return(result);
449}
450