Annotation of Gnu-Mach/i386/i386at/gpl/linux/linux_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: 
        !            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.