]> git.saurik.com Git - apple/xnu.git/blame - osfmk/chud/chud_thread.c
xnu-1228.15.4.tar.gz
[apple/xnu.git] / osfmk / chud / chud_thread.c
CommitLineData
0c530ab8 1/*
2d21ac55 2 * Copyright (c) 2003-2007 Apple Inc. All rights reserved.
0c530ab8 3 *
2d21ac55
A
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
0c530ab8
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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@
0c530ab8
A
27 */
28
29#include <mach/mach_types.h>
30#include <mach/task.h>
31#include <mach/thread_act.h>
32
33#include <kern/kern_types.h>
34#include <kern/processor.h>
35#include <kern/thread.h>
36#include <kern/kalloc.h>
37
38#include <chud/chud_xnu.h>
39#include <chud/chud_xnu_private.h>
2d21ac55 40#include <chud/chud_thread.h>
0c530ab8
A
41
42#include <machine/machine_routines.h>
43
2d21ac55
A
44#include <libkern/OSAtomic.h>
45
0c530ab8
A
46// include the correct file to find real_ncpus
47#if defined(__i386__) || defined(__x86_64__)
48# include <i386/mp.h>
2d21ac55 49#elif defined(__ppc__) || defined(__ppc64__)
0c530ab8 50# include <ppc/cpu_internal.h>
2d21ac55
A
51#else
52// fall back on declaring it extern. The linker will sort us out.
53extern unsigned int real_ncpus;
54#endif
55
56// Mask for supported options
57#define T_CHUD_BIND_OPT_MASK (-1UL)
0c530ab8
A
58
59#pragma mark **** thread binding ****
60
2d21ac55
A
61/*
62 * This method will bind a given thread to the requested CPU starting at the
63 * next time quantum. If the thread is the current thread, this method will
64 * force a thread_block(). The result is that if you call this method on the
65 * current thread, you will be on the requested CPU when this method returns.
66 */
0c530ab8 67__private_extern__ kern_return_t
2d21ac55 68chudxnu_bind_thread(thread_t thread, int cpu, __unused int options)
0c530ab8
A
69{
70 processor_t proc = NULL;
2d21ac55
A
71
72 if(cpu < 0 || (unsigned int)cpu >= real_ncpus) // sanity check
0c530ab8 73 return KERN_FAILURE;
2d21ac55
A
74
75 // temporary restriction until after phase 2 of the scheduler
76 if(thread != current_thread())
77 return KERN_FAILURE;
0c530ab8
A
78
79 proc = cpu_to_processor(cpu);
80
2d21ac55
A
81 /*
82 * Potentially racey, but mainly to prevent bind to shutdown
83 * processor.
84 */
0c530ab8 85 if(proc && !(proc->state == PROCESSOR_OFF_LINE) &&
2d21ac55
A
86 !(proc->state == PROCESSOR_SHUTDOWN)) {
87
88 thread_bind(proc);
89
90 /*
91 * If we're trying to bind the current thread, and
92 * we're not on the target cpu, and not at interrupt
93 * context, block the current thread to force a
94 * reschedule on the target CPU.
95 */
96 if(thread == current_thread() &&
97 !(ml_at_interrupt_context() && cpu_number() == cpu)) {
0c530ab8
A
98 (void)thread_block(THREAD_CONTINUE_NULL);
99 }
100 return KERN_SUCCESS;
101 }
102 return KERN_FAILURE;
103}
104
105__private_extern__ kern_return_t
2d21ac55 106chudxnu_unbind_thread(thread_t thread, __unused int options)
0c530ab8 107{
2d21ac55
A
108 if(thread == current_thread())
109 thread_bind(PROCESSOR_NULL);
0c530ab8
A
110 return KERN_SUCCESS;
111}
112
2d21ac55
A
113__private_extern__ boolean_t
114chudxnu_thread_get_idle(thread_t thread) {
115 /*
116 * Instantaneous snapshot of the idle state of
117 * a given thread.
118 *
119 * Should be called only on an interrupted or
120 * suspended thread to avoid a race.
121 */
122 return ((thread->state & TH_IDLE) == TH_IDLE);
123}
124
0c530ab8
A
125#pragma mark **** task and thread info ****
126
2d21ac55
A
127__private_extern__ boolean_t
128chudxnu_is_64bit_task(task_t task)
0c530ab8
A
129{
130 return (task_has_64BitAddr(task));
131}
132
133#define THING_TASK 0
134#define THING_THREAD 1
135
136// an exact copy of processor_set_things() except no mig conversion at the end!
137static kern_return_t
138chudxnu_private_processor_set_things(
139 processor_set_t pset,
140 mach_port_t **thing_list,
141 mach_msg_type_number_t *count,
142 int type)
143{
144 unsigned int actual; /* this many things */
145 unsigned int maxthings;
146 unsigned int i;
147
148 vm_size_t size, size_needed;
149 void *addr;
150
2d21ac55 151 if (pset == PROCESSOR_SET_NULL || pset != &pset0)
0c530ab8
A
152 return (KERN_INVALID_ARGUMENT);
153
2d21ac55 154 size = 0; addr = NULL;
0c530ab8
A
155
156 for (;;) {
2d21ac55 157 mutex_lock(&tasks_threads_lock);
0c530ab8
A
158
159 if (type == THING_TASK)
2d21ac55 160 maxthings = tasks_count;
0c530ab8 161 else
2d21ac55 162 maxthings = threads_count;
0c530ab8
A
163
164 /* do we have the memory we need? */
165
166 size_needed = maxthings * sizeof (mach_port_t);
167 if (size_needed <= size)
168 break;
169
2d21ac55 170 mutex_unlock(&tasks_threads_lock);
0c530ab8
A
171
172 if (size != 0)
173 kfree(addr, size);
174
175 assert(size_needed > 0);
176 size = size_needed;
177
178 addr = kalloc(size);
179 if (addr == 0)
180 return (KERN_RESOURCE_SHORTAGE);
181 }
182
183 /* OK, have memory and the processor_set is locked & active */
184
185 actual = 0;
186 switch (type) {
187
188 case THING_TASK:
189 {
2d21ac55 190 task_t task, *task_list = (task_t *)addr;
0c530ab8 191
2d21ac55
A
192 for (task = (task_t)queue_first(&tasks);
193 !queue_end(&tasks, (queue_entry_t)task);
194 task = (task_t)queue_next(&task->tasks)) {
0c530ab8 195 task_reference_internal(task);
2d21ac55 196 task_list[actual++] = task;
0c530ab8
A
197 }
198
199 break;
200 }
201
202 case THING_THREAD:
203 {
2d21ac55 204 thread_t thread, *thread_list = (thread_t *)addr;
0c530ab8 205
2d21ac55
A
206 for (i = 0, thread = (thread_t)queue_first(&threads);
207 !queue_end(&threads, (queue_entry_t)thread);
208 thread = (thread_t)queue_next(&thread->threads)) {
0c530ab8 209 thread_reference_internal(thread);
2d21ac55 210 thread_list[actual++] = thread;
0c530ab8
A
211 }
212
213 break;
214 }
215 }
216
2d21ac55 217 mutex_unlock(&tasks_threads_lock);
0c530ab8
A
218
219 if (actual < maxthings)
220 size_needed = actual * sizeof (mach_port_t);
221
222 if (actual == 0) {
223 /* no things, so return null pointer and deallocate memory */
2d21ac55 224 *thing_list = NULL;
0c530ab8
A
225 *count = 0;
226
227 if (size != 0)
228 kfree(addr, size);
229 }
230 else {
231 /* if we allocated too much, must copy */
232
233 if (size_needed < size) {
234 void *newaddr;
235
236 newaddr = kalloc(size_needed);
237 if (newaddr == 0) {
238 switch (type) {
239
240 case THING_TASK:
241 {
2d21ac55 242 task_t *task_list = (task_t *)addr;
0c530ab8
A
243
244 for (i = 0; i < actual; i++)
2d21ac55 245 task_deallocate(task_list[i]);
0c530ab8
A
246 break;
247 }
248
249 case THING_THREAD:
250 {
2d21ac55 251 thread_t *thread_list = (thread_t *)addr;
0c530ab8
A
252
253 for (i = 0; i < actual; i++)
2d21ac55 254 thread_deallocate(thread_list[i]);
0c530ab8
A
255 break;
256 }
257 }
258
259 kfree(addr, size);
260 return (KERN_RESOURCE_SHORTAGE);
261 }
262
263 bcopy((void *) addr, (void *) newaddr, size_needed);
264 kfree(addr, size);
265 addr = newaddr;
266 }
267
268 *thing_list = (mach_port_t *)addr;
269 *count = actual;
270 }
271
272 return (KERN_SUCCESS);
273}
274
275// an exact copy of task_threads() except no mig conversion at the end!
276static kern_return_t
277chudxnu_private_task_threads(
278 task_t task,
279 thread_act_array_t *threads_out,
280 mach_msg_type_number_t *count)
281{
282 mach_msg_type_number_t actual;
2d21ac55 283 thread_t *thread_list;
0c530ab8
A
284 thread_t thread;
285 vm_size_t size, size_needed;
286 void *addr;
287 unsigned int i, j;
288
289 if (task == TASK_NULL)
290 return (KERN_INVALID_ARGUMENT);
291
2d21ac55 292 size = 0; addr = NULL;
0c530ab8
A
293
294 for (;;) {
295 task_lock(task);
296 if (!task->active) {
297 task_unlock(task);
298
299 if (size != 0)
300 kfree(addr, size);
301
302 return (KERN_FAILURE);
303 }
304
305 actual = task->thread_count;
306
307 /* do we have the memory we need? */
308 size_needed = actual * sizeof (mach_port_t);
309 if (size_needed <= size)
310 break;
311
312 /* unlock the task and allocate more memory */
313 task_unlock(task);
314
315 if (size != 0)
316 kfree(addr, size);
317
318 assert(size_needed > 0);
319 size = size_needed;
320
321 addr = kalloc(size);
322 if (addr == 0)
323 return (KERN_RESOURCE_SHORTAGE);
324 }
325
326 /* OK, have memory and the task is locked & active */
2d21ac55 327 thread_list = (thread_t *)addr;
0c530ab8
A
328
329 i = j = 0;
330
331 for (thread = (thread_t)queue_first(&task->threads); i < actual;
332 ++i, thread = (thread_t)queue_next(&thread->task_threads)) {
333 thread_reference_internal(thread);
2d21ac55 334 thread_list[j++] = thread;
0c530ab8
A
335 }
336
337 assert(queue_end(&task->threads, (queue_entry_t)thread));
338
339 actual = j;
340 size_needed = actual * sizeof (mach_port_t);
341
342 /* can unlock task now that we've got the thread refs */
343 task_unlock(task);
344
345 if (actual == 0) {
346 /* no threads, so return null pointer and deallocate memory */
347
2d21ac55 348 *threads_out = NULL;
0c530ab8
A
349 *count = 0;
350
351 if (size != 0)
352 kfree(addr, size);
353 }
354 else {
355 /* if we allocated too much, must copy */
356
357 if (size_needed < size) {
358 void *newaddr;
359
360 newaddr = kalloc(size_needed);
361 if (newaddr == 0) {
362 for (i = 0; i < actual; ++i)
2d21ac55 363 thread_deallocate(thread_list[i]);
0c530ab8
A
364 kfree(addr, size);
365 return (KERN_RESOURCE_SHORTAGE);
366 }
367
368 bcopy(addr, newaddr, size_needed);
369 kfree(addr, size);
2d21ac55 370 thread_list = (thread_t *)newaddr;
0c530ab8
A
371 }
372
2d21ac55 373 *threads_out = thread_list;
0c530ab8
A
374 *count = actual;
375 }
376
377 return (KERN_SUCCESS);
378}
379
380
381__private_extern__ kern_return_t
382chudxnu_all_tasks(
383 task_array_t *task_list,
384 mach_msg_type_number_t *count)
385{
2d21ac55 386 return chudxnu_private_processor_set_things(&pset0, (mach_port_t **)task_list, count, THING_TASK);
0c530ab8
A
387}
388
389__private_extern__ kern_return_t
390chudxnu_free_task_list(
391 task_array_t *task_list,
392 mach_msg_type_number_t *count)
393{
394 vm_size_t size = (*count)*sizeof(mach_port_t);
395 void *addr = *task_list;
396
397 if(addr) {
398 int i, maxCount = *count;
399 for(i=0; i<maxCount; i++) {
400 task_deallocate((*task_list)[i]);
401 }
402 kfree(addr, size);
403 *task_list = NULL;
404 *count = 0;
405 return KERN_SUCCESS;
406 } else {
407 return KERN_FAILURE;
408 }
409}
0c530ab8
A
410__private_extern__ kern_return_t
411chudxnu_all_threads(
412 thread_array_t *thread_list,
413 mach_msg_type_number_t *count)
414{
2d21ac55 415 return chudxnu_private_processor_set_things(&pset0, (mach_port_t **)thread_list, count, THING_THREAD);
0c530ab8
A
416}
417
418__private_extern__ kern_return_t
419chudxnu_task_threads(
420 task_t task,
421 thread_array_t *thread_list,
422 mach_msg_type_number_t *count)
423{
424 return chudxnu_private_task_threads(task, thread_list, count);
425}
426
427__private_extern__ kern_return_t
428chudxnu_free_thread_list(
429 thread_array_t *thread_list,
430 mach_msg_type_number_t *count)
431{
432 vm_size_t size = (*count)*sizeof(mach_port_t);
433 void *addr = *thread_list;
434
435 if(addr) {
436 int i, maxCount = *count;
437 for(i=0; i<maxCount; i++) {
438 thread_deallocate((*thread_list)[i]);
439 }
440 kfree(addr, size);
441 *thread_list = NULL;
442 *count = 0;
443 return KERN_SUCCESS;
444 } else {
445 return KERN_FAILURE;
446 }
447}
448
449__private_extern__ task_t
450chudxnu_current_task(void)
451{
452 return current_task();
453}
454
455__private_extern__ thread_t
456chudxnu_current_thread(void)
457{
458 return current_thread();
459}
460
461__private_extern__ task_t
462chudxnu_task_for_thread(thread_t thread)
463{
464 return get_threadtask(thread);
465}
466
467__private_extern__ kern_return_t
468chudxnu_thread_info(
469 thread_t thread,
470 thread_flavor_t flavor,
471 thread_info_t thread_info_out,
472 mach_msg_type_number_t *thread_info_count)
473{
474 return thread_info(thread, flavor, thread_info_out, thread_info_count);
475}
476
2d21ac55 477
0c530ab8
A
478__private_extern__ kern_return_t
479chudxnu_thread_last_context_switch(thread_t thread, uint64_t *timestamp)
480{
481 *timestamp = thread->last_switch;
482 return KERN_SUCCESS;
483}
484
2d21ac55
A
485/* thread marking stuff */
486
487__private_extern__ boolean_t
488chudxnu_thread_get_marked(thread_t thread)
489{
490 if(thread)
491 return ((thread->t_chud & T_CHUD_MARKED) != 0);
492 return FALSE;
493}
494
495__private_extern__ boolean_t
496chudxnu_thread_set_marked(thread_t thread, boolean_t new_value)
497{
498 boolean_t old_val;
499
500 if(thread) {
501 if(new_value) {
502 // set the marked bit
503 old_val = OSBitOrAtomic(T_CHUD_MARKED, (UInt32 *) &(thread->t_chud));
504 } else {
505 // clear the marked bit
506 old_val = OSBitAndAtomic(~T_CHUD_MARKED, (UInt32 *) &(thread->t_chud));
507 }
508 return (old_val & T_CHUD_MARKED) == T_CHUD_MARKED;
509 }
510 return FALSE;
511}
512