Annotation of Gnu-Mach/linux/dev/glue/kmem.c, revision 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.