home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/mm/vmalloc.c
- *
- * Copyright (C) 1993 Linus Torvalds
- */
-
- #include <asm/system.h>
- #include <linux/config.h>
-
- #include <linux/signal.h>
- #include <linux/sched.h>
- #include <linux/head.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/types.h>
- #include <linux/malloc.h>
- #include <asm/segment.h>
-
- struct vm_struct {
- unsigned long flags;
- void * addr;
- unsigned long size;
- struct vm_struct * next;
- };
-
- static struct vm_struct * vmlist = NULL;
-
- #define VMALLOC_OFFSET (8*1024*1024)
-
- #ifdef __i386__
-
- static inline void set_pgdir(unsigned long dindex, unsigned long value)
- {
- struct task_struct * p;
-
- p = &init_task;
- do {
- ((unsigned long *) p->tss.cr3)[dindex] = value;
- p = p->next_task;
- } while (p != &init_task);
- }
-
- static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
- {
- unsigned long page, *pte;
-
- if (!(PAGE_PRESENT & (page = swapper_pg_dir[dindex])))
- return 0;
- page &= PAGE_MASK;
- pte = index + (unsigned long *) page;
- for ( ; nr > 0 ; nr--, pte++) {
- unsigned long pg = *pte;
- *pte = 0;
- if (!(pg & PAGE_PRESENT))
- continue;
- free_page(pg);
- }
- pte = (unsigned long *) page;
- for (nr = 0 ; nr < 1024 ; nr++, pte++)
- if (*pte)
- return 0;
- set_pgdir(dindex,0);
- mem_map[MAP_NR(page)] &= ~MAP_PAGE_RESERVED;
- free_page(page);
- return 0;
- }
-
- static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
- {
- unsigned long page, *pte;
-
- page = swapper_pg_dir[dindex];
- if (!page) {
- page = get_free_page(GFP_KERNEL);
- if (!page)
- return -ENOMEM;
- if (swapper_pg_dir[dindex]) {
- free_page(page);
- page = swapper_pg_dir[dindex];
- } else {
- mem_map[MAP_NR(page)] |= MAP_PAGE_RESERVED;
- set_pgdir(dindex, page | PAGE_SHARED);
- }
- }
- page &= PAGE_MASK;
- pte = index + (unsigned long *) page;
- *pte = PAGE_SHARED; /* remove a race with vfree() */
- for ( ; nr > 0 ; nr--, pte++) {
- unsigned long pg = get_free_page(GFP_KERNEL);
-
- if (!pg)
- return -ENOMEM;
- *pte = pg | PAGE_SHARED;
- }
- return 0;
- }
-
- static int do_area(void * addr, unsigned long size,
- int (*area_fn)(unsigned long,unsigned long,unsigned long))
- {
- unsigned long nr, dindex, index;
-
- nr = size >> PAGE_SHIFT;
- dindex = (TASK_SIZE + (unsigned long) addr) >> 22;
- index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
- while (nr > 0) {
- unsigned long i = PTRS_PER_PAGE - index;
-
- if (i > nr)
- i = nr;
- if (area_fn(dindex, index, i))
- return -1;
- nr -= i;
- index = 0;
- dindex++;
- }
- return 0;
- }
-
- #elif defined(__mc68000__)
-
- static int free_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
- {
- unsigned long ptr, *ptrp, page, *pte;
- extern unsigned long *krt;
-
- if (!(PAGE_TABLE & (ptr = krt[dindex >> 3])))
- return 0;
- ptrp = (unsigned long *)PTOV(ptr & TABLE_MASK);
- ptrp += (16*sizeof(long))*(dindex & (NUM_L2_ENTRIES-1));
- if (!(PAGE_TABLE & (page = ptrp[16*(dindex & (NUM_L2_ENTRIES-1))])))
- return 0;
- page = PTOV(page & PAGE_MASK);
- pte = index + (unsigned long *) page;
- for ( ; nr > 0 ; nr--, pte++) {
- unsigned long pg = *pte;
- *pte = 0;
- if (!(pg & PAGE_PRESENT))
- continue;
- free_page(PTOV(pg & PAGE_MASK));
- }
- pte = (unsigned long *) page;
- for (nr = 0 ; nr < PTRS_PER_PAGE ; nr++, pte++)
- if (*pte)
- return 0;
- mem_map[MAP_NR(page)] &= ~MAP_PAGE_RESERVED;
- free_page_table(&ptrp[16*(dindex & (NUM_L2_ENTRIES-1))]);
- return 0;
- }
-
- static unsigned long get_pt (unsigned long dindex)
- {
- unsigned long ptr, *ptrp, page;
- extern unsigned long *krt;
-
- ptr = krt[dindex >> 3];
- if (!ptr) {
- ptrp = get_pointer_table();
- if (!ptrp)
- return -ENOMEM;
- if (krt[dindex >> 3]) {
- free_pointer_table(ptrp);
- ptr = krt[dindex >> 3];
- } else
- krt[dindex >> 3] = VTOP(ptrp) | PAGE_TABLE;
- } else
- ptrp = (unsigned long *)PTOV(ptr & TABLE_MASK);
- page = ptrp[16*(dindex & (NUM_L2_ENTRIES-1))];
- if (!page) {
- page = (unsigned long)get_page_table (&ptrp[16*(dindex & (NUM_L2_ENTRIES-1))]);
- if (!page)
- return -ENOMEM;
- else
- mem_map[MAP_NR(page)] |= MAP_PAGE_RESERVED;
- } else
- page = PTOV(page & PAGE_MASK);
- return page;
- }
-
- static int alloc_area_pages(unsigned long dindex, unsigned long index, unsigned long nr)
- {
- unsigned long page, *pte;
-
- page = get_pt(dindex);
- pte = index + (unsigned long *) page;
- *pte = PAGE_SHARED; /* remove a race with vfree() */
- for ( ; nr > 0 ; nr--, pte++) {
- unsigned long pg = get_free_page(GFP_KERNEL);
-
- if (!pg)
- return -ENOMEM;
- *pte = VTOP(pg) | PAGE_SHARED;
- }
- return 0;
- }
-
- static int do_area(void * addr, unsigned long size,
- int (*area_fn)(unsigned long,unsigned long,unsigned long))
- {
- unsigned long nr, dindex, index;
-
- nr = size >> PAGE_SHIFT;
- dindex = ((unsigned long) addr) >> 22;
- index = (((unsigned long) addr) >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
- while (nr > 0) {
- unsigned long i = PTRS_PER_PAGE - index;
-
- if (i > nr)
- i = nr;
- if (area_fn(dindex, index, i))
- return -1;
- nr -= i;
- index = 0;
- dindex++;
- }
- return 0;
- }
- #endif
-
- void vfree(void * addr)
- {
- struct vm_struct **p, *tmp;
-
- if (!addr)
- return;
- if ((PAGE_SIZE-1) & (unsigned long) addr) {
- printk("Trying to vfree() bad address (%p)\n", addr);
- return;
- }
- for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
- if (tmp->addr == addr) {
- *p = tmp->next;
- do_area(tmp->addr, tmp->size, free_area_pages);
- kfree(tmp);
- return;
- }
- }
- printk("Trying to vfree() nonexistent vm area (%p)\n", addr);
- }
-
- void * vmalloc(unsigned long size)
- {
- void * addr;
- struct vm_struct **p, *tmp, *area;
-
- size = PAGE_ALIGN(size);
- if (!size || size > high_memory)
- return NULL;
- area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
- if (!area)
- return NULL;
- addr = (void *) ((high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1));
- area->size = size + PAGE_SIZE;
- area->next = NULL;
- for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
- if (size + (unsigned long) addr <= (unsigned long) tmp->addr)
- break;
- addr = (void *) (tmp->size + (unsigned long) tmp->addr);
- }
- area->addr = addr;
- area->next = *p;
- *p = area;
- if (do_area(addr, size, alloc_area_pages)) {
- vfree(addr);
- return NULL;
- }
- return addr;
- }
-
- int vread(char *buf, char *addr, int count)
- {
- struct vm_struct **p, *tmp;
- char *vaddr, *buf_start = buf;
- int n;
-
- for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
- vaddr = (char *) tmp->addr;
- while (addr < vaddr) {
- if (count == 0)
- goto finished;
- put_fs_byte('\0', buf++), addr++, count--;
- }
- n = tmp->size - PAGE_SIZE;
- if (addr > vaddr)
- n -= addr - vaddr;
- while (--n >= 0) {
- if (count == 0)
- goto finished;
- put_fs_byte(*addr++, buf++), count--;
- }
- }
- finished:
- return buf - buf_start;
- }
-