linux-old/mm/vmalloc.c
<<
>>
Prefs
   1/*
   2 *  linux/mm/vmalloc.c
   3 *
   4 *  Copyright (C) 1993  Linus Torvalds
   5 */
   6
   7#include <linux/malloc.h>
   8#include <linux/vmalloc.h>
   9
  10#include <asm/uaccess.h>
  11
  12static struct vm_struct * vmlist = NULL;
  13
  14static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
  15{
  16        pte_t * pte;
  17        unsigned long end;
  18
  19        if (pmd_none(*pmd))
  20                return;
  21        if (pmd_bad(*pmd)) {
  22                printk("free_area_pte: bad pmd (%08lx)\n", pmd_val(*pmd));
  23                pmd_clear(pmd);
  24                return;
  25        }
  26        pte = pte_offset(pmd, address);
  27        address &= ~PMD_MASK;
  28        end = address + size;
  29        if (end > PMD_SIZE)
  30                end = PMD_SIZE;
  31        while (address < end) {
  32                pte_t page = *pte;
  33                pte_clear(pte);
  34                address += PAGE_SIZE;
  35                pte++;
  36                if (pte_none(page))
  37                        continue;
  38                if (pte_present(page)) {
  39                        free_page(pte_page(page));
  40                        continue;
  41                }
  42                printk("Whee.. Swapped out page in kernel page table\n");
  43        }
  44}
  45
  46static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
  47{
  48        pmd_t * pmd;
  49        unsigned long end;
  50
  51        if (pgd_none(*dir))
  52                return;
  53        if (pgd_bad(*dir)) {
  54                printk("free_area_pmd: bad pgd (%08lx)\n", pgd_val(*dir));
  55                pgd_clear(dir);
  56                return;
  57        }
  58        pmd = pmd_offset(dir, address);
  59        address &= ~PGDIR_MASK;
  60        end = address + size;
  61        if (end > PGDIR_SIZE)
  62                end = PGDIR_SIZE;
  63        while (address < end) {
  64                free_area_pte(pmd, address, end - address);
  65                address = (address + PMD_SIZE) & PMD_MASK;
  66                pmd++;
  67        }
  68}
  69
  70void vmfree_area_pages(unsigned long address, unsigned long size)
  71{
  72        pgd_t * dir;
  73        unsigned long start = address;
  74        unsigned long end = address + size;
  75
  76        dir = pgd_offset_k(address);
  77        flush_cache_all();
  78        while (address >= start && address < end) {
  79                free_area_pmd(dir, address, end - address);
  80                address = (address + PGDIR_SIZE) & PGDIR_MASK;
  81                dir++;
  82        }
  83        flush_tlb_all();
  84}
  85
  86static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size)
  87{
  88        unsigned long end;
  89
  90        address &= ~PMD_MASK;
  91        end = address + size;
  92        if (end > PMD_SIZE)
  93                end = PMD_SIZE;
  94        while (address < end) {
  95                unsigned long page;
  96                if (!pte_none(*pte))
  97                        printk("alloc_area_pte: page already exists\n");
  98                page = __get_free_page(GFP_KERNEL);
  99                if (!page)
 100                        return -ENOMEM;
 101                set_pte(pte, mk_pte(page, PAGE_KERNEL));
 102                address += PAGE_SIZE;
 103                pte++;
 104        }
 105        return 0;
 106}
 107
 108static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size)
 109{
 110        unsigned long end;
 111
 112        address &= ~PGDIR_MASK;
 113        end = address + size;
 114        if (end > PGDIR_SIZE)
 115                end = PGDIR_SIZE;
 116        while (address < end) {
 117                pte_t * pte = pte_alloc_kernel(pmd, address);
 118                if (!pte)
 119                        return -ENOMEM;
 120                if (alloc_area_pte(pte, address, end - address))
 121                        return -ENOMEM;
 122                address = (address + PMD_SIZE) & PMD_MASK;
 123                pmd++;
 124        }
 125        return 0;
 126}
 127
 128int vmalloc_area_pages(unsigned long address, unsigned long size)
 129{
 130        pgd_t * dir;
 131        unsigned long start = address;
 132        unsigned long end = address + size;
 133
 134        dir = pgd_offset_k(address);
 135        while (address >= start && address < end) {
 136                pmd_t *pmd;
 137                pgd_t olddir = *dir;
 138                
 139                pmd = pmd_alloc_kernel(dir, address);
 140                if (!pmd)
 141                        return -ENOMEM;
 142                if (alloc_area_pmd(pmd, address, end - address))
 143                        return -ENOMEM;
 144                if (pgd_val(olddir) != pgd_val(*dir))
 145                        set_pgdir(address, *dir);
 146                address = (address + PGDIR_SIZE) & PGDIR_MASK;
 147                dir++;
 148        }
 149        flush_cache_all();
 150        flush_tlb_all();
 151        return 0;
 152}
 153
 154struct vm_struct * get_vm_area(unsigned long size)
 155{
 156        unsigned long addr;
 157        struct vm_struct **p, *tmp, *area;
 158
 159        area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
 160        if (!area)
 161                return NULL;
 162        size += PAGE_SIZE;
 163        if (!size) {
 164                kfree(area);
 165                return NULL;
 166        }
 167        addr = VMALLOC_START;
 168        for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 169                if ((size + addr) < addr) {
 170                        kfree(area);
 171                        return NULL;
 172                }
 173                if (size + addr < (unsigned long) tmp->addr)
 174                        break;
 175                addr = tmp->size + (unsigned long) tmp->addr;
 176                if (addr > VMALLOC_END-size) {
 177                        kfree(area);
 178                        return NULL;
 179                }
 180        }
 181        area->addr = (void *)addr;
 182        area->size = size;
 183        area->next = *p;
 184        *p = area;
 185        return area;
 186}
 187
 188void vfree(void * addr)
 189{
 190        struct vm_struct **p, *tmp;
 191
 192        if (!addr)
 193                return;
 194        if ((PAGE_SIZE-1) & (unsigned long) addr) {
 195                printk("Trying to vfree() bad address (%p)\n", addr);
 196                return;
 197        }
 198        for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
 199                if (tmp->addr == addr) {
 200                        *p = tmp->next;
 201                        vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
 202                        kfree(tmp);
 203                        return;
 204                }
 205        }
 206        printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
 207}
 208
 209void * vmalloc(unsigned long size)
 210{
 211        void * addr;
 212        struct vm_struct *area;
 213
 214        size = PAGE_ALIGN(size);
 215        if (!size || size > (max_mapnr << PAGE_SHIFT))
 216                return NULL;
 217        area = get_vm_area(size);
 218        if (!area)
 219                return NULL;
 220        addr = area->addr;
 221        if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) {
 222                vfree(addr);
 223                return NULL;
 224        }
 225        return addr;
 226}
 227
 228long vread(char *buf, char *addr, unsigned long count)
 229{
 230        struct vm_struct *tmp;
 231        char *vaddr, *buf_start = buf;
 232        unsigned long n;
 233
 234        /* Don't allow overflow */
 235        if ((unsigned long) addr + count < count)
 236                count = -(unsigned long) addr;
 237
 238        for (tmp = vmlist; tmp; tmp = tmp->next) {
 239                vaddr = (char *) tmp->addr;
 240                if (addr >= vaddr + tmp->size - PAGE_SIZE)
 241                        continue;
 242                while (addr < vaddr) {
 243                        if (count == 0)
 244                                goto finished;
 245                        put_user('\0', buf);
 246                        buf++;
 247                        addr++;
 248                        count--;
 249                }
 250                n = vaddr + tmp->size - PAGE_SIZE - addr;
 251                do {
 252                        if (count == 0)
 253                                goto finished;
 254                        put_user(*addr, buf);
 255                        buf++;
 256                        addr++;
 257                        count--;
 258                } while (--n > 0);
 259        }
 260finished:
 261        return buf - buf_start;
 262}
 263
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.