Commit 0bf7d47d authored by Marcus Alanen's avatar Marcus Alanen Committed by Linus Torvalds

[PATCH] vmalloc.c error path fixes

This fixes some problems in vmalloc.c.  The two first parts of the diff
fix a spinlock being held if an error occurs in map_vm_area, and the
last part fixes the error path of __vmalloc.
parent d17e9bb6
...@@ -153,15 +153,20 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages) ...@@ -153,15 +153,20 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)
unsigned long address = VMALLOC_VMADDR(area->addr); unsigned long address = VMALLOC_VMADDR(area->addr);
unsigned long end = address + (area->size-PAGE_SIZE); unsigned long end = address + (area->size-PAGE_SIZE);
pgd_t *dir; pgd_t *dir;
int err = 0;
dir = pgd_offset_k(address); dir = pgd_offset_k(address);
spin_lock(&init_mm.page_table_lock); spin_lock(&init_mm.page_table_lock);
do { do {
pmd_t *pmd = pmd_alloc(&init_mm, dir, address); pmd_t *pmd = pmd_alloc(&init_mm, dir, address);
if (!pmd) if (!pmd) {
return -ENOMEM; err = -ENOMEM;
if (map_area_pmd(pmd, address, end - address, prot, pages)) break;
return -ENOMEM; }
if (map_area_pmd(pmd, address, end - address, prot, pages)) {
err = -ENOMEM;
break;
}
address = (address + PGDIR_SIZE) & PGDIR_MASK; address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++; dir++;
...@@ -169,7 +174,7 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages) ...@@ -169,7 +174,7 @@ int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages)
spin_unlock(&init_mm.page_table_lock); spin_unlock(&init_mm.page_table_lock);
flush_cache_all(); flush_cache_all();
return 0; return err;
} }
...@@ -379,15 +384,21 @@ void *__vmalloc(unsigned long size, int gfp_mask, pgprot_t prot) ...@@ -379,15 +384,21 @@ void *__vmalloc(unsigned long size, int gfp_mask, pgprot_t prot)
area->nr_pages = nr_pages; area->nr_pages = nr_pages;
area->pages = pages = kmalloc(array_size, (gfp_mask & ~__GFP_HIGHMEM)); area->pages = pages = kmalloc(array_size, (gfp_mask & ~__GFP_HIGHMEM));
if (!area->pages) if (!area->pages) {
remove_vm_area(area->addr);
kfree(area);
return NULL; return NULL;
}
memset(area->pages, 0, array_size); memset(area->pages, 0, array_size);
for (i = 0; i < area->nr_pages; i++) { for (i = 0; i < area->nr_pages; i++) {
area->pages[i] = alloc_page(gfp_mask); area->pages[i] = alloc_page(gfp_mask);
if (unlikely(!area->pages[i])) if (unlikely(!area->pages[i])) {
/* Successfully allocated i pages, free them in __vunmap() */
area->nr_pages = i;
goto fail; goto fail;
} }
}
if (map_vm_area(area, prot, &pages)) if (map_vm_area(area, prot, &pages))
goto fail; goto fail;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment