Annotation of Gnu-Mach/linux/dev/glue/kmem.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Linux memory allocation.
                      3:  * 
                      4:  * Copyright (C) 1996 The University of Utah and the Computer Systems
                      5:  * Laboratory at the University of Utah (CSL)
                      6:  *
                      7:  * This program is free software; you can redistribute it and/or modify
                      8:  * it under the terms of the GNU General Public License as published by
                      9:  * the Free Software Foundation; either version 2, or (at your option)
                     10:  * any later version.
                     11:  *
                     12:  * This program is distributed in the hope that it will be useful,
                     13:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     14:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     15:  * GNU General Public License for more details.
                     16:  *
                     17:  * You should have received a copy of the GNU General Public License
                     18:  * along with this program; if not, write to the Free Software
                     19:  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
                     20:  *
                     21:  *      Author: Shantanu Goel, University of Utah CSL
                     22:  *
                     23:  */
                     24: 
                     25: #include <sys/types.h>
                     26: 
                     27: #include <mach/mach_types.h>
                     28: #include <mach/vm_param.h>
                     29: 
                     30: #include <kern/assert.h>
                     31: #include <kern/kalloc.h>
                     32: 
                     33: #include <vm/vm_page.h>
                     34: #include <vm/vm_kern.h>
                     35: 
                     36: #define MACH_INCLUDE
                     37: #include <linux/sched.h>
                     38: #include <linux/malloc.h>
                     39: #include <linux/delay.h>
                     40: 
                     41: #include <asm/system.h>
                     42: 
                     43: extern void *alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t *);
                     44: extern int printf (const char *, ...);
                     45: 
                     46: /* Amount of memory to reserve for Linux memory allocator.
                     47:    We reserve 64K chunks to stay within DMA limits.
                     48:    Increase MEM_CHUNKS if the kernel is running out of memory.  */
                     49: #define MEM_CHUNK_SIZE (64 * 1024)
                     50: #define MEM_CHUNKS     7
                     51: 
                     52: /* Mininum amount that linux_kmalloc will allocate.  */
                     53: #define MIN_ALLOC      12
                     54: 
                     55: #ifndef NBPW
                     56: #define NBPW           32
                     57: #endif
                     58: 
                     59: /* Memory block header.  */
                     60: struct blkhdr
                     61: {
                     62:   unsigned short free; /* 1 if block is free */
                     63:   unsigned short size; /* size of block */
                     64: };
                     65: 
                     66: /* This structure heads a page allocated by linux_kmalloc.  */
                     67: struct pagehdr
                     68: {
                     69:   unsigned size;               /* size (multiple of PAGE_SIZE) */
                     70:   struct pagehdr *next;        /* next header in list */
                     71: };
                     72: 
                     73: /* This structure describes a memory chunk.  */
                     74: struct chunkhdr
                     75: {
                     76:   unsigned long start; /* start address */
                     77:   unsigned long end;           /* end address */
                     78:   unsigned long bitmap;        /* busy/free bitmap of pages */
                     79: };
                     80: 
                     81: /* Chunks from which pages are allocated.  */
                     82: static struct chunkhdr pages_free[MEM_CHUNKS];
                     83: 
                     84: /* Memory list maintained by linux_kmalloc.  */
                     85: static struct pagehdr *memlist;
                     86: 
                     87: /* Some statistics.  */
                     88: int num_block_coalesce = 0;
                     89: int num_page_collect = 0;
                     90: int linux_mem_avail;
                     91: 
                     92: /* Initialize the Linux memory allocator.  */
                     93: void
                     94: linux_kmem_init ()
                     95: {
                     96:   int i, j;
                     97:   vm_page_t p, pages;
                     98: 
                     99:   for (i = 0; i < MEM_CHUNKS; i++)
                    100:     {
                    101:       /* Allocate memory.  */
                    102:       pages_free[i].start = (unsigned long) alloc_contig_mem (MEM_CHUNK_SIZE,
                    103:                                                              16 * 1024 * 1024,
                    104:                                                              0xffff, &pages);
                    105: 
                    106:       assert (pages_free[i].start);
                    107:       assert ((pages_free[i].start & 0xffff) == 0);
                    108: 
                    109:       /* Sanity check: ensure pages are contiguous and within DMA limits.  */
                    110:       for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE)
                    111:        {
                    112:          assert (p->phys_addr < 16 * 1024 * 1024);
                    113:          assert (p->phys_addr + PAGE_SIZE
                    114:                  == ((vm_page_t) p->pageq.next)->phys_addr);
                    115: 
                    116:          p = (vm_page_t) p->pageq.next;
                    117:        }
                    118: 
                    119:       pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE;
                    120: 
                    121:       /* Initialize free page bitmap.  */
                    122:       pages_free[i].bitmap = 0;
                    123:       j = MEM_CHUNK_SIZE >> PAGE_SHIFT;
                    124:       while (--j >= 0)
                    125:        pages_free[i].bitmap |= 1 << j;
                    126:     }
                    127: 
                    128:   linux_mem_avail = (MEM_CHUNKS * MEM_CHUNK_SIZE) >> PAGE_SHIFT;
                    129: }
                    130: 
                    131: /* Return the number by which the page size should be
                    132:    shifted such that the resulting value is >= SIZE.  */
                    133: static unsigned long
                    134: get_page_order (int size)
                    135: {
                    136:   unsigned long order;
                    137: 
                    138:   for (order = 0; (PAGE_SIZE << order) < size; order++)
                    139:     ;
                    140:   return order;
                    141: }
                    142: 
                    143: #ifdef LINUX_DEV_DEBUG
                    144: static void
                    145: check_page_list (int line)
                    146: {
                    147:   unsigned size;
                    148:   struct pagehdr *ph;
                    149:   struct blkhdr *bh;
                    150: 
                    151:   for (ph = memlist; ph; ph = ph->next)
                    152:     {
                    153:       if ((int) ph & PAGE_MASK)
                    154:        panic ("%s:%d: page header not aligned", __FILE__, line);
                    155: 
                    156:       size = 0;
                    157:       bh = (struct blkhdr *) (ph + 1);
                    158:       while (bh < (struct blkhdr *) ((void *) ph + ph->size))
                    159:        {
                    160:          size += bh->size + sizeof (struct blkhdr);
                    161:          bh = (void *) (bh + 1) + bh->size;
                    162:        }
                    163: 
                    164:       if (size + sizeof (struct pagehdr) != ph->size)
                    165:        panic ("%s:%d: memory list destroyed", __FILE__, line);
                    166:     }
                    167: }
                    168: #else
                    169: #define check_page_list(line)
                    170: #endif
                    171: 
                    172: /* Merge adjacent free blocks in the memory list.  */
                    173: static void
                    174: coalesce_blocks ()
                    175: {
                    176:   struct pagehdr *ph;
                    177:   struct blkhdr *bh, *bhp, *ebh;
                    178: 
                    179:   num_block_coalesce++;
                    180: 
                    181:   for (ph = memlist; ph; ph = ph->next)
                    182:     {
                    183:       bh = (struct blkhdr *) (ph + 1);
                    184:       ebh = (struct blkhdr *) ((void *) ph + ph->size);
                    185:       while (1)
                    186:        {
                    187:          /* Skip busy blocks.  */
                    188:          while (bh < ebh && !bh->free)
                    189:            bh = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
                    190:          if (bh == ebh)
                    191:            break;
                    192: 
                    193:          /* Merge adjacent free blocks.  */
                    194:          while (1)
                    195:            {
                    196:              bhp = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
                    197:              if (bhp == ebh)
                    198:                {
                    199:                  bh = bhp;
                    200:                  break;
                    201:                }
                    202:              if (!bhp->free)
                    203:                {
                    204:                  bh = (struct blkhdr *) ((void *) (bhp + 1) + bhp->size);
                    205:                  break;
                    206:                }
                    207:              bh->size += bhp->size + sizeof (struct blkhdr);
                    208:            }
                    209:        }
                    210:     }
                    211: }
                    212: 
                    213: /* Allocate SIZE bytes of memory.
                    214:    The PRIORITY parameter specifies various flags
                    215:    such as DMA, atomicity, etc.  It is not used by Mach.  */
                    216: void *
                    217: linux_kmalloc (unsigned int size, int priority)
                    218: {
                    219:   int order, coalesced = 0;
                    220:   unsigned flags;
                    221:   struct pagehdr *ph;
                    222:   struct blkhdr *bh, *new_bh;
                    223: 
                    224:   if (size < MIN_ALLOC)
                    225:     size = MIN_ALLOC;
                    226:   else
                    227:     size = (size + sizeof (int) - 1) & ~(sizeof (int) - 1);
                    228: 
                    229:   assert (size <= (MEM_CHUNK_SIZE
                    230:                   - sizeof (struct pagehdr)
                    231:                   - sizeof (struct blkhdr)));
                    232: 
                    233:   save_flags (flags);
                    234:   cli ();
                    235: 
                    236: again:
                    237:   check_page_list (__LINE__);
                    238: 
                    239:   /* Walk the page list and find the first free block with size
                    240:      greater than or equal to the one required.  */
                    241:   for (ph = memlist; ph; ph = ph->next)
                    242:     {
                    243:       bh = (struct blkhdr *) (ph + 1);
                    244:       while (bh < (struct blkhdr *) ((void *) ph + ph->size))
                    245:        {
                    246:          if (bh->free && bh->size >= size)
                    247:            {
                    248:              bh->free = 0;
                    249:              if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
                    250:                {
                    251:                  /* Split the current block and create a new free block.  */
                    252:                  new_bh = (void *) (bh + 1) + size;
                    253:                  new_bh->free = 1;
                    254:                  new_bh->size = bh->size - size - sizeof (struct blkhdr);
                    255:                  bh->size = size;
                    256:                }
                    257: 
                    258:              check_page_list (__LINE__);
                    259: 
                    260:              restore_flags (flags);
                    261:              return bh + 1;
                    262:            }
                    263:          bh = (void *) (bh + 1) + bh->size;
                    264:        }
                    265:     }
                    266: 
                    267:   check_page_list (__LINE__);
                    268: 
                    269:   /* Allocation failed; coalesce free blocks and try again.  */
                    270:   if (!coalesced)
                    271:     {
                    272:       coalesce_blocks ();
                    273:       coalesced = 1;
                    274:       goto again;
                    275:     }
                    276: 
                    277:   /* Allocate more pages.  */
                    278:   order = get_page_order (size
                    279:                          + sizeof (struct pagehdr)
                    280:                          + sizeof (struct blkhdr));
                    281:   ph = (struct pagehdr *) __get_free_pages (GFP_KERNEL, order, ~0UL);
                    282:   if (!ph)
                    283:     {
                    284:       restore_flags (flags);
                    285:       return NULL;
                    286:     }
                    287: 
                    288:   ph->size = PAGE_SIZE << order;
                    289:   ph->next = memlist;
                    290:   memlist = ph;
                    291:   bh = (struct blkhdr *) (ph + 1);
                    292:   bh->free = 0;
                    293:   bh->size = ph->size - sizeof (struct pagehdr) - sizeof (struct blkhdr);
                    294:   if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
                    295:     {
                    296:       new_bh = (void *) (bh + 1) + size;
                    297:       new_bh->free = 1;
                    298:       new_bh->size = bh->size - size - sizeof (struct blkhdr);
                    299:       bh->size = size;
                    300:     }
                    301: 
                    302:   check_page_list (__LINE__);
                    303: 
                    304:   restore_flags (flags);
                    305:   return bh + 1;
                    306: }
                    307: 
                    308: /* Free memory P previously allocated by linux_kmalloc.  */
                    309: void
                    310: linux_kfree (void *p)
                    311: {
                    312:   unsigned flags;
                    313:   struct blkhdr *bh;
                    314:   struct pagehdr *ph;
                    315: 
                    316:   assert (((int) p & (sizeof (int) - 1)) == 0);
                    317: 
                    318:   save_flags (flags);
                    319:   cli ();
                    320: 
                    321:   check_page_list (__LINE__);
                    322: 
                    323:   for (ph = memlist; ph; ph = ph->next)
                    324:     if (p >= (void *) ph && p < (void *) ph + ph->size)
                    325:       break;
                    326: 
                    327:   assert (ph);
                    328: 
                    329:   bh = (struct blkhdr *) p - 1;
                    330: 
                    331:   assert (!bh->free);
                    332:   assert (bh->size >= MIN_ALLOC);
                    333:   assert ((bh->size & (sizeof (int) - 1)) == 0);
                    334: 
                    335:   bh->free = 1;
                    336: 
                    337:   check_page_list (__LINE__);
                    338: 
                    339:   restore_flags (flags);
                    340: }
                    341: 
                    342: /* Free any pages that are not in use.
                    343:    Called by __get_free_pages when pages are running low.  */
                    344: static void
                    345: collect_kmalloc_pages ()
                    346: {
                    347:   struct blkhdr *bh;
                    348:   struct pagehdr *ph, **prev_ph;
                    349: 
                    350:   check_page_list (__LINE__);
                    351: 
                    352:   coalesce_blocks ();
                    353: 
                    354:   check_page_list (__LINE__);
                    355: 
                    356:   ph = memlist;
                    357:   prev_ph = &memlist;
                    358:   while (ph)
                    359:     {
                    360:       bh = (struct blkhdr *) (ph + 1);
                    361:       if (bh->free && (void *) (bh + 1) + bh->size == (void *) ph + ph->size)
                    362:        {
                    363:          *prev_ph = ph->next;
                    364:          free_pages ((unsigned long) ph, get_page_order (ph->size));
                    365:          ph = *prev_ph;
                    366:        }
                    367:       else
                    368:        {
                    369:          prev_ph = &ph->next;
                    370:          ph = ph->next;
                    371:        }
                    372:     }
                    373: 
                    374:   check_page_list (__LINE__);
                    375: }
                    376: 
                    377: /* Allocate ORDER + 1 number of physically contiguous pages.
                    378:    PRIORITY and DMA are not used in Mach.
                    379: 
                    380:    XXX: This needs to be dynamic.  To do that we need to make
                    381:    the Mach page manipulation routines interrupt safe and they
                    382:    must provide machine dependant hooks.  */
                    383: unsigned long
                    384: __get_free_pages (int priority, unsigned long order, int dma)
                    385: {
                    386:   int i, pages_collected = 0;
                    387:   unsigned flags, bits, off, j, len;
                    388: 
                    389:   assert ((PAGE_SIZE << order) <= MEM_CHUNK_SIZE);
                    390: 
                    391:   /* Construct bitmap of contiguous pages.  */
                    392:   bits = 0;
                    393:   j = 0;
                    394:   len = 0;
                    395:   while (len < (PAGE_SIZE << order))
                    396:     {
                    397:       bits |= 1 << j++;
                    398:       len += PAGE_SIZE;
                    399:     }
                    400: 
                    401:   save_flags (flags);
                    402:   cli ();
                    403: again:
                    404: 
                    405:   /* Search each chunk for the required number of contiguous pages.  */
                    406:   for (i = 0; i < MEM_CHUNKS; i++)
                    407:     {
                    408:       off = 0;
                    409:       j = bits;
                    410:       while (MEM_CHUNK_SIZE - off >= (PAGE_SIZE << order))
                    411:        {
                    412:          if ((pages_free[i].bitmap & j) == j)
                    413:            {
                    414:              pages_free[i].bitmap &= ~j;
                    415:              linux_mem_avail -= order + 1;
                    416:              restore_flags (flags);
                    417:              return pages_free[i].start + off;
                    418:            }
                    419:          j <<= 1;
                    420:          off += PAGE_SIZE;
                    421:        }
                    422:     }
                    423: 
                    424:   /* Allocation failed; collect kmalloc and buffer pages
                    425:      and try again.  */
                    426:   if (!pages_collected)
                    427:     {
                    428:       num_page_collect++;
                    429:       collect_kmalloc_pages ();
                    430:       pages_collected = 1;
                    431:       goto again;
                    432:     }
                    433: 
                    434:   printf ("%s:%d: __get_free_pages: ran out of pages\n", __FILE__, __LINE__);
                    435: 
                    436:   restore_flags (flags);
                    437:   return 0;
                    438: }
                    439: 
                    440: /* Free ORDER + 1 number of physically
                    441:    contiguous pages starting at address ADDR.  */
                    442: void
                    443: free_pages (unsigned long addr, unsigned long order)
                    444: {
                    445:   int i;
                    446:   unsigned flags, bits, len, j;
                    447: 
                    448:   assert ((addr & PAGE_MASK) == 0);
                    449: 
                    450:   for (i = 0; i < MEM_CHUNKS; i++)
                    451:     if (addr >= pages_free[i].start && addr < pages_free[i].end)
                    452:       break;
                    453: 
                    454:   assert (i < MEM_CHUNKS);
                    455: 
                    456:   /* Contruct bitmap of contiguous pages.  */
                    457:   len = 0;
                    458:   j = 0;
                    459:   bits = 0;
                    460:   while (len < (PAGE_SIZE << order))
                    461:     {
                    462:       bits |= 1 << j++;
                    463:       len += PAGE_SIZE;
                    464:     }
                    465:   bits <<= (addr - pages_free[i].start) >> PAGE_SHIFT;
                    466: 
                    467:   save_flags (flags);
                    468:   cli ();
                    469: 
                    470:   assert ((pages_free[i].bitmap & bits) == 0);
                    471: 
                    472:   pages_free[i].bitmap |= bits;
                    473:   linux_mem_avail += order + 1;
                    474:   restore_flags (flags);
                    475: }
                    476: 
                    477: 
                    478: /* vmalloc management routines. */
                    479: struct vmalloc_struct
                    480: {
                    481:   struct vmalloc_struct *prev;
                    482:   struct vmalloc_struct *next;
                    483:   vm_offset_t start;
                    484:   vm_size_t size;
                    485: };
                    486: 
                    487: static struct vmalloc_struct
                    488: vmalloc_list = { &vmalloc_list, &vmalloc_list, 0, 0 };
                    489: 
                    490: static inline void
                    491: vmalloc_list_insert (vm_offset_t start, vm_size_t size)
                    492: {
                    493:   struct vmalloc_struct *p;
                    494: 
                    495:   p = (struct vmalloc_struct *) kalloc (sizeof (struct vmalloc_struct));
                    496:   if (p == NULL)
                    497:     panic ("kernel memory is exhausted");
                    498: 
                    499:   p->prev = vmalloc_list.prev;
                    500:   p->next = &vmalloc_list;
                    501:   vmalloc_list.prev->next = p;
                    502:   vmalloc_list.prev = p;
                    503: 
                    504:   p->start = start;
                    505:   p->size = size;
                    506: }
                    507: 
                    508: static struct vmalloc_struct *
                    509: vmalloc_list_lookup (vm_offset_t start)
                    510: {
                    511:   struct vmalloc_struct *p;
                    512: 
                    513:   for (p = vmalloc_list.next; p != &vmalloc_list; p = p->next)
                    514:     {
                    515:       if (p->start == start)
                    516:        return p;
                    517:     }
                    518: 
                    519:   return NULL;
                    520: }
                    521: 
                    522: static inline void
                    523: vmalloc_list_remove (struct vmalloc_struct *p)
                    524: {
                    525:   p->next->prev = p->prev;
                    526:   p->prev->next = p->next;
                    527: 
                    528:   kfree (p, sizeof (struct vmalloc_struct));
                    529: }
                    530: 
                    531: /* Allocate SIZE bytes of memory.  The pages need not be contiguous.  */
                    532: void *
                    533: vmalloc (unsigned long size)
                    534: {
                    535:   kern_return_t ret;
                    536:   vm_offset_t addr;
                    537:   
                    538:   ret = kmem_alloc_wired (kernel_map, &addr, round_page (size));
                    539:   if (ret != KERN_SUCCESS)
                    540:     return NULL;
                    541: 
                    542:   vmalloc_list_insert (addr, round_page (size));
                    543:   return (void *) addr;
                    544: }
                    545: 
                    546: /* Free vmalloc'ed and vremap'ed virtual address space. */
                    547: void
                    548: vfree (void *addr)
                    549: {
                    550:   struct vmalloc_struct *p;
                    551: 
                    552:   p = vmalloc_list_lookup ((vm_offset_t) addr);
                    553:   if (p)
                    554:     panic ("vmalloc_list_lookup failure");
                    555:   
                    556:   kmem_free (kernel_map, addr, p->size);
                    557:   vmalloc_list_remove (p);
                    558: }
                    559: 
                    560: /* XXX: Quick hacking. */
                    561: /* Remap physical address into virtual address. */
                    562: void *
                    563: vremap (unsigned long offset, unsigned long size)
                    564: {
                    565:   extern vm_offset_t pmap_map_bd (register vm_offset_t virt,
                    566:                                  register vm_offset_t start,
                    567:                                  register vm_offset_t end,
                    568:                                  vm_prot_t prot);
                    569:   vm_offset_t addr;
                    570:   kern_return_t ret;
                    571:   
                    572:   ret = kmem_alloc_wired (kernel_map, &addr, round_page (size));
                    573:   if (ret != KERN_SUCCESS)
                    574:     return NULL;
                    575:   
                    576:   (void) pmap_map_bd (addr, offset, offset + round_page (size),
                    577:                      VM_PROT_READ | VM_PROT_WRITE);
                    578:   
                    579:   vmalloc_list_insert (addr, round_page (size));
                    580:   return (void *) addr;
                    581: }

unix.superglobalmegacorp.com

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