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

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>
1.1.1.2   root       32: #include <kern/printf.h>
1.1       root       33: 
                     34: #include <vm/vm_page.h>
                     35: #include <vm/vm_kern.h>
                     36: 
                     37: #define MACH_INCLUDE
                     38: #include <linux/sched.h>
                     39: #include <linux/malloc.h>
                     40: #include <linux/delay.h>
                     41: 
                     42: #include <asm/system.h>
                     43: 
1.1.1.2   root       44: #include <linux/dev/glue/glue.h>
1.1       root       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)
1.1.1.2   root       50: #define MEM_CHUNKS     32
                     51: #define MEM_DMA_LIMIT  (16 * 1024 * 1024)
1.1       root       52: 
                     53: /* Mininum amount that linux_kmalloc will allocate.  */
                     54: #define MIN_ALLOC      12
                     55: 
                     56: #ifndef NBPW
                     57: #define NBPW           32
                     58: #endif
                     59: 
                     60: /* Memory block header.  */
                     61: struct blkhdr
                     62: {
                     63:   unsigned short free; /* 1 if block is free */
                     64:   unsigned short size; /* size of block */
                     65: };
                     66: 
                     67: /* This structure heads a page allocated by linux_kmalloc.  */
                     68: struct pagehdr
                     69: {
                     70:   unsigned size;               /* size (multiple of PAGE_SIZE) */
                     71:   struct pagehdr *next;        /* next header in list */
                     72: };
                     73: 
                     74: /* This structure describes a memory chunk.  */
                     75: struct chunkhdr
                     76: {
                     77:   unsigned long start; /* start address */
                     78:   unsigned long end;           /* end address */
                     79:   unsigned long bitmap;        /* busy/free bitmap of pages */
                     80: };
                     81: 
                     82: /* Chunks from which pages are allocated.  */
                     83: static struct chunkhdr pages_free[MEM_CHUNKS];
                     84: 
                     85: /* Memory list maintained by linux_kmalloc.  */
                     86: static struct pagehdr *memlist;
                     87: 
                     88: /* Some statistics.  */
                     89: int num_block_coalesce = 0;
                     90: int num_page_collect = 0;
                     91: int linux_mem_avail;
                     92: 
                     93: /* Initialize the Linux memory allocator.  */
                     94: void
                     95: linux_kmem_init ()
                     96: {
                     97:   int i, j;
                     98:   vm_page_t p, pages;
                     99: 
                    100:   for (i = 0; i < MEM_CHUNKS; i++)
                    101:     {
                    102:       /* Allocate memory.  */
                    103:       pages_free[i].start = (unsigned long) alloc_contig_mem (MEM_CHUNK_SIZE,
1.1.1.2   root      104:                                                              MEM_DMA_LIMIT,
1.1       root      105:                                                              0xffff, &pages);
                    106: 
                    107:       assert (pages_free[i].start);
                    108:       assert ((pages_free[i].start & 0xffff) == 0);
                    109: 
                    110:       /* Sanity check: ensure pages are contiguous and within DMA limits.  */
                    111:       for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE)
                    112:        {
1.1.1.2   root      113:          assert (p->phys_addr < MEM_DMA_LIMIT);
1.1.1.3 ! root      114:          assert (p->phys_addr + PAGE_SIZE == (p + 1)->phys_addr);
        !           115:          p++;
1.1       root      116:        }
                    117: 
                    118:       pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE;
                    119: 
                    120:       /* Initialize free page bitmap.  */
                    121:       pages_free[i].bitmap = 0;
                    122:       j = MEM_CHUNK_SIZE >> PAGE_SHIFT;
                    123:       while (--j >= 0)
                    124:        pages_free[i].bitmap |= 1 << j;
                    125:     }
                    126: 
                    127:   linux_mem_avail = (MEM_CHUNKS * MEM_CHUNK_SIZE) >> PAGE_SHIFT;
                    128: }
                    129: 
                    130: /* Return the number by which the page size should be
                    131:    shifted such that the resulting value is >= SIZE.  */
                    132: static unsigned long
                    133: get_page_order (int size)
                    134: {
                    135:   unsigned long order;
                    136: 
                    137:   for (order = 0; (PAGE_SIZE << order) < size; order++)
                    138:     ;
                    139:   return order;
                    140: }
                    141: 
                    142: #ifdef LINUX_DEV_DEBUG
                    143: static void
                    144: check_page_list (int line)
                    145: {
                    146:   unsigned size;
                    147:   struct pagehdr *ph;
                    148:   struct blkhdr *bh;
                    149: 
                    150:   for (ph = memlist; ph; ph = ph->next)
                    151:     {
                    152:       if ((int) ph & PAGE_MASK)
                    153:        panic ("%s:%d: page header not aligned", __FILE__, line);
                    154: 
                    155:       size = 0;
                    156:       bh = (struct blkhdr *) (ph + 1);
                    157:       while (bh < (struct blkhdr *) ((void *) ph + ph->size))
                    158:        {
                    159:          size += bh->size + sizeof (struct blkhdr);
                    160:          bh = (void *) (bh + 1) + bh->size;
                    161:        }
                    162: 
                    163:       if (size + sizeof (struct pagehdr) != ph->size)
                    164:        panic ("%s:%d: memory list destroyed", __FILE__, line);
                    165:     }
                    166: }
                    167: #else
                    168: #define check_page_list(line)
                    169: #endif
                    170: 
                    171: /* Merge adjacent free blocks in the memory list.  */
                    172: static void
                    173: coalesce_blocks ()
                    174: {
                    175:   struct pagehdr *ph;
                    176:   struct blkhdr *bh, *bhp, *ebh;
                    177: 
                    178:   num_block_coalesce++;
                    179: 
                    180:   for (ph = memlist; ph; ph = ph->next)
                    181:     {
                    182:       bh = (struct blkhdr *) (ph + 1);
                    183:       ebh = (struct blkhdr *) ((void *) ph + ph->size);
                    184:       while (1)
                    185:        {
                    186:          /* Skip busy blocks.  */
                    187:          while (bh < ebh && !bh->free)
                    188:            bh = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
                    189:          if (bh == ebh)
                    190:            break;
                    191: 
                    192:          /* Merge adjacent free blocks.  */
                    193:          while (1)
                    194:            {
                    195:              bhp = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
                    196:              if (bhp == ebh)
                    197:                {
                    198:                  bh = bhp;
                    199:                  break;
                    200:                }
                    201:              if (!bhp->free)
                    202:                {
                    203:                  bh = (struct blkhdr *) ((void *) (bhp + 1) + bhp->size);
                    204:                  break;
                    205:                }
                    206:              bh->size += bhp->size + sizeof (struct blkhdr);
                    207:            }
                    208:        }
                    209:     }
                    210: }
                    211: 
                    212: /* Allocate SIZE bytes of memory.
                    213:    The PRIORITY parameter specifies various flags
                    214:    such as DMA, atomicity, etc.  It is not used by Mach.  */
                    215: void *
                    216: linux_kmalloc (unsigned int size, int priority)
                    217: {
                    218:   int order, coalesced = 0;
1.1.1.2   root      219:   unsigned long flags;
1.1       root      220:   struct pagehdr *ph;
                    221:   struct blkhdr *bh, *new_bh;
                    222: 
                    223:   if (size < MIN_ALLOC)
                    224:     size = MIN_ALLOC;
                    225:   else
                    226:     size = (size + sizeof (int) - 1) & ~(sizeof (int) - 1);
                    227: 
                    228:   assert (size <= (MEM_CHUNK_SIZE
                    229:                   - sizeof (struct pagehdr)
                    230:                   - sizeof (struct blkhdr)));
                    231: 
                    232:   save_flags (flags);
                    233:   cli ();
                    234: 
                    235: again:
                    236:   check_page_list (__LINE__);
                    237: 
                    238:   /* Walk the page list and find the first free block with size
                    239:      greater than or equal to the one required.  */
                    240:   for (ph = memlist; ph; ph = ph->next)
                    241:     {
                    242:       bh = (struct blkhdr *) (ph + 1);
                    243:       while (bh < (struct blkhdr *) ((void *) ph + ph->size))
                    244:        {
                    245:          if (bh->free && bh->size >= size)
                    246:            {
                    247:              bh->free = 0;
                    248:              if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
                    249:                {
                    250:                  /* Split the current block and create a new free block.  */
                    251:                  new_bh = (void *) (bh + 1) + size;
                    252:                  new_bh->free = 1;
                    253:                  new_bh->size = bh->size - size - sizeof (struct blkhdr);
                    254:                  bh->size = size;
                    255:                }
                    256: 
                    257:              check_page_list (__LINE__);
                    258: 
                    259:              restore_flags (flags);
                    260:              return bh + 1;
                    261:            }
                    262:          bh = (void *) (bh + 1) + bh->size;
                    263:        }
                    264:     }
                    265: 
                    266:   check_page_list (__LINE__);
                    267: 
                    268:   /* Allocation failed; coalesce free blocks and try again.  */
                    269:   if (!coalesced)
                    270:     {
                    271:       coalesce_blocks ();
                    272:       coalesced = 1;
                    273:       goto again;
                    274:     }
                    275: 
                    276:   /* Allocate more pages.  */
                    277:   order = get_page_order (size
                    278:                          + sizeof (struct pagehdr)
                    279:                          + sizeof (struct blkhdr));
                    280:   ph = (struct pagehdr *) __get_free_pages (GFP_KERNEL, order, ~0UL);
                    281:   if (!ph)
                    282:     {
                    283:       restore_flags (flags);
                    284:       return NULL;
                    285:     }
                    286: 
                    287:   ph->size = PAGE_SIZE << order;
                    288:   ph->next = memlist;
                    289:   memlist = ph;
                    290:   bh = (struct blkhdr *) (ph + 1);
                    291:   bh->free = 0;
                    292:   bh->size = ph->size - sizeof (struct pagehdr) - sizeof (struct blkhdr);
                    293:   if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
                    294:     {
                    295:       new_bh = (void *) (bh + 1) + size;
                    296:       new_bh->free = 1;
                    297:       new_bh->size = bh->size - size - sizeof (struct blkhdr);
                    298:       bh->size = size;
                    299:     }
                    300: 
                    301:   check_page_list (__LINE__);
                    302: 
                    303:   restore_flags (flags);
                    304:   return bh + 1;
                    305: }
                    306: 
                    307: /* Free memory P previously allocated by linux_kmalloc.  */
                    308: void
                    309: linux_kfree (void *p)
                    310: {
1.1.1.2   root      311:   unsigned long flags;
1.1       root      312:   struct blkhdr *bh;
                    313:   struct pagehdr *ph;
                    314: 
                    315:   assert (((int) p & (sizeof (int) - 1)) == 0);
                    316: 
                    317:   save_flags (flags);
                    318:   cli ();
                    319: 
                    320:   check_page_list (__LINE__);
                    321: 
                    322:   for (ph = memlist; ph; ph = ph->next)
                    323:     if (p >= (void *) ph && p < (void *) ph + ph->size)
                    324:       break;
                    325: 
                    326:   assert (ph);
                    327: 
                    328:   bh = (struct blkhdr *) p - 1;
                    329: 
                    330:   assert (!bh->free);
                    331:   assert (bh->size >= MIN_ALLOC);
                    332:   assert ((bh->size & (sizeof (int) - 1)) == 0);
                    333: 
                    334:   bh->free = 1;
                    335: 
                    336:   check_page_list (__LINE__);
                    337: 
                    338:   restore_flags (flags);
                    339: }
                    340: 
                    341: /* Free any pages that are not in use.
                    342:    Called by __get_free_pages when pages are running low.  */
                    343: static void
                    344: collect_kmalloc_pages ()
                    345: {
                    346:   struct blkhdr *bh;
                    347:   struct pagehdr *ph, **prev_ph;
                    348: 
                    349:   check_page_list (__LINE__);
                    350: 
                    351:   coalesce_blocks ();
                    352: 
                    353:   check_page_list (__LINE__);
                    354: 
                    355:   ph = memlist;
                    356:   prev_ph = &memlist;
                    357:   while (ph)
                    358:     {
                    359:       bh = (struct blkhdr *) (ph + 1);
                    360:       if (bh->free && (void *) (bh + 1) + bh->size == (void *) ph + ph->size)
                    361:        {
                    362:          *prev_ph = ph->next;
                    363:          free_pages ((unsigned long) ph, get_page_order (ph->size));
                    364:          ph = *prev_ph;
                    365:        }
                    366:       else
                    367:        {
                    368:          prev_ph = &ph->next;
                    369:          ph = ph->next;
                    370:        }
                    371:     }
                    372: 
                    373:   check_page_list (__LINE__);
                    374: }
                    375: 
                    376: /* Allocate ORDER + 1 number of physically contiguous pages.
                    377:    PRIORITY and DMA are not used in Mach.
                    378: 
                    379:    XXX: This needs to be dynamic.  To do that we need to make
                    380:    the Mach page manipulation routines interrupt safe and they
                    381:    must provide machine dependant hooks.  */
                    382: unsigned long
                    383: __get_free_pages (int priority, unsigned long order, int dma)
                    384: {
                    385:   int i, pages_collected = 0;
1.1.1.2   root      386:   unsigned bits, off, j, len;
                    387:   unsigned long flags;
1.1       root      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;
1.1.1.2   root      446:   unsigned bits, len, j;
                    447:   unsigned long flags;
1.1       root      448: 
                    449:   assert ((addr & PAGE_MASK) == 0);
                    450: 
                    451:   for (i = 0; i < MEM_CHUNKS; i++)
                    452:     if (addr >= pages_free[i].start && addr < pages_free[i].end)
                    453:       break;
                    454: 
                    455:   assert (i < MEM_CHUNKS);
                    456: 
                    457:   /* Contruct bitmap of contiguous pages.  */
                    458:   len = 0;
                    459:   j = 0;
                    460:   bits = 0;
                    461:   while (len < (PAGE_SIZE << order))
                    462:     {
                    463:       bits |= 1 << j++;
                    464:       len += PAGE_SIZE;
                    465:     }
                    466:   bits <<= (addr - pages_free[i].start) >> PAGE_SHIFT;
                    467: 
                    468:   save_flags (flags);
                    469:   cli ();
                    470: 
                    471:   assert ((pages_free[i].bitmap & bits) == 0);
                    472: 
                    473:   pages_free[i].bitmap |= bits;
                    474:   linux_mem_avail += order + 1;
                    475:   restore_flags (flags);
                    476: }
                    477: 
                    478: 
                    479: /* vmalloc management routines. */
                    480: struct vmalloc_struct
                    481: {
                    482:   struct vmalloc_struct *prev;
                    483:   struct vmalloc_struct *next;
                    484:   vm_offset_t start;
                    485:   vm_size_t size;
                    486: };
                    487: 
                    488: static struct vmalloc_struct
                    489: vmalloc_list = { &vmalloc_list, &vmalloc_list, 0, 0 };
                    490: 
                    491: static inline void
                    492: vmalloc_list_insert (vm_offset_t start, vm_size_t size)
                    493: {
                    494:   struct vmalloc_struct *p;
                    495: 
                    496:   p = (struct vmalloc_struct *) kalloc (sizeof (struct vmalloc_struct));
                    497:   if (p == NULL)
                    498:     panic ("kernel memory is exhausted");
                    499: 
                    500:   p->prev = vmalloc_list.prev;
                    501:   p->next = &vmalloc_list;
                    502:   vmalloc_list.prev->next = p;
                    503:   vmalloc_list.prev = p;
                    504: 
                    505:   p->start = start;
                    506:   p->size = size;
                    507: }
                    508: 
                    509: static struct vmalloc_struct *
                    510: vmalloc_list_lookup (vm_offset_t start)
                    511: {
                    512:   struct vmalloc_struct *p;
                    513: 
                    514:   for (p = vmalloc_list.next; p != &vmalloc_list; p = p->next)
                    515:     {
                    516:       if (p->start == start)
                    517:        return p;
                    518:     }
                    519: 
                    520:   return NULL;
                    521: }
                    522: 
                    523: static inline void
                    524: vmalloc_list_remove (struct vmalloc_struct *p)
                    525: {
                    526:   p->next->prev = p->prev;
                    527:   p->prev->next = p->next;
                    528: 
1.1.1.2   root      529:   kfree ((vm_offset_t) p, sizeof (struct vmalloc_struct));
1.1       root      530: }
                    531: 
                    532: /* Allocate SIZE bytes of memory.  The pages need not be contiguous.  */
                    533: void *
                    534: vmalloc (unsigned long size)
                    535: {
                    536:   kern_return_t ret;
                    537:   vm_offset_t addr;
                    538:   
                    539:   ret = kmem_alloc_wired (kernel_map, &addr, round_page (size));
                    540:   if (ret != KERN_SUCCESS)
                    541:     return NULL;
                    542: 
                    543:   vmalloc_list_insert (addr, round_page (size));
                    544:   return (void *) addr;
                    545: }
                    546: 
                    547: /* Free vmalloc'ed and vremap'ed virtual address space. */
                    548: void
                    549: vfree (void *addr)
                    550: {
                    551:   struct vmalloc_struct *p;
                    552: 
                    553:   p = vmalloc_list_lookup ((vm_offset_t) addr);
1.1.1.2   root      554:   if (!p)
1.1       root      555:     panic ("vmalloc_list_lookup failure");
                    556:   
1.1.1.2   root      557:   kmem_free (kernel_map, (vm_offset_t) addr, p->size);
1.1       root      558:   vmalloc_list_remove (p);
                    559: }
                    560: 
1.1.1.2   root      561: unsigned long
                    562: vmtophys (void *addr)
                    563: {
                    564:        return kvtophys((vm_offset_t) addr);
                    565: }
                    566: 
1.1       root      567: /* XXX: Quick hacking. */
                    568: /* Remap physical address into virtual address. */
                    569: void *
                    570: vremap (unsigned long offset, unsigned long size)
                    571: {
                    572:   extern vm_offset_t pmap_map_bd (register vm_offset_t virt,
                    573:                                  register vm_offset_t start,
                    574:                                  register vm_offset_t end,
                    575:                                  vm_prot_t prot);
                    576:   vm_offset_t addr;
                    577:   kern_return_t ret;
                    578:   
                    579:   ret = kmem_alloc_wired (kernel_map, &addr, round_page (size));
                    580:   if (ret != KERN_SUCCESS)
                    581:     return NULL;
                    582:   
                    583:   (void) pmap_map_bd (addr, offset, offset + round_page (size),
                    584:                      VM_PROT_READ | VM_PROT_WRITE);
                    585:   
                    586:   vmalloc_list_insert (addr, round_page (size));
                    587:   return (void *) addr;
                    588: }

unix.superglobalmegacorp.com

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