Annotation of Gnu-Mach/i386/i386at/gpl/linux/linux_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: 
                     35: #include <i386at/gpl/linux/linux_emul.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: 
                     44: /* Amount of memory to reserve for Linux memory allocator.
                     45:    We reserve 64K chunks to stay within DMA limits.
                     46:    Increase MEM_CHUNKS if the kernel is running out of memory.  */
                     47: #define MEM_CHUNK_SIZE (64 * 1024)
                     48: #define MEM_CHUNKS     3
                     49: 
                     50: /* Mininum amount that linux_kmalloc will allocate.  */
                     51: #define MIN_ALLOC      12
                     52: 
                     53: #ifndef NBPW
                     54: #define NBPW           32
                     55: #endif
                     56: 
                     57: /* Memory block header.  */
                     58: struct blkhdr
                     59: {
                     60:   unsigned short free; /* 1 if block is free */
                     61:   unsigned short size; /* size of block */
                     62: };
                     63: 
                     64: /* This structure heads a page allocated by linux_kmalloc.  */
                     65: struct pagehdr
                     66: {
                     67:   unsigned size;       /* size (multiple of PAGE_SIZE) */
                     68:   struct pagehdr *next;        /* next header in list */
                     69: };
                     70: 
                     71: /* This structure describes a memory chunk.  */
                     72: struct chunkhdr
                     73: {
                     74:   unsigned long start; /* start address */
                     75:   unsigned long end;   /* end address */
                     76:   unsigned long bitmap;        /* busy/free bitmap of pages */
                     77: };
                     78: 
                     79: /* Chunks from which pages are allocated.  */
                     80: static struct chunkhdr pages_free[MEM_CHUNKS];
                     81: 
                     82: /* Memory list maintained by linux_kmalloc.  */
                     83: static struct pagehdr *memlist;
                     84: 
                     85: /* Some statistics.  */
                     86: int num_block_coalesce = 0;
                     87: int num_page_collect = 0;
                     88: int linux_mem_avail;
                     89: 
                     90: /* Initialize the Linux memory allocator.  */
                     91: void
                     92: linux_kmem_init ()
                     93: {
                     94:   int i, j;
                     95:   vm_page_t p, pages;
                     96: 
                     97:   for (i = 0; i < MEM_CHUNKS; i++)
                     98:     {
                     99:       /* Allocate memory.  */
                    100:       pages_free[i].start = (unsigned long) alloc_contig_mem (MEM_CHUNK_SIZE,
                    101:                                                              16 * 1024 * 1024,
                    102:                                                              0xffff, &pages);
                    103: 
                    104:       assert (pages_free[i].start);
                    105:       assert ((pages_free[i].start & 0xffff) == 0);
                    106: 
                    107:       /* Sanity check: ensure pages are contiguous and within DMA limits.  */
                    108:       for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE)
                    109:        {
                    110:          assert (p->phys_addr < 16 * 1024 * 1024);
                    111:          assert (p->phys_addr + PAGE_SIZE
                    112:                  == ((vm_page_t) p->pageq.next)->phys_addr);
                    113: 
                    114:          p = (vm_page_t) p->pageq.next;
                    115:        }
                    116: 
                    117:       pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE;
                    118: 
                    119:       /* Initialize free page bitmap.  */
                    120:       pages_free[i].bitmap = 0;
                    121:       j = MEM_CHUNK_SIZE >> PAGE_SHIFT;
                    122:       while (--j >= 0)
                    123:        pages_free[i].bitmap |= 1 << j;
                    124:     }
                    125: 
                    126:   linux_mem_avail = (MEM_CHUNKS * MEM_CHUNK_SIZE) >> PAGE_SHIFT;
                    127: }
                    128: 
                    129: /* Return the number by which the page size should be
                    130:    shifted such that the resulting value is >= SIZE.  */
                    131: static unsigned long
                    132: get_page_order (int size)
                    133: {
                    134:   unsigned long order;
                    135: 
                    136:   for (order = 0; (PAGE_SIZE << order) < size; order++)
                    137:     ;
                    138:   return order;
                    139: }
                    140: 
                    141: #ifdef LINUX_DEV_DEBUG
                    142: static void
                    143: check_page_list (int line)
                    144: {
                    145:   unsigned size;
                    146:   struct pagehdr *ph;
                    147:   struct blkhdr *bh;
                    148: 
                    149:   for (ph = memlist; ph; ph = ph->next)
                    150:     {
                    151:       if ((int) ph & PAGE_MASK)
                    152:        panic ("%s:%d: page header not aligned", __FILE__, line);
                    153: 
                    154:       size = 0;
                    155:       bh = (struct blkhdr *) (ph + 1);
                    156:       while (bh < (struct blkhdr *) ((void *) ph + ph->size))
                    157:        {
                    158:          size += bh->size + sizeof (struct blkhdr);
                    159:          bh = (void *) (bh + 1) + bh->size;
                    160:        }
                    161: 
                    162:       if (size + sizeof (struct pagehdr) != ph->size)
                    163:        panic ("%s:%d: memory list destroyed", __FILE__, line);
                    164:     }
                    165: }
                    166: #else
                    167: #define check_page_list(line)
                    168: #endif
                    169: 
                    170: /* Merge adjacent free blocks in the memory list.  */
                    171: static void
                    172: coalesce_blocks ()
                    173: {
                    174:   struct pagehdr *ph;
                    175:   struct blkhdr *bh, *bhp, *ebh;
                    176: 
                    177:   num_block_coalesce++;
                    178: 
                    179:   for (ph = memlist; ph; ph = ph->next)
                    180:     {
                    181:       bh = (struct blkhdr *) (ph + 1);
                    182:       ebh = (struct blkhdr *) ((void *) ph + ph->size);
                    183:       while (1)
                    184:        {
                    185:          /* Skip busy blocks.  */
                    186:          while (bh < ebh && ! bh->free)
                    187:            bh = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
                    188:          if (bh == ebh)
                    189:            break;
                    190: 
                    191:          /* Merge adjacent free blocks.  */
                    192:          while (1)
                    193:            {
                    194:              bhp = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
                    195:              if (bhp == ebh)
                    196:                {
                    197:                  bh = bhp;
                    198:                  break;
                    199:                }
                    200:              if (! bhp->free)
                    201:                {
                    202:                  bh = (struct blkhdr *) ((void *) (bhp + 1) + bhp->size);
                    203:                  break;
                    204:                }
                    205:              bh->size += bhp->size + sizeof (struct blkhdr);
                    206:            }
                    207:        }
                    208:     }
                    209: }
                    210: 
                    211: /* Allocate SIZE bytes of memory.
                    212:    The PRIORITY parameter specifies various flags
                    213:    such as DMA, atomicity, etc.  It is not used by Mach.  */
                    214: void *
                    215: linux_kmalloc (unsigned int size, int priority)
                    216: {
                    217:   int order, coalesced = 0;
                    218:   unsigned flags;
                    219:   struct pagehdr *ph;
                    220:   struct blkhdr *bh, *new_bh;
                    221: 
                    222:   if (size < MIN_ALLOC)
                    223:     size = MIN_ALLOC;
                    224:   else
                    225:     size = (size + sizeof (int) - 1) & ~(sizeof (int) - 1);
                    226: 
                    227:   assert (size <= (MEM_CHUNK_SIZE
                    228:                   - sizeof (struct pagehdr)
                    229:                   - sizeof (struct blkhdr)));
                    230: 
                    231:   save_flags (flags);
                    232:   cli ();
                    233: 
                    234: again:
                    235:   check_page_list (__LINE__);
                    236: 
                    237:   /* Walk the page list and find the first free block with size
                    238:      greater than or equal to the one required.  */
                    239:   for (ph = memlist; ph; ph = ph->next)
                    240:     {
                    241:       bh = (struct blkhdr *) (ph + 1);
                    242:       while (bh < (struct blkhdr *) ((void *) ph + ph->size))
                    243:        {
                    244:          if (bh->free && bh->size >= size)
                    245:            {
                    246:              bh->free = 0;
                    247:              if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
                    248:                {
                    249:                  /* Split the current block and create a new free block.  */
                    250:                  new_bh = (void *) (bh + 1) + size;
                    251:                  new_bh->free = 1;
                    252:                  new_bh->size = bh->size - size - sizeof (struct blkhdr);
                    253:                  bh->size = size;
                    254:                }
                    255: 
                    256:              check_page_list (__LINE__);
                    257: 
                    258:              restore_flags (flags);
                    259:              return bh + 1;
                    260:            }
                    261:          bh = (void *) (bh + 1) + bh->size;
                    262:        }
                    263:     }
                    264: 
                    265:   check_page_list (__LINE__); 
                    266: 
                    267:   /* Allocation failed; coalesce free blocks and try again.  */
                    268:   if (! coalesced)
                    269:     {
                    270:       coalesce_blocks ();
                    271:       coalesced = 1;
                    272:       goto again;
                    273:     }
                    274: 
                    275:   /* Allocate more pages.  */
                    276:   order = get_page_order (size
                    277:                          + sizeof (struct pagehdr)
                    278:                          + sizeof (struct blkhdr));
                    279:   ph = (struct pagehdr *) __get_free_pages (GFP_KERNEL, order, ~0UL);
                    280:   if (! ph)
                    281:     {
                    282:       restore_flags (flags);
                    283:       return NULL;
                    284:     }
                    285: 
                    286:   ph->size = PAGE_SIZE << order;
                    287:   ph->next = memlist;
                    288:   memlist = ph;
                    289:   bh = (struct blkhdr *) (ph + 1);
                    290:   bh->free = 0;
                    291:   bh->size = ph->size - sizeof (struct pagehdr) - sizeof (struct blkhdr);
                    292:   if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
                    293:     {
                    294:       new_bh = (void *) (bh + 1) + size;
                    295:       new_bh->free = 1;
                    296:       new_bh->size = bh->size - size - sizeof (struct blkhdr);
                    297:       bh->size = size;
                    298:     }
                    299: 
                    300:   check_page_list (__LINE__);
                    301: 
                    302:   restore_flags (flags);
                    303:   return bh + 1;
                    304: }
                    305: 
                    306: /* Free memory P previously allocated by linux_kmalloc.  */
                    307: void
                    308: linux_kfree (void *p)
                    309: {
                    310:   unsigned flags;
                    311:   struct blkhdr *bh, *bhp;
                    312:   struct pagehdr *ph;
                    313: 
                    314:   assert (((int) p & (sizeof (int) - 1)) == 0);
                    315: 
                    316:   save_flags (flags);
                    317:   cli ();
                    318: 
                    319:   check_page_list (__LINE__);
                    320: 
                    321:   for (ph = memlist; ph; ph = ph->next)
                    322:     if (p >= (void *) ph && p < (void *) ph + ph->size)
                    323:       break;
                    324: 
                    325:   assert (ph);
                    326: 
                    327:   bh = (struct blkhdr *) p - 1;
                    328: 
                    329:   assert (! bh->free);
                    330:   assert (bh->size >= MIN_ALLOC);
                    331:   assert ((bh->size & (sizeof (int) - 1)) == 0);
                    332: 
                    333:   bh->free = 1;
                    334: 
                    335:   check_page_list (__LINE__);
                    336: 
                    337:   restore_flags (flags);
                    338: }
                    339: 
                    340: /* Free any pages that are not in use.
                    341:    Called by __get_free_pages when pages are running low.  */
                    342: static void
                    343: collect_kmalloc_pages ()
                    344: {
                    345:   struct blkhdr *bh;
                    346:   struct pagehdr *ph, **prev_ph;
                    347: 
                    348:   check_page_list (__LINE__);
                    349: 
                    350:   coalesce_blocks ();
                    351: 
                    352:   check_page_list (__LINE__);
                    353: 
                    354:   ph = memlist;
                    355:   prev_ph = &memlist;
                    356:   while (ph)
                    357:     {
                    358:       bh = (struct blkhdr *) (ph + 1);
                    359:       if (bh->free && (void *) (bh + 1) + bh->size == (void *) ph + ph->size)
                    360:        {
                    361:          *prev_ph = ph->next;
                    362:          free_pages ((unsigned long) ph, get_page_order (ph->size));
                    363:          ph = *prev_ph;
                    364:        }
                    365:       else
                    366:        {
                    367:          prev_ph = &ph->next;
                    368:          ph = ph->next;
                    369:        }
                    370:     }
                    371: 
                    372:   check_page_list (__LINE__);
                    373: }
                    374: 
                    375: /* Allocate ORDER + 1 number of physically contiguous pages.
                    376:    PRIORITY and MAX_ADDR are not used in Mach.
                    377: 
                    378:    XXX: This needs to be dynamic.  To do that we need to make
                    379:    the Mach page manipulation routines interrupt safe and they
                    380:    must provide machine dependant hooks.  */
                    381: unsigned long
                    382: __get_free_pages (int priority, unsigned long order, unsigned long max_addr)
                    383: {
                    384:   int i, pages_collected = 0;
                    385:   unsigned flags, bits, off, j, len;
                    386: 
                    387:   assert ((PAGE_SIZE << order) <= MEM_CHUNK_SIZE);
                    388: 
                    389:   /* Construct bitmap of contiguous pages.  */
                    390:   bits = 0;
                    391:   j = 0;
                    392:   len = 0;
                    393:   while (len < (PAGE_SIZE << order))
                    394:     {
                    395:       bits |= 1 << j++;
                    396:       len += PAGE_SIZE;
                    397:     }
                    398: 
                    399: again:
                    400:   save_flags (flags);
                    401:   cli ();
                    402: 
                    403:   /* Search each chunk for the required number of contiguous pages.  */
                    404:   for (i = 0; i < MEM_CHUNKS; i++)
                    405:     {
                    406:       off = 0;
                    407:       j = bits;
                    408:       while (MEM_CHUNK_SIZE - off >= (PAGE_SIZE << order))
                    409:        {
                    410:          if ((pages_free[i].bitmap & j) == j)
                    411:            {
                    412:              pages_free[i].bitmap &= ~j;
                    413:              linux_mem_avail -= order + 1;
                    414:              restore_flags (flags);
                    415:              return pages_free[i].start + off;
                    416:            }
                    417:          j <<= 1;
                    418:          off += PAGE_SIZE;
                    419:        }
                    420:     }
                    421: 
                    422:   /* Allocation failed; collect kmalloc and buffer pages
                    423:      and try again.  */
                    424:   if (! pages_collected)
                    425:     {
                    426:       num_page_collect++;
                    427:       collect_kmalloc_pages ();
                    428:       collect_buffer_pages ();
                    429:       pages_collected = 1;
                    430:       goto again;
                    431:     }
                    432: 
                    433:   printf ("%s:%d: __get_free_pages: ran out of pages\n", __FILE__, __LINE__);
                    434: 
                    435:   restore_flags (flags);
                    436:   return 0;
                    437: }
                    438: 
                    439: /* Free ORDER + 1 number of physically
                    440:    contiguous pages starting at address ADDR.  */
                    441: void
                    442: free_pages (unsigned long addr, unsigned long order)
                    443: {
                    444:   int i;
                    445:   unsigned flags, bits, len, j;
                    446: 
                    447:   assert ((addr & PAGE_MASK) == 0);
                    448: 
                    449:   for (i = 0; i < MEM_CHUNKS; i++)
                    450:     if (addr >= pages_free[i].start && addr < pages_free[i].end)
                    451:       break;
                    452: 
                    453:   assert (i < MEM_CHUNKS);
                    454: 
                    455:   /* Contruct bitmap of contiguous pages.  */
                    456:   len = 0;
                    457:   j = 0;
                    458:   bits = 0;
                    459:   while (len < (PAGE_SIZE << order))
                    460:     {
                    461:       bits |= 1 << j++;
                    462:       len += PAGE_SIZE;
                    463:     }
                    464:   bits <<= (addr - pages_free[i].start) >> PAGE_SHIFT;
                    465: 
                    466:   save_flags (flags);
                    467:   cli ();
                    468: 
                    469:   assert ((pages_free[i].bitmap & bits) == 0);
                    470: 
                    471:   pages_free[i].bitmap |= bits;
                    472:   linux_mem_avail += order + 1;
                    473:   restore_flags (flags);
                    474: }
                    475: 
                    476: /* Allocate SIZE bytes of memory.  The pages need not be contiguous.  */
                    477: void *
                    478: vmalloc (unsigned long size)
                    479: {
                    480:   return (void *) __get_free_pages (GFP_KERNEL, get_page_order (size), ~0UL);
                    481: }

unix.superglobalmegacorp.com

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