root/mm/vmalloc.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_pgdir
  2. free_area_pages
  3. alloc_area_pages
  4. do_area
  5. vfree
  6. vmalloc
  7. vread

   1 /*
   2  *  linux/mm/vmalloc.c
   3  *
   4  *  Copyright (C) 1993  Linus Torvalds
   5  */
   6 
   7 #include <asm/system.h>
   8 #include <linux/config.h>
   9 
  10 #include <linux/signal.h>
  11 #include <linux/sched.h>
  12 #include <linux/head.h>
  13 #include <linux/kernel.h>
  14 #include <linux/errno.h>
  15 #include <linux/types.h>
  16 #include <linux/malloc.h>
  17 #include <asm/segment.h>
  18 
  19 struct vm_struct {
  20         unsigned long flags;
  21         void * addr;
  22         unsigned long size;
  23         struct vm_struct * next;
  24 };
  25 
  26 static struct vm_struct * vmlist = NULL;
  27 
  28 #define VMALLOC_OFFSET  (8*1024*1024)
  29 
  30 static inline void set_pgdir(unsigned long dindex, unsigned long value)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32         struct task_struct * p;
  33 
  34         p = &init_task;
  35         do {
  36                 ((unsigned long *) p->tss.cr3)[dindex] = value;
  37                 p = p->next_task;
  38         } while (p != &init_task);
  39 }
  40 
  41 static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
     /* [previous][next][first][last][top][bottom][index][help] */
  42 {
  43         unsigned long page, *pte;
  44 
  45         if (!(PAGE_PRESENT & (page = swapper_pg_dir[dindex])))
  46                 return 0;
  47         page &= PAGE_MASK;
  48         pte = index + (unsigned long *) page;
  49         for ( ; nr > 0 ; nr--, pte++) {
  50                 unsigned long pg = *pte;
  51                 *pte = 0;
  52                 if (!(pg & PAGE_PRESENT))
  53                         continue;
  54                 free_page(pg);
  55         }
  56         pte = (unsigned long *) page;
  57         for (nr = 0 ; nr < 1024 ; nr++, pte++)
  58                 if (*pte)
  59                         return 0;
  60         set_pgdir(dindex,0);
  61         mem_map[MAP_NR(page)] &= ~MAP_PAGE_RESERVED;
  62         free_page(page);
  63         return 0;
  64 }
  65 
  66 static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68         unsigned long page, *pte;
  69 
  70         page = swapper_pg_dir[dindex];
  71         if (!page) {
  72                 page = get_free_page(GFP_KERNEL);
  73                 if (!page)
  74                         return -ENOMEM;
  75                 if (swapper_pg_dir[dindex]) {
  76                         free_page(page);
  77                         page = swapper_pg_dir[dindex];
  78                 } else {
  79                         mem_map[MAP_NR(page)] |= MAP_PAGE_RESERVED;
  80                         set_pgdir(dindex, page | PAGE_SHARED);
  81                 }
  82         }
  83         page &= PAGE_MASK;
  84         pte = index + (unsigned long *) page;
  85         *pte = PAGE_SHARED;             /* remove a race with vfree() */
  86         for ( ; nr > 0 ; nr--, pte++) {
  87                 unsigned long pg = get_free_page(GFP_KERNEL);
  88 
  89                 if (!pg)
  90                         return -ENOMEM;
  91                 *pte = pg | PAGE_SHARED;
  92         }
  93         return 0;
  94 }
  95 
  96 static int do_area(void * addr, unsigned long size,
     /* [previous][next][first][last][top][bottom][index][help] */
  97         int (*area_fn)(unsigned long,unsigned long,unsigned long))
  98 {
  99         unsigned long nr, dindex, index;
 100 
 101         nr = size >> PAGE_SHIFT;
 102         dindex = (TASK_SIZE + (unsigned long) addr) >> 22;
 103         index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
 104         while (nr > 0) {
 105                 unsigned long i = PTRS_PER_PAGE - index;
 106 
 107                 if (i > nr)
 108                         i = nr;
 109                 if (area_fn(dindex, index, i))
 110                         return -1;
 111                 nr -= i;
 112                 index = 0;
 113                 dindex++;
 114         }
 115         return 0;
 116 }
 117 
 118 void vfree(void * addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120         struct vm_struct **p, *tmp;
 121 
 122         if (!addr)
 123                 return;
 124         if ((PAGE_SIZE-1) & (unsigned long) addr) {
 125                 printk("Trying to vfree() bad address (%p)\n", addr);
 126                 return;
 127         }
 128         for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
 129                 if (tmp->addr == addr) {
 130                         *p = tmp->next;
 131                         do_area(tmp->addr, tmp->size, free_area_pages);
 132                         kfree(tmp);
 133                         return;
 134                 }
 135         }
 136         printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
 137 }
 138 
 139 void * vmalloc(unsigned long size)
     /* [previous][next][first][last][top][bottom][index][help] */
 140 {
 141         void * addr;
 142         struct vm_struct **p, *tmp, *area;
 143 
 144         size = PAGE_ALIGN(size);
 145         if (!size || size > high_memory)
 146                 return NULL;
 147         area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
 148         if (!area)
 149                 return NULL;
 150         addr = (void *) ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
 151         area->size = size + PAGE_SIZE;
 152         area->next = NULL;
 153         for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 154                 if (size + (unsigned long) addr <= (unsigned long) tmp->addr)
 155                         break;
 156                 addr = (void *) (tmp->size + (unsigned long) tmp->addr);
 157         }
 158         area->addr = addr;
 159         area->next = *p;
 160         *p = area;
 161         if (do_area(addr, size, alloc_area_pages)) {
 162                 vfree(addr);
 163                 return NULL;
 164         }
 165         return addr;
 166 }
 167 
 168 int vread(char *buf, char *addr, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170         struct vm_struct **p, *tmp;
 171         char *vaddr, *buf_start = buf;
 172         int n;
 173 
 174         for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 175                 vaddr = (char *) tmp->addr;
 176                 while (addr < vaddr) {
 177                         if (count == 0)
 178                                 goto finished;
 179                         put_fs_byte('\0', buf++), addr++, count--;
 180                 }
 181                 n = tmp->size - PAGE_SIZE;
 182                 if (addr > vaddr)
 183                         n -= addr - vaddr;
 184                 while (--n >= 0) {
 185                         if (count == 0)
 186                                 goto finished;
 187                         put_fs_byte(*addr++, buf++), count--;
 188                 }
 189         }
 190 finished:
 191         return buf - buf_start;
 192 }

/* [previous][next][first][last][top][bottom][index][help] */