]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/stack.c
6b5ea83027170c69662cc9d3642a1667334e37ed
[apple/xnu.git] / osfmk / kern / stack.c
1 /*
2 * Copyright (c) 2003-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 * Kernel stack management routines.
30 */
31
32 #include <mach/mach_host.h>
33 #include <mach/mach_types.h>
34 #include <mach/processor_set.h>
35
36 #include <kern/kern_types.h>
37 #include <kern/mach_param.h>
38 #include <kern/processor.h>
39 #include <kern/thread.h>
40 #include <kern/zalloc.h>
41 #include <kern/kalloc.h>
42
43 #include <vm/vm_map.h>
44 #include <vm/vm_kern.h>
45
46 #include <mach_debug.h>
47
48 /*
49 * We allocate stacks from generic kernel VM.
50 *
51 * The stack_free_list can only be accessed at splsched,
52 * because stack_alloc_try/thread_invoke operate at splsched.
53 */
54
55 decl_simple_lock_data(static,stack_lock_data)
56 #define stack_lock() simple_lock(&stack_lock_data)
57 #define stack_unlock() simple_unlock(&stack_lock_data)
58
59 #define STACK_CACHE_SIZE 2
60
61 static vm_offset_t stack_free_list;
62
63 static unsigned int stack_free_count, stack_free_hiwat; /* free list count */
64 static unsigned int stack_hiwat;
65 unsigned int stack_total; /* current total count */
66 unsigned long long stack_allocs; /* total count of allocations */
67
68 static int stack_fake_zone_index = -1; /* index in zone_info array */
69
70 static unsigned int stack_free_target;
71 static int stack_free_delta;
72
73 static unsigned int stack_new_count; /* total new stack allocations */
74
75 static vm_offset_t stack_addr_mask;
76
77 unsigned int kernel_stack_pages = KERNEL_STACK_SIZE / PAGE_SIZE;
78 vm_offset_t kernel_stack_size = KERNEL_STACK_SIZE;
79 vm_offset_t kernel_stack_mask = -KERNEL_STACK_SIZE;
80 vm_offset_t kernel_stack_depth_max = 0;
81
82 static inline void
83 STACK_ZINFO_PALLOC(thread_t thread)
84 {
85 task_t task;
86 zinfo_usage_t zinfo;
87
88 thread->tkm_private.alloc += kernel_stack_size;
89 if (stack_fake_zone_index != -1 &&
90 (task = thread->task) != NULL && (zinfo = task->tkm_zinfo) != NULL)
91 OSAddAtomic64(kernel_stack_size,
92 (int64_t *)&zinfo[stack_fake_zone_index].alloc);
93 }
94
95 static inline void
96 STACK_ZINFO_PFREE(thread_t thread)
97 {
98 task_t task;
99 zinfo_usage_t zinfo;
100
101 thread->tkm_private.free += kernel_stack_size;
102 if (stack_fake_zone_index != -1 &&
103 (task = thread->task) != NULL && (zinfo = task->tkm_zinfo) != NULL)
104 OSAddAtomic64(kernel_stack_size,
105 (int64_t *)&zinfo[stack_fake_zone_index].free);
106 }
107
108 static inline void
109 STACK_ZINFO_HANDOFF(thread_t from, thread_t to)
110 {
111 from->tkm_private.free += kernel_stack_size;
112 to->tkm_private.alloc += kernel_stack_size;
113 if (stack_fake_zone_index != -1) {
114 task_t task;
115 zinfo_usage_t zinfo;
116
117 if ((task = from->task) != NULL && (zinfo = task->tkm_zinfo) != NULL)
118 OSAddAtomic64(kernel_stack_size,
119 (int64_t *)&zinfo[stack_fake_zone_index].free);
120
121 if ((task = to->task) != NULL && (zinfo = task->tkm_zinfo) != NULL)
122 OSAddAtomic64(kernel_stack_size,
123 (int64_t *)&zinfo[stack_fake_zone_index].alloc);
124 }
125 }
126
127 /*
128 * The next field is at the base of the stack,
129 * so the low end is left unsullied.
130 */
131 #define stack_next(stack) \
132 (*((vm_offset_t *)((stack) + kernel_stack_size) - 1))
133
134 static inline int
135 log2(vm_offset_t size)
136 {
137 int result;
138 for (result = 0; size > 0; result++)
139 size >>= 1;
140 return result;
141 }
142
143 static inline vm_offset_t
144 roundup_pow2(vm_offset_t size)
145 {
146 return 1UL << (log2(size - 1) + 1);
147 }
148
149 static vm_offset_t stack_alloc_internal(void);
150 static void stack_free_stack(vm_offset_t);
151
152 void
153 stack_init(void)
154 {
155 simple_lock_init(&stack_lock_data, 0);
156
157 if (PE_parse_boot_argn("kernel_stack_pages",
158 &kernel_stack_pages,
159 sizeof (kernel_stack_pages))) {
160 kernel_stack_size = kernel_stack_pages * PAGE_SIZE;
161 printf("stack_init: kernel_stack_pages=%d kernel_stack_size=%p\n",
162 kernel_stack_pages, (void *) kernel_stack_size);
163 }
164
165 if (kernel_stack_size < round_page(kernel_stack_size))
166 panic("stack_init: stack size %p not a multiple of page size %d\n",
167 (void *) kernel_stack_size, PAGE_SIZE);
168
169 stack_addr_mask = roundup_pow2(kernel_stack_size) - 1;
170 kernel_stack_mask = ~stack_addr_mask;
171 }
172
173 /*
174 * stack_alloc:
175 *
176 * Allocate a stack for a thread, may
177 * block.
178 */
179
180 static vm_offset_t
181 stack_alloc_internal(void)
182 {
183 vm_offset_t stack;
184 spl_t s;
185 int guard_flags;
186
187 s = splsched();
188 stack_lock();
189 stack_allocs++;
190 stack = stack_free_list;
191 if (stack != 0) {
192 stack_free_list = stack_next(stack);
193 stack_free_count--;
194 }
195 else {
196 if (++stack_total > stack_hiwat)
197 stack_hiwat = stack_total;
198 stack_new_count++;
199 }
200 stack_free_delta--;
201 stack_unlock();
202 splx(s);
203
204 if (stack == 0) {
205
206 /*
207 * Request guard pages on either side of the stack. Ask
208 * kernel_memory_allocate() for two extra pages to account
209 * for these.
210 */
211
212 guard_flags = KMA_GUARD_FIRST | KMA_GUARD_LAST;
213 if (kernel_memory_allocate(kernel_map, &stack,
214 kernel_stack_size + (2*PAGE_SIZE),
215 stack_addr_mask,
216 KMA_KOBJECT | guard_flags)
217 != KERN_SUCCESS)
218 panic("stack_alloc: kernel_memory_allocate");
219
220 /*
221 * The stack address that comes back is the address of the lower
222 * guard page. Skip past it to get the actual stack base address.
223 */
224
225 stack += PAGE_SIZE;
226 }
227 return stack;
228 }
229
230 void
231 stack_alloc(
232 thread_t thread)
233 {
234
235 assert(thread->kernel_stack == 0);
236 machine_stack_attach(thread, stack_alloc_internal());
237 STACK_ZINFO_PALLOC(thread);
238 }
239
240 void
241 stack_handoff(thread_t from, thread_t to)
242 {
243 assert(from == current_thread());
244 machine_stack_handoff(from, to);
245 STACK_ZINFO_HANDOFF(from, to);
246 }
247
248 /*
249 * stack_free:
250 *
251 * Detach and free the stack for a thread.
252 */
253 void
254 stack_free(
255 thread_t thread)
256 {
257 vm_offset_t stack = machine_stack_detach(thread);
258
259 assert(stack);
260 if (stack != thread->reserved_stack) {
261 STACK_ZINFO_PFREE(thread);
262 stack_free_stack(stack);
263 }
264 }
265
266 void
267 stack_free_reserved(
268 thread_t thread)
269 {
270 if (thread->reserved_stack != thread->kernel_stack) {
271 stack_free_stack(thread->reserved_stack);
272 STACK_ZINFO_PFREE(thread);
273 }
274 }
275
276 static void
277 stack_free_stack(
278 vm_offset_t stack)
279 {
280 struct stack_cache *cache;
281 spl_t s;
282
283 s = splsched();
284 cache = &PROCESSOR_DATA(current_processor(), stack_cache);
285 if (cache->count < STACK_CACHE_SIZE) {
286 stack_next(stack) = cache->free;
287 cache->free = stack;
288 cache->count++;
289 }
290 else {
291 stack_lock();
292 stack_next(stack) = stack_free_list;
293 stack_free_list = stack;
294 if (++stack_free_count > stack_free_hiwat)
295 stack_free_hiwat = stack_free_count;
296 stack_free_delta++;
297 stack_unlock();
298 }
299 splx(s);
300 }
301
302 /*
303 * stack_alloc_try:
304 *
305 * Non-blocking attempt to allocate a
306 * stack for a thread.
307 *
308 * Returns TRUE on success.
309 *
310 * Called at splsched.
311 */
312 boolean_t
313 stack_alloc_try(
314 thread_t thread)
315 {
316 struct stack_cache *cache;
317 vm_offset_t stack;
318
319 cache = &PROCESSOR_DATA(current_processor(), stack_cache);
320 stack = cache->free;
321 if (stack != 0) {
322 STACK_ZINFO_PALLOC(thread);
323 cache->free = stack_next(stack);
324 cache->count--;
325 }
326 else {
327 if (stack_free_list != 0) {
328 stack_lock();
329 stack = stack_free_list;
330 if (stack != 0) {
331 STACK_ZINFO_PALLOC(thread);
332 stack_free_list = stack_next(stack);
333 stack_free_count--;
334 stack_free_delta--;
335 }
336 stack_unlock();
337 }
338 }
339
340 if (stack != 0 || (stack = thread->reserved_stack) != 0) {
341 machine_stack_attach(thread, stack);
342 return (TRUE);
343 }
344
345 return (FALSE);
346 }
347
348 static unsigned int stack_collect_tick, last_stack_tick;
349
350 /*
351 * stack_collect:
352 *
353 * Free excess kernel stacks, may
354 * block.
355 */
356 void
357 stack_collect(void)
358 {
359 if (stack_collect_tick != last_stack_tick) {
360 unsigned int target;
361 vm_offset_t stack;
362 spl_t s;
363
364 s = splsched();
365 stack_lock();
366
367 target = stack_free_target + (STACK_CACHE_SIZE * processor_count);
368 target += (stack_free_delta >= 0)? stack_free_delta: -stack_free_delta;
369
370 while (stack_free_count > target) {
371 stack = stack_free_list;
372 stack_free_list = stack_next(stack);
373 stack_free_count--; stack_total--;
374 stack_unlock();
375 splx(s);
376
377 /*
378 * Get the stack base address, then decrement by one page
379 * to account for the lower guard page. Add two extra pages
380 * to the size to account for the guard pages on both ends
381 * that were originally requested when the stack was allocated
382 * back in stack_alloc().
383 */
384
385 stack = (vm_offset_t)vm_map_trunc_page(stack);
386 stack -= PAGE_SIZE;
387 if (vm_map_remove(
388 kernel_map,
389 stack,
390 stack + kernel_stack_size+(2*PAGE_SIZE),
391 VM_MAP_REMOVE_KUNWIRE)
392 != KERN_SUCCESS)
393 panic("stack_collect: vm_map_remove");
394 stack = 0;
395
396 s = splsched();
397 stack_lock();
398
399 target = stack_free_target + (STACK_CACHE_SIZE * processor_count);
400 target += (stack_free_delta >= 0)? stack_free_delta: -stack_free_delta;
401 }
402
403 last_stack_tick = stack_collect_tick;
404
405 stack_unlock();
406 splx(s);
407 }
408 }
409
410 /*
411 * compute_stack_target:
412 *
413 * Computes a new target free list count
414 * based on recent alloc / free activity.
415 *
416 * Limits stack collection to once per
417 * computation period.
418 */
419 void
420 compute_stack_target(
421 __unused void *arg)
422 {
423 spl_t s;
424
425 s = splsched();
426 stack_lock();
427
428 if (stack_free_target > 5)
429 stack_free_target = (4 * stack_free_target) / 5;
430 else
431 if (stack_free_target > 0)
432 stack_free_target--;
433
434 stack_free_target += (stack_free_delta >= 0)? stack_free_delta: -stack_free_delta;
435
436 stack_free_delta = 0;
437 stack_collect_tick++;
438
439 stack_unlock();
440 splx(s);
441 }
442
443 void
444 stack_fake_zone_init(int zone_index)
445 {
446 stack_fake_zone_index = zone_index;
447 }
448
449 void
450 stack_fake_zone_info(int *count,
451 vm_size_t *cur_size, vm_size_t *max_size, vm_size_t *elem_size, vm_size_t *alloc_size,
452 uint64_t *sum_size, int *collectable, int *exhaustable, int *caller_acct)
453 {
454 unsigned int total, hiwat, free;
455 unsigned long long all;
456 spl_t s;
457
458 s = splsched();
459 stack_lock();
460 all = stack_allocs;
461 total = stack_total;
462 hiwat = stack_hiwat;
463 free = stack_free_count;
464 stack_unlock();
465 splx(s);
466
467 *count = total - free;
468 *cur_size = kernel_stack_size * total;
469 *max_size = kernel_stack_size * hiwat;
470 *elem_size = kernel_stack_size;
471 *alloc_size = kernel_stack_size;
472 *sum_size = all * kernel_stack_size;
473
474 *collectable = 1;
475 *exhaustable = 0;
476 *caller_acct = 1;
477 }
478
479 /* OBSOLETE */
480 void stack_privilege(
481 thread_t thread);
482
483 void
484 stack_privilege(
485 __unused thread_t thread)
486 {
487 /* OBSOLETE */
488 }
489
490 /*
491 * Return info on stack usage for threads in a specific processor set
492 */
493 kern_return_t
494 processor_set_stack_usage(
495 processor_set_t pset,
496 unsigned int *totalp,
497 vm_size_t *spacep,
498 vm_size_t *residentp,
499 vm_size_t *maxusagep,
500 vm_offset_t *maxstackp)
501 {
502 #if !MACH_DEBUG
503 return KERN_NOT_SUPPORTED;
504 #else
505 unsigned int total;
506 vm_size_t maxusage;
507 vm_offset_t maxstack;
508
509 register thread_t *thread_list;
510 register thread_t thread;
511
512 unsigned int actual; /* this many things */
513 unsigned int i;
514
515 vm_size_t size, size_needed;
516 void *addr;
517
518 if (pset == PROCESSOR_SET_NULL || pset != &pset0)
519 return KERN_INVALID_ARGUMENT;
520
521 size = 0;
522 addr = NULL;
523
524 for (;;) {
525 lck_mtx_lock(&tasks_threads_lock);
526
527 actual = threads_count;
528
529 /* do we have the memory we need? */
530
531 size_needed = actual * sizeof(thread_t);
532 if (size_needed <= size)
533 break;
534
535 lck_mtx_unlock(&tasks_threads_lock);
536
537 if (size != 0)
538 kfree(addr, size);
539
540 assert(size_needed > 0);
541 size = size_needed;
542
543 addr = kalloc(size);
544 if (addr == 0)
545 return KERN_RESOURCE_SHORTAGE;
546 }
547
548 /* OK, have memory and list is locked */
549 thread_list = (thread_t *) addr;
550 for (i = 0, thread = (thread_t) queue_first(&threads);
551 !queue_end(&threads, (queue_entry_t) thread);
552 thread = (thread_t) queue_next(&thread->threads)) {
553 thread_reference_internal(thread);
554 thread_list[i++] = thread;
555 }
556 assert(i <= actual);
557
558 lck_mtx_unlock(&tasks_threads_lock);
559
560 /* calculate maxusage and free thread references */
561
562 total = 0;
563 maxusage = 0;
564 maxstack = 0;
565 while (i > 0) {
566 thread_t threadref = thread_list[--i];
567
568 if (threadref->kernel_stack != 0)
569 total++;
570
571 thread_deallocate(threadref);
572 }
573
574 if (size != 0)
575 kfree(addr, size);
576
577 *totalp = total;
578 *residentp = *spacep = total * round_page(kernel_stack_size);
579 *maxusagep = maxusage;
580 *maxstackp = maxstack;
581 return KERN_SUCCESS;
582
583 #endif /* MACH_DEBUG */
584 }
585
586 vm_offset_t min_valid_stack_address(void)
587 {
588 return (vm_offset_t)vm_map_min(kernel_map);
589 }
590
591 vm_offset_t max_valid_stack_address(void)
592 {
593 return (vm_offset_t)vm_map_max(kernel_map);
594 }