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#include <asm/system.h>
  12
  13static struct vm_struct * vmlist = NULL;
  14
  15static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
  16{
  17        pte_t * pte;
  18        unsigned long end;
  19
  20        if (pmd_none(*pmd))
  21                return;
  22        if (pmd_bad(*pmd)) {
  23                printk("free_area_pte: bad pmd (%08lx)\n", pmd_val(*pmd));
  24                pmd_clear(pmd);
  25                return;
  26        }
  27        pte = pte_offset(pmd, address);
  28        address &= ~PMD_MASK;
  29        end = address + size;
  30        if (end > PMD_SIZE)
  31                end = PMD_SIZE;
  32        while (address < end) {
  33                pte_t page = *pte;
  34                pte_clear(pte);
  35                address += PAGE_SIZE;
  36                pte++;
  37                if (pte_none(page))
  38                        continue;
  39                if (pte_present(page)) {
  40                        free_page(pte_page(page));
  41                        continue;
  42                }
  43                printk("Whee.. Swapped out page in kernel page table\n");
  44        }
  45}
  46
  47static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
  48{
  49        pmd_t * pmd;
  50        unsigned long end;
  51
  52        if (pgd_none(*dir))
  53                return;
  54        if (pgd_bad(*dir)) {
  55                printk("free_area_pmd: bad pgd (%08lx)\n", pgd_val(*dir));
  56                pgd_clear(dir);
  57                return;
  58        }
  59        pmd = pmd_offset(dir, address);
  60        address &= ~PGDIR_MASK;
  61        end = address + size;
  62        if (end > PGDIR_SIZE)
  63                end = PGDIR_SIZE;
  64        while (address < end) {
  65                free_area_pte(pmd, address, end - address);
  66                address = (address + PMD_SIZE) & PMD_MASK;
  67                pmd++;
  68        }
  69}
  70
  71void vmfree_area_pages(unsigned long address, unsigned long size)
  72{
  73        pgd_t * dir;
  74        unsigned long end = address + size;
  75
  76        dir = pgd_offset_k(address);
  77        flush_cache_all();
  78        while (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 end = address + size;
 132
 133        dir = pgd_offset_k(address);
 134        flush_cache_all();
 135        while (address < end) {
 136                pmd_t *pmd = pmd_alloc_kernel(dir, address);
 137                if (!pmd)
 138                        return -ENOMEM;
 139                if (alloc_area_pmd(pmd, address, end - address))
 140                        return -ENOMEM;
 141                set_pgdir(address, *dir);
 142                address = (address + PGDIR_SIZE) & PGDIR_MASK;
 143                dir++;
 144        }
 145        flush_tlb_all();
 146        return 0;
 147}
 148
 149struct vm_struct * get_vm_area(unsigned long size)
 150{
 151        void *addr;
 152        struct vm_struct **p, *tmp, *area;
 153
 154        area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
 155        if (!area)
 156                return NULL;
 157        addr = (void *) VMALLOC_START;
 158        area->size = size + PAGE_SIZE;
 159        area->next = NULL;
 160        for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 161                if (size + (unsigned long) addr < (unsigned long) tmp->addr)
 162                        break;
 163                addr = (void *) (tmp->size + (unsigned long) tmp->addr);
 164        }
 165        area->addr = addr;
 166        area->next = *p;
 167        *p = area;
 168        return area;
 169}
 170
 171void vfree(void * addr)
 172{
 173        struct vm_struct **p, *tmp;
 174
 175        if (!addr)
 176                return;
 177        if ((PAGE_SIZE-1) & (unsigned long) addr) {
 178                printk("Trying to vfree() bad address (%p)\n", addr);
 179                return;
 180        }
 181        for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
 182                if (tmp->addr == addr) {
 183                        *p = tmp->next;
 184                        vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
 185                        kfree(tmp);
 186                        return;
 187                }
 188        }
 189        printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
 190}
 191
 192void * vmalloc(unsigned long size)
 193{
 194        void * addr;
 195        struct vm_struct *area;
 196
 197        size = PAGE_ALIGN(size);
 198        if (!size || size > (max_mapnr << PAGE_SHIFT))
 199                return NULL;
 200        area = get_vm_area(size);
 201        if (!area)
 202                return NULL;
 203        addr = area->addr;
 204        if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) {
 205                vfree(addr);
 206                return NULL;
 207        }
 208        return addr;
 209}
 210
 211int vread(char *buf, char *addr, int count)
 212{
 213        struct vm_struct **p, *tmp;
 214        char *vaddr, *buf_start = buf;
 215        int n;
 216
 217        for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
 218                vaddr = (char *) tmp->addr;
 219                while (addr < vaddr) {
 220                        if (count == 0)
 221                                goto finished;
 222                        put_user('\0', buf);
 223                        buf++;
 224                        addr++;
 225                        count--;
 226                }
 227                n = tmp->size - PAGE_SIZE;
 228                if (addr > vaddr)
 229                        n -= addr - vaddr;
 230                while (--n >= 0) {
 231                        if (count == 0)
 232                                goto finished;
 233                        put_user(*addr, buf);
 234                        buf++;
 235                        addr++;
 236                        count--;
 237                }
 238        }
 239finished:
 240        return buf - buf_start;
 241}
 242
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.