Annotation of kernel/kern/kernel_stack.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
                      3:  *
                      4:  * @APPLE_LICENSE_HEADER_START@
                      5:  * 
                      6:  * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
                      7:  * Reserved.  This file contains Original Code and/or Modifications of
                      8:  * Original Code as defined in and that are subject to the Apple Public
                      9:  * Source License Version 1.1 (the "License").  You may not use this file
                     10:  * except in compliance with the License.  Please obtain a copy of the
                     11:  * License at http://www.apple.com/publicsource and read it before using
                     12:  * this file.
                     13:  * 
                     14:  * The Original Code and all software distributed under the License are
                     15:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
                     16:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
                     17:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
                     18:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
                     19:  * License for the specific language governing rights and limitations
                     20:  * under the License.
                     21:  * 
                     22:  * @APPLE_LICENSE_HEADER_END@
                     23:  */
                     24: 
                     25: /* 
                     26:  * Copyright (c) 1990 NeXT, Inc.
                     27:  *
                     28:  *  History:
                     29:  *
                     30:  *     18-Jul-90: Brian Pinkerton at NeXT
                     31:  *             created
                     32:  */
                     33:  
                     34: /*
                     35:  *  Kernel stack module: handle the allocation, swapping (unwiring) and
                     36:  *  freeing of thread kernel stacks.  Kernel stacks are allocated from whole
                     37:  *  pages. They may only cross page boundaries if they are bigger than a page.
                     38:  *  The constant KERNEL_STACK_SIZE (kernel_stack.h) defines the actual size of
                     39:  *  kernel stacks.
                     40:  *
                     41:  *  The address of a kernel stack given to a client differs from the actual
                     42:  *  address of the stack.  Internally, we prepend a struct _kernelStack to
                     43:  *  every stack we return to the user.  This structure allows us to queue the
                     44:  *  stacks, and maintain information about the status of the stack.
                     45:  *
                     46:  *  Access to the stack structures is protected by a single lock.  We need
                     47:  *  a sleep lock because certain functions we call can block (for example,
                     48:  *  vm_map_pageable).  Ultimately, this module should use finer granularity
                     49:  *  locks -- one on the chunk of stacks, one on an individual stack, and an
                     50:  *  infrequently used one on the entire system.
                     51:  *
                     52:  *  Exported routines:
                     53:  *
                     54:  *  void initKernelStacks()              initializes this module
                     55:  *  vm_offset_t allocStack()             allocate a new kernel stack
                     56:  *  void freeStack(vm_offset_t stack)    free a previously alloc'ed kernel stack
                     57:  *  void swapOutStack(vm_offset_t stack)  try to swap out a stack
                     58:  *  void swapInStack(vm_offset_t stack)   swap in a stack
                     59:  *
                     60:  *  Internal routines:
                     61:  *
                     62:  *  vm_offset_t newStack():              allocate new stacks from new memory
                     63:  *  void enterFreeList(vm_offset_t stack) put a stack on the free list
                     64:  *  void checkFreeList(vm_offset_t stack) try to free (or swap) an entire page
                     65:  *  int canSwap(vm_offset_t stack)       return TRUE if we can swap this stack
                     66:  *  void doSwapout(vm_offset_t stack)    really swap out this stack
                     67:  */
                     68: 
                     69: #import <mach_debug.h>
                     70: 
                     71: #import <kern/queue.h>
                     72: #import <kern/thread.h>
                     73: #import <kern/kernel_stack.h>
                     74: #import <kern/sched_prim.h>
                     75: #import <mach/vm_param.h>
                     76: #import <vm/vm_map.h>
                     77: #import <vm/vm_kern.h>
                     78: 
                     79: #import <kern/assert.h>
                     80: 
                     81: /*
                     82:  *  Internal prototypes
                     83:  */
                     84: vm_offset_t newStack();
                     85: static __inline__ vm_offset_t _allocStack(boolean_t canblock);
                     86: static void enterFreeList(vm_offset_t stack);
                     87: static void checkFreeList(vm_offset_t stack);
                     88: int canSwap(vm_offset_t stack);
                     89: void doSwapout(vm_offset_t stack);
                     90: 
                     91: /*
                     92:  *  Data structures
                     93:  */
                     94: static queue_head_t    stack_queue;
                     95: lock_data_t            stack_queue_lock;
                     96: 
                     97: static int             kernelStackBlock;       /* actual size of stack */
                     98: static int             stacksPerPage;          /* can be <= 1 */
                     99: static int             stack_free_count = 0;   /* number actually free */
                    100: static int             stack_free_target = 8;  /* number we want free */
                    101: static boolean_t       need_stack_wakeup = FALSE;/* if true, notify that
                    102:                                                   * stacks are available */
                    103: 
                    104: struct stackStats {
                    105:        int     allocatedChunks;    /* space alloc'ed from the kernel map */
                    106:        int     allocatedStacks;    /* total number of allocated stacks */
                    107:        int     freeStacks;         /* number of stacks on the free list */
                    108:        int     swappableStacks;    /* number of stacks marked swappable */
                    109:        int     swappedChunks;      /* number of pages swapped out */
                    110: } stackStats;
                    111: 
                    112: 
                    113: /*
                    114:  *  kstack_init: initialize the kernel stack data structures
                    115:  */
                    116: void
                    117: initKernelStacks()
                    118: {      
                    119:        queue_init(&stack_queue);
                    120:        lock_init(&stack_queue_lock, TRUE);
                    121:        
                    122:        kernelStackBlock = KERNEL_STACK_SIZE + sizeof(struct _kernelStack);
                    123:        stacksPerPage = (page_size + kernelStackBlock - 1) / kernelStackBlock;
                    124: }
                    125: 
                    126: 
                    127: /*
                    128:  *  enterFreeList: enter a stack on the free list, marking it free in the process
                    129:  */
                    130: static void
                    131: enterFreeList(vm_offset_t stack)
                    132: {
                    133:        /*
                    134:         *  Put the guy on the free list
                    135:         */
                    136:        ((kernelStack) stack)->status = STACK_FREE;
                    137:        queue_enter(&stack_queue, (kernelStack) stack, kernelStack, freeList);
                    138:        stack_free_count ++;
                    139:        stackStats.freeStacks++;
                    140: }
                    141: 
                    142: 
                    143: /*
                    144:  *  checkFreeList: try to free up a chunk of memory of all stacks in that chunk
                    145:  *  are free.
                    146:  */
                    147: static void
                    148: checkFreeList(vm_offset_t stack)
                    149: {
                    150:        int i, freeAll;
                    151:        vm_offset_t page = trunc_page(stack);
                    152:        vm_offset_t thisStack;
                    153:        
                    154:        /*
                    155:         *  Determine if we should free a page by checking the status of each
                    156:         *  stack on a page.
                    157:         */
                    158:        thisStack = page;
                    159:        freeAll = TRUE;
                    160:        for (i = 0; i < stacksPerPage; i++) {
                    161:                if ( ((kernelStack) thisStack)->status != STACK_FREE )
                    162:                        freeAll = FALSE;
                    163:                        
                    164:                thisStack += kernelStackBlock;
                    165:        }
                    166:        
                    167:        if (!freeAll) {
                    168:                if (canSwap(stack))
                    169:                        doSwapout(stack);
                    170:                return;
                    171:        }
                    172: 
                    173:        /*
                    174:         *  We should free the page, so go through and remove all the stacks
                    175:         *  on this page from the free list, then free the page.
                    176:         */
                    177:        thisStack = page;
                    178:        for (i = 0; i < stacksPerPage; i++) {
                    179:                queue_remove(&stack_queue, (kernelStack) thisStack, kernelStack, freeList);
                    180:                stack_free_count--;
                    181:                stackStats.freeStacks--;
                    182: 
                    183: #if    MACH_DEBUG
                    184:                stack_finalize(thisStack + sizeof (struct _kernelStack));
                    185: #endif /* MACH_DEBUG */
                    186:                        
                    187:                thisStack += kernelStackBlock;
                    188:        }
                    189: 
                    190:        kmem_free(kernel_map, stack, kernelStackBlock);
                    191:        stackStats.allocatedChunks--;
                    192: }
                    193: 
                    194: 
                    195: /*
                    196:  *  freeStack: free up a kernel stack
                    197:  *
                    198:  *  We put the stack on the free list, then check all items on that page to see if
                    199:  *  they can be freed.  If so, we remove all the stacks on the page from the free
                    200:  *  list and free the page.
                    201:  *  
                    202:  *  A further optimization would be to try to swap the page if only free stacks and
                    203:  *  swapped stacks remained on the page.
                    204:  */
                    205: void
                    206: freeStack(vm_offset_t stack)
                    207: {      
                    208:        stackStats.allocatedStacks--;
                    209:        stack -= sizeof(struct _kernelStack);
                    210: 
                    211:        assert(((kernelStack) stack)->status == STACK_IN_USE);
                    212:                
                    213:        lock_write(&stack_queue_lock);
                    214:        enterFreeList(stack);
                    215:        lock_done(&stack_queue_lock);
                    216:        
                    217:        /*
                    218:         *  Try to keep some stacks free so not everyone goes through the pain of
                    219:         *  allocation.
                    220:         */
                    221:        if (need_stack_wakeup) {
                    222:                need_stack_wakeup = FALSE;
                    223:                thread_wakeup(&stack_queue);
                    224:        }
                    225:        
                    226:        if (stack_free_count <= stack_free_target)
                    227:                return;
                    228:                
                    229:        checkFreeList(stack);
                    230: }
                    231: 
                    232: 
                    233: /*
                    234:  *  newStack: allocate a new kernel stack
                    235:  *
                    236:  *  Here, we just allocate a new page and break it up into its constituent stacks.
                    237:  *  One stack (the first in the chunk) is returned as the new stack, and the
                    238:  *  remaining ones are marked as free and put on the free list.
                    239:  */
                    240: vm_offset_t
                    241: newStack()
                    242: {
                    243:        vm_offset_t newPage, stack;
                    244:        int i;
                    245:        
                    246:        if (kmem_alloc_wired(kernel_map,
                    247:                                &newPage, kernelStackBlock) != KERN_SUCCESS)
                    248:                return 0;
                    249: 
                    250:        stackStats.allocatedChunks++;
                    251:                
                    252:        ((kernelStack) newPage)->status = STACK_IN_USE;
                    253:        
                    254:        stackStats.allocatedStacks++;
                    255: 
                    256: #if    MACH_DEBUG
                    257:        stack_init(newPage + sizeof (struct _kernelStack));
                    258: #endif /* MACH_DEBUG */
                    259: 
                    260:        if (stacksPerPage <= 1)
                    261:                return newPage + sizeof(struct _kernelStack);
                    262:        
                    263:        /*
                    264:         *  Return the first guy on the page as our stack, and create
                    265:         *  free stacks out of the rest of the slots on the page.
                    266:         */
                    267:        lock_write(&stack_queue_lock);
                    268:        stack = newPage + kernelStackBlock;
                    269:        for (i = 1; i < stacksPerPage; i++) {
                    270: #if    MACH_DEBUG
                    271:                stack_init(stack + sizeof (struct _kernelStack));
                    272: #endif /* MACH_DEBUG */
                    273:                enterFreeList(stack);
                    274:                stack += kernelStackBlock;
                    275:        }
                    276:        lock_done(&stack_queue_lock);
                    277:        
                    278:        return newPage + sizeof(struct _kernelStack);
                    279: }
                    280: 
                    281: 
                    282: /*
                    283:  *  allocStack: allocate and return a kernel stack (was stack_alloc)
                    284:  *
                    285:  *  Try to grab a free stack off the list of free stacks.  If that fails, get
                    286:  *  a new stack.  If that fails (unlikely), fall asleep and wait for someone to
                    287:  *  free a stack.
                    288:  *
                    289:  *  Return the address of the new stack.
                    290:  */
                    291: static __inline__
                    292: vm_offset_t
                    293: _allocStack(
                    294:        boolean_t       canblock
                    295: )
                    296: {
                    297:        register vm_offset_t    stack;
                    298:        register boolean_t      msg_printed = FALSE;
                    299:        register kern_return_t  result = THREAD_AWAKENED;
                    300: 
                    301:        do {
                    302:            lock_write(&stack_queue_lock);
                    303:            if (stack_free_count != 0) {
                    304:                stack = (vm_offset_t) dequeue_head(&stack_queue);
                    305:                ((kernelStack) stack)->status = STACK_IN_USE;
                    306:                stack += sizeof(struct _kernelStack);
                    307:                stack_free_count--;
                    308:                stackStats.freeStacks--;
                    309:                stackStats.allocatedStacks++;
                    310:            } else {
                    311:                stack = (vm_offset_t)0;
                    312:            }
                    313:            lock_done(&stack_queue_lock);
                    314:            
                    315:            if (!canblock)
                    316:                return (stack);
                    317: 
                    318:            /*
                    319:             *  If no stacks on queue, allocate one.  If that fails,
                    320:             *  pause and wait for a stack to be freed.
                    321:             */
                    322:            if (stack == (vm_offset_t)0)
                    323:                stack = newStack();
                    324: 
                    325:            if (stack == (vm_offset_t)0) {
                    326:                if (!msg_printed ) {
                    327:                    msg_printed = TRUE;
                    328:                    uprintf("MACH: Out of kernel stacks, pausing...");
                    329:                    if (!need_stack_wakeup)
                    330:                        printf("stack_alloc: Kernel stacks exhausted\n");
                    331:                }
                    332:                else if (result != THREAD_AWAKENED) {
                    333:                    /*
                    334:                     *  Somebody wants us; return a bogus stack.
                    335:                     */
                    336:                    return((vm_offset_t)0);
                    337:                }
                    338: 
                    339:                /*
                    340:                 *      Now wait for stack, but first make sure one
                    341:                 *      hasn't appeared in the interim.
                    342:                 */
                    343:                lock_write(&stack_queue_lock);
                    344:                if(stack_free_count != 0) {
                    345:                    lock_done(&stack_queue_lock);
                    346:                    result = THREAD_AWAKENED;
                    347:                    continue;
                    348:                }
                    349:                assert_wait(&stack_queue, FALSE);
                    350:                need_stack_wakeup = TRUE;
                    351:                lock_done(&stack_queue_lock);
                    352:                thread_block();
                    353:                result = current_thread()->wait_result;
                    354:            } else {
                    355:                if (msg_printed)
                    356:                    uprintf("continuing\n");            /* got a stack now */
                    357:                }
                    358:        } while (stack == (vm_offset_t)0);
                    359:        
                    360:        return(stack);
                    361: }
                    362: 
                    363: vm_offset_t
                    364: allocStack()
                    365: {
                    366:        return (_allocStack(TRUE));
                    367: }
                    368: 
                    369: 
                    370: /*
                    371:  *  canSwap: see if we can swap the entire chunk that this stack lives on
                    372:  *
                    373:  *  Return TRUE if we can, FALSE otherwise.
                    374:  */
                    375: int
                    376: canSwap(vm_offset_t stack)
                    377: {
                    378:        int i;
                    379:        vm_offset_t thisStack;
                    380: 
                    381:        /*
                    382:         *  Determine if we should swap a page by checking the status of each
                    383:         *  stack on a page.
                    384:         */
                    385:        thisStack = trunc_page(stack);
                    386:        for (i = 0; i < stacksPerPage; i++) {
                    387:                if ( ((kernelStack) thisStack)->status == STACK_IN_USE )
                    388:                        return FALSE;
                    389:                        
                    390:                thisStack += kernelStackBlock;
                    391:        }
                    392:        
                    393:        return TRUE;
                    394: }
                    395: 
                    396: 
                    397: /*
                    398:  *  doSwapout: really swap out a stack.
                    399:  *
                    400:  *  The stack_queue_lock must be held across this call.
                    401:  */
                    402: void
                    403: doSwapout(vm_offset_t stack)
                    404: {
                    405:        int i, swapAll;
                    406:        vm_offset_t page = trunc_page(stack);
                    407:        vm_offset_t thisStack;
                    408: 
                    409:        /*
                    410:         *  Make sure we remove all free stacks on this page from the free list.
                    411:         */
                    412:        thisStack = page;
                    413:        swapAll = TRUE;
                    414:        for (i = 0; i < stacksPerPage; i++) {
                    415:                
                    416:                assert( ((kernelStack) thisStack)->status != STACK_IN_USE );
                    417:                
                    418:                if ( ((kernelStack) thisStack)->status == STACK_FREE ) {
                    419:                        queue_remove(&stack_queue, (kernelStack) thisStack, kernelStack, freeList);
                    420:                        stack_free_count--;
                    421:                        stackStats.freeStacks--;
                    422:                }
                    423: 
                    424:                thisStack += kernelStackBlock;
                    425:        }
                    426:        
                    427:        /*
                    428:         *  Hack... we need a way to designate that the page is really
                    429:         *  unwired so that when we bring it back in, we can notice that
                    430:         *  it had been unwired.
                    431:         */
                    432:        ((kernelStack) page)->freeList.next = (struct queue_entry *) 0xfeedface;
                    433:        (void) vm_map_pageable(kernel_map, page,
                    434:                               round_page(page + kernelStackBlock), TRUE);
                    435:        stackStats.swappedChunks++;
                    436: }
                    437: 
                    438: 
                    439: /*
                    440:  *  swapoutStack: try to swap out a stack
                    441:  *
                    442:  *  We swap out stacks by unwiring their memory, then allowing the pagout daemon
                    443:  *  to page out the unused stack.  If a kernel stack spans whole pages, we can just
                    444:  *  unwire its memory right away.  However, if it occupies a fraction of a page,
                    445:  *  then we must also be able to swap any other stacks on that page. 
                    446:  */
                    447: void
                    448: swapoutStack(vm_offset_t stack)
                    449: {
                    450:        int i, swapAll;
                    451:        vm_offset_t page = trunc_page(stack);
                    452:        vm_offset_t thisStack;
                    453: 
                    454:        stack -= sizeof(struct _kernelStack);
                    455:        stackStats.swappableStacks++;
                    456:        
                    457:        lock_write(&stack_queue_lock);
                    458:        /*
                    459:         *  Mark this stack swappable
                    460:         */
                    461:        ((kernelStack) stack)->status = STACK_SWAPPED;
                    462:        
                    463:        /*
                    464:         *  Bug out now if we can't swap the stack
                    465:         */
                    466:        if (!canSwap(stack)) {
                    467:                lock_done(&stack_queue_lock);
                    468:                return;
                    469:        }
                    470:        
                    471:        doSwapout(stack);
                    472:        lock_done(&stack_queue_lock);
                    473: }
                    474: 
                    475: 
                    476: /*
                    477:  *  swapinStack: swap in a stack
                    478:  *
                    479:  *  We swap in stacks by wiring their memory.  We can just wire its memory right
                    480:  *  away.  If there are other stacks in that memory, no problem, they just end up
                    481:  *  resident too.
                    482:  */
                    483: void
                    484: swapinStack(vm_offset_t stack)
                    485: {
                    486:        int i, swapAll;
                    487:        vm_offset_t page = trunc_page(stack);
                    488:        vm_offset_t thisStack;
                    489: 
                    490:        stack -= sizeof(struct _kernelStack);
                    491:        stackStats.swappableStacks--;
                    492:        
                    493:        (void) vm_map_pageable(kernel_map, page,
                    494:                               round_page(page + kernelStackBlock), FALSE);
                    495:        
                    496:        lock_write(&stack_queue_lock);
                    497:        /*
                    498:         *  Mark our particular stack in use.
                    499:         */
                    500:        ((kernelStack) stack)->status = STACK_IN_USE;
                    501:        
                    502:        /*
                    503:         *  Check the magic hack to see if we've already put this stuff on the free
                    504:         *  list.
                    505:         */
                    506:        if (((kernelStack) page)->freeList.next != (struct queue_entry *) 0xfeedface) {
                    507:                lock_done(&stack_queue_lock);
                    508:                return;
                    509:        }
                    510:                
                    511:        ((kernelStack) page)->freeList.next = (struct queue_entry *) 0;
                    512:        stackStats.swappedChunks--;
                    513:                
                    514:        /*
                    515:         *  Scan through the memory we just brought in and put free stacks on
                    516:         *  the free list.
                    517:         */
                    518:        thisStack = page;
                    519:        swapAll = TRUE;
                    520:        for (i = 0; i < stacksPerPage; i++) {
                    521:                if ( ((kernelStack) thisStack)->status == STACK_FREE ) {
                    522:                        enterFreeList(thisStack);
                    523:                }
                    524:                        
                    525:                thisStack += kernelStackBlock;
                    526:        }       
                    527:        lock_done(&stack_queue_lock);
                    528: }
                    529: 
                    530: boolean_t stack_alloc_try(
                    531:        thread_t        thread,
                    532:        void            (*resume)(thread_t))
                    533: {
                    534:        register vm_offset_t stack = _allocStack(FALSE);
                    535:        
                    536:        if (!stack)
                    537:                stack = thread->stack_privilege;
                    538:        if (stack) {
                    539:                stack_attach(thread, stack, resume);
                    540:                return TRUE;
                    541:        }
                    542: 
                    543:        return FALSE;
                    544: }
                    545: 
                    546: void stack_alloc(
                    547:        thread_t        thread,
                    548:        void            (*resume)(thread_t))
                    549: {
                    550:        vm_offset_t stack;
                    551:        
                    552:        stack = allocStack();
                    553:        if (!stack)
                    554:                panic("stack_alloc");
                    555: 
                    556:        stack_attach(thread, stack, resume);
                    557: }
                    558: 
                    559: void stack_free(
                    560:        thread_t thread)
                    561: {
                    562:        register vm_offset_t stack;
                    563: 
                    564:        stack = stack_detach(thread);
                    565: 
                    566:        if (stack != thread->stack_privilege)
                    567:                freeStack(stack);
                    568: }
                    569: 
                    570: void stack_collect(void)
                    571: {
                    572: }
                    573: 
                    574: #if    MACH_DEBUG
                    575: 
                    576: void stack_statistics(
                    577:        natural_t       *totalp,
                    578:        vm_size_t       *maxusagep)
                    579: {
                    580:        extern boolean_t stack_check_usage;
                    581: 
                    582:        lock_read(&stack_queue_lock);
                    583:        if (stack_check_usage) {
                    584:                vm_offset_t stack;
                    585:                
                    586:                for (stack = (vm_offset_t)queue_first(&stack_queue);
                    587:                        !queue_end(&stack_queue, (queue_entry_t)stack);
                    588:                                stack = (vm_offset_t)
                    589:                                        queue_next((queue_entry_t)stack)) {
                    590:                        vm_size_t usage =
                    591:                                stack_usage(stack +
                    592:                                        sizeof (struct _kernelStack));
                    593:                                        
                    594:                        if (usage > *maxusagep)
                    595:                                *maxusagep = usage;
                    596:                }
                    597:        }
                    598:        
                    599:        *totalp = stack_free_count;
                    600:        lock_done(&stack_queue_lock);
                    601: }
                    602: 
                    603: #endif /* MACH_DEBUG */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.