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