patch-1.3.10 linux/mm/vmalloc.c
Next file: linux/net/Changes
Previous file: linux/include/net/tcp.h
Back to the patch index
Back to the overall index
- Lines: 148
- Date:
Wed Jul 12 08:32:17 1995
- Orig file:
v1.3.9/linux/mm/vmalloc.c
- Orig date:
Tue Jun 27 14:11:47 1995
diff -u --recursive --new-file v1.3.9/linux/mm/vmalloc.c linux/mm/vmalloc.c
@@ -167,6 +167,89 @@
return 0;
}
+static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
+ unsigned long offset)
+{
+ unsigned long end;
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ do {
+ if (!pte_none(*pte))
+ printk("remap_area_pte: page already exists\n");
+ *pte = mk_pte(offset, PAGE_KERNEL);
+ address += PAGE_SIZE;
+ offset += PAGE_SIZE;
+ pte++;
+ } while (address < end);
+}
+
+static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
+ unsigned long offset)
+{
+ unsigned long end;
+
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+ offset -= address;
+ do {
+ pte_t * pte = pte_alloc_kernel(pmd, address);
+ if (!pte)
+ return -ENOMEM;
+ remap_area_pte(pte, address, end - address, address + offset);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address < end);
+ return 0;
+}
+
+static int remap_area_pages(unsigned long address, unsigned long offset, unsigned long size)
+{
+ pgd_t * dir;
+ unsigned long end = address + size;
+
+ offset -= address;
+ dir = pgd_offset(&init_task, address);
+ while (address < end) {
+ pmd_t *pmd = pmd_alloc_kernel(dir, address);
+ if (!pmd)
+ return -ENOMEM;
+ if (remap_area_pmd(pmd, address, end - address, offset + address))
+ return -ENOMEM;
+ set_pgdir(address, *dir);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ dir++;
+ }
+ invalidate();
+ return 0;
+}
+
+static struct vm_struct * get_vm_area(unsigned long size)
+{
+ void *addr;
+ struct vm_struct **p, *tmp, *area;
+
+ area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
+ if (!area)
+ return NULL;
+ addr = (void *) VMALLOC_START;
+ 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;
+ return area;
+}
+
void vfree(void * addr)
{
struct vm_struct **p, *tmp;
@@ -191,26 +274,44 @@
void * vmalloc(unsigned long size)
{
void * addr;
- struct vm_struct **p, *tmp, *area;
+ struct vm_struct *area;
size = PAGE_ALIGN(size);
if (!size || size > high_memory)
return NULL;
- area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
+ area = get_vm_area(size);
if (!area)
return NULL;
- addr = (void *) VMALLOC_START;
- 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;
+ addr = area->addr;
if (alloc_area_pages(VMALLOC_VMADDR(addr), size)) {
+ vfree(addr);
+ return NULL;
+ }
+ return addr;
+}
+
+/*
+ * Remap an arbitrary physical address space into the kernel virtual
+ * address space. Needed when the kernel wants to access high addresses
+ * directly.
+ */
+void * vremap(unsigned long offset, unsigned long size)
+{
+ void * addr;
+ struct vm_struct * area;
+
+ if (offset < high_memory)
+ return NULL;
+ if (offset & ~PAGE_MASK)
+ return NULL;
+ size = PAGE_ALIGN(size);
+ if (!size || size > offset + size)
+ return NULL;
+ area = get_vm_area(size);
+ if (!area)
+ return NULL;
+ addr = area->addr;
+ if (remap_area_pages(VMALLOC_VMADDR(addr), offset, size)) {
vfree(addr);
return NULL;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this