i386: fix iounmap's use of vm_struct's size field
authorJeremy Fitzhardinge <jeremy@goop.org>
Sat, 21 Jul 2007 15:11:35 +0000 (17:11 +0200)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 22 Jul 2007 01:37:14 +0000 (18:37 -0700)
get_vm_area always returns an area with an adjacent guard page.  That guard
page is included in vm_struct.size.  iounmap uses vm_struct.size to
determine how much address space needs to have change_page_attr applied to
it, which will BUG if applied to the guard page.

This patch adds a helper function - get_vm_area_size() in linux/vmalloc.h -
to return the actual size of a vm area, and uses it to make iounmap do the
right thing.  There are probably other places which should be using
get_vm_area_size().

Thanks to Dave Young <hidave.darkstar@gmail.com> for debugging the
problem.

[ Andi, it wasn't clear to me whether x86_64 needs the same fix. ]

Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
Cc: Dave Young <hidave.darkstar@gmail.com>
Cc: Chuck Ebbert <cebbert@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/i386/mm/ioremap.c
include/linux/vmalloc.h

index fff08ae7b5ed57c61767615decca493e3e9c7686..0b278315d73796e1707b94fdaa1bbba35a088881 100644 (file)
@@ -196,7 +196,7 @@ void iounmap(volatile void __iomem *addr)
        /* Reset the direct mapping. Can block */
        if ((p->flags >> 20) && p->phys_addr < virt_to_phys(high_memory) - 1) {
                change_page_attr(virt_to_page(__va(p->phys_addr)),
-                                p->size >> PAGE_SHIFT,
+                                get_vm_area_size(p) >> PAGE_SHIFT,
                                 PAGE_KERNEL);
                global_flush_tlb();
        } 
index c2b10cae5da52d06bf9c62a36aae5d36e24df6d4..89338b468d0de9c90d857e7e6154ecebcd3bc598 100644 (file)
@@ -58,6 +58,13 @@ void vmalloc_sync_all(void);
 /*
  *     Lowlevel-APIs (not for driver use!)
  */
+
+static inline size_t get_vm_area_size(const struct vm_struct *area)
+{
+       /* return actual size without guard page */
+       return area->size - PAGE_SIZE;
+}
+
 extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
 extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
                                        unsigned long start, unsigned long end);