From 898ad89a9a9dcbb9061d155ab1060d0812bf11c3 Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Date: Tue, 23 Dec 2008 21:50:11 +0530 Subject: [PATCH] --- yaml --- r: 121032 b: refs/heads/master c: 1fcccb008be12ea823aaa392758e1e41fb82de9a h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/Documentation/x86/pat.txt | 24 --- trunk/arch/x86/include/asm/pgtable.h | 14 -- trunk/arch/x86/include/asm/pgtable_32.h | 9 + trunk/arch/x86/include/asm/pgtable_64.h | 6 + trunk/arch/x86/kernel/traps.c | 32 ++-- trunk/arch/x86/mm/pat.c | 236 ------------------------ trunk/include/asm-generic/pgtable.h | 50 ----- trunk/include/linux/mm.h | 19 -- trunk/mm/memory.c | 70 +++---- 10 files changed, 52 insertions(+), 410 deletions(-) diff --git a/[refs] b/[refs] index 04421fe3ce41..a480455f82c1 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: c1c15b65ec30275575dac9322aae607075769fbc +refs/heads/master: 1fcccb008be12ea823aaa392758e1e41fb82de9a diff --git a/trunk/Documentation/x86/pat.txt b/trunk/Documentation/x86/pat.txt index cf08c9fff3cd..c93ff5f4c0dd 100644 --- a/trunk/Documentation/x86/pat.txt +++ b/trunk/Documentation/x86/pat.txt @@ -80,30 +80,6 @@ pci proc | -- | -- | WC | | | | | ------------------------------------------------------------------- -Advanced APIs for drivers -------------------------- -A. Exporting pages to users with remap_pfn_range, io_remap_pfn_range, -vm_insert_pfn - -Drivers wanting to export some pages to userspace do it by using mmap -interface and a combination of -1) pgprot_noncached() -2) io_remap_pfn_range() or remap_pfn_range() or vm_insert_pfn() - -With PAT support, a new API pgprot_writecombine is being added. So, drivers can -continue to use the above sequence, with either pgprot_noncached() or -pgprot_writecombine() in step 1, followed by step 2. - -In addition, step 2 internally tracks the region as UC or WC in memtype -list in order to ensure no conflicting mapping. - -Note that this set of APIs only works with IO (non RAM) regions. If driver -wants to export a RAM region, it has to do set_memory_uc() or set_memory_wc() -as step 0 above and also track the usage of those pages and use set_memory_wb() -before the page is freed to free pool. - - - Notes: -- in the above table mean "Not suggested usage for the API". Some of the --'s diff --git a/trunk/arch/x86/include/asm/pgtable.h b/trunk/arch/x86/include/asm/pgtable.h index 875192bf72cb..c012f3b11671 100644 --- a/trunk/arch/x86/include/asm/pgtable.h +++ b/trunk/arch/x86/include/asm/pgtable.h @@ -158,19 +158,8 @@ #define PGD_IDENT_ATTR 0x001 /* PRESENT (no other attributes) */ #endif -/* - * Macro to mark a page protection value as UC- - */ -#define pgprot_noncached(prot) \ - ((boot_cpu_data.x86 > 3) \ - ? (__pgprot(pgprot_val(prot) | _PAGE_CACHE_UC_MINUS)) \ - : (prot)) - #ifndef __ASSEMBLY__ -#define pgprot_writecombine pgprot_writecombine -extern pgprot_t pgprot_writecombine(pgprot_t prot); - /* * ZERO_PAGE is a global shared page that is always zero: used * for zero-mapped memory areas etc.. @@ -340,9 +329,6 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot) #define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask) #ifndef __ASSEMBLY__ -/* Indicate that x86 has its own track and untrack pfn vma functions */ -#define __HAVE_PFNMAP_TRACKING - #define __HAVE_PHYS_MEM_ACCESS_PROT struct file; pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, diff --git a/trunk/arch/x86/include/asm/pgtable_32.h b/trunk/arch/x86/include/asm/pgtable_32.h index 72b020deb46b..f9d5889b336b 100644 --- a/trunk/arch/x86/include/asm/pgtable_32.h +++ b/trunk/arch/x86/include/asm/pgtable_32.h @@ -100,6 +100,15 @@ extern unsigned long pg0[]; # include #endif +/* + * Macro to mark a page protection value as "uncacheable". + * On processors which do not support it, this is a no-op. + */ +#define pgprot_noncached(prot) \ + ((boot_cpu_data.x86 > 3) \ + ? (__pgprot(pgprot_val(prot) | _PAGE_PCD | _PAGE_PWT)) \ + : (prot)) + /* * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. diff --git a/trunk/arch/x86/include/asm/pgtable_64.h b/trunk/arch/x86/include/asm/pgtable_64.h index 4798a4033e3a..545a0e042bb2 100644 --- a/trunk/arch/x86/include/asm/pgtable_64.h +++ b/trunk/arch/x86/include/asm/pgtable_64.h @@ -176,6 +176,12 @@ static inline int pmd_bad(pmd_t pmd) #define pages_to_mb(x) ((x) >> (20 - PAGE_SHIFT)) /* FIXME: is this right? */ +/* + * Macro to mark a page protection value as "uncacheable". + */ +#define pgprot_noncached(prot) \ + (__pgprot(pgprot_val((prot)) | _PAGE_PCD | _PAGE_PWT)) + /* * Conversion functions: convert a page and protection to a page entry, * and a page entry and page directory to the page they refer to. diff --git a/trunk/arch/x86/kernel/traps.c b/trunk/arch/x86/kernel/traps.c index 04d242ab0161..f37cee75ab58 100644 --- a/trunk/arch/x86/kernel/traps.c +++ b/trunk/arch/x86/kernel/traps.c @@ -664,7 +664,7 @@ void math_error(void __user *ip) { struct task_struct *task; siginfo_t info; - unsigned short cwd, swd; + unsigned short cwd, swd, err; /* * Save the info for the exception handler and clear the error. @@ -675,7 +675,6 @@ void math_error(void __user *ip) task->thread.error_code = 0; info.si_signo = SIGFPE; info.si_errno = 0; - info.si_code = __SI_FAULT; info.si_addr = ip; /* * (~cwd & swd) will mask out exceptions that are not set to unmasked @@ -689,34 +688,31 @@ void math_error(void __user *ip) */ cwd = get_fpu_cwd(task); swd = get_fpu_swd(task); - switch (swd & ~cwd & 0x3f) { - case 0x000: /* No unmasked exception */ + + err = swd & ~cwd & 0x3f; + #ifdef CONFIG_X86_32 + if (!err) return; #endif - default: /* Multiple exceptions */ - break; - case 0x001: /* Invalid Op */ + + if (err & 0x001) { /* Invalid op */ /* * swd & 0x240 == 0x040: Stack Underflow * swd & 0x240 == 0x240: Stack Overflow * User must clear the SF bit (0x40) if set */ info.si_code = FPE_FLTINV; - break; - case 0x002: /* Denormalize */ - case 0x010: /* Underflow */ - info.si_code = FPE_FLTUND; - break; - case 0x004: /* Zero Divide */ + } else if (err & 0x004) { /* Divide by Zero */ info.si_code = FPE_FLTDIV; - break; - case 0x008: /* Overflow */ + } else if (err & 0x008) { /* Overflow */ info.si_code = FPE_FLTOVF; - break; - case 0x020: /* Precision */ + } else if (err & 0x012) { /* Denormal, Underflow */ + info.si_code = FPE_FLTUND; + } else if (err & 0x020) { /* Precision */ info.si_code = FPE_FLTRES; - break; + } else { + info.si_code = __SI_FAULT|SI_KERNEL; /* WTF? */ } force_sig_info(SIGFPE, &info, task); } diff --git a/trunk/arch/x86/mm/pat.c b/trunk/arch/x86/mm/pat.c index 85cbd3cd3723..eb1bf000d12e 100644 --- a/trunk/arch/x86/mm/pat.c +++ b/trunk/arch/x86/mm/pat.c @@ -596,242 +596,6 @@ void unmap_devmem(unsigned long pfn, unsigned long size, pgprot_t vma_prot) free_memtype(addr, addr + size); } -/* - * Internal interface to reserve a range of physical memory with prot. - * Reserved non RAM regions only and after successful reserve_memtype, - * this func also keeps identity mapping (if any) in sync with this new prot. - */ -static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot) -{ - int is_ram = 0; - int id_sz, ret; - unsigned long flags; - unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK); - - is_ram = pagerange_is_ram(paddr, paddr + size); - - if (is_ram != 0) { - /* - * For mapping RAM pages, drivers need to call - * set_memory_[uc|wc|wb] directly, for reserve and free, before - * setting up the PTE. - */ - WARN_ON_ONCE(1); - return 0; - } - - ret = reserve_memtype(paddr, paddr + size, want_flags, &flags); - if (ret) - return ret; - - if (flags != want_flags) { - free_memtype(paddr, paddr + size); - printk(KERN_ERR - "%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n", - current->comm, current->pid, - cattr_name(want_flags), - (unsigned long long)paddr, - (unsigned long long)(paddr + size), - cattr_name(flags)); - return -EINVAL; - } - - /* Need to keep identity mapping in sync */ - if (paddr >= __pa(high_memory)) - return 0; - - id_sz = (__pa(high_memory) < paddr + size) ? - __pa(high_memory) - paddr : - size; - - if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) { - free_memtype(paddr, paddr + size); - printk(KERN_ERR - "%s:%d reserve_pfn_range ioremap_change_attr failed %s " - "for %Lx-%Lx\n", - current->comm, current->pid, - cattr_name(flags), - (unsigned long long)paddr, - (unsigned long long)(paddr + size)); - return -EINVAL; - } - return 0; -} - -/* - * Internal interface to free a range of physical memory. - * Frees non RAM regions only. - */ -static void free_pfn_range(u64 paddr, unsigned long size) -{ - int is_ram; - - is_ram = pagerange_is_ram(paddr, paddr + size); - if (is_ram == 0) - free_memtype(paddr, paddr + size); -} - -/* - * track_pfn_vma_copy is called when vma that is covering the pfnmap gets - * copied through copy_page_range(). - * - * If the vma has a linear pfn mapping for the entire range, we get the prot - * from pte and reserve the entire vma range with single reserve_pfn_range call. - * Otherwise, we reserve the entire vma range, my ging through the PTEs page - * by page to get physical address and protection. - */ -int track_pfn_vma_copy(struct vm_area_struct *vma) -{ - int retval = 0; - unsigned long i, j; - resource_size_t paddr; - unsigned long prot; - unsigned long vma_start = vma->vm_start; - unsigned long vma_end = vma->vm_end; - unsigned long vma_size = vma_end - vma_start; - - if (!pat_enabled) - return 0; - - if (is_linear_pfn_mapping(vma)) { - /* - * reserve the whole chunk covered by vma. We need the - * starting address and protection from pte. - */ - if (follow_phys(vma, vma_start, 0, &prot, &paddr)) { - WARN_ON_ONCE(1); - return -EINVAL; - } - return reserve_pfn_range(paddr, vma_size, __pgprot(prot)); - } - - /* reserve entire vma page by page, using pfn and prot from pte */ - for (i = 0; i < vma_size; i += PAGE_SIZE) { - if (follow_phys(vma, vma_start + i, 0, &prot, &paddr)) - continue; - - retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot)); - if (retval) - goto cleanup_ret; - } - return 0; - -cleanup_ret: - /* Reserve error: Cleanup partial reservation and return error */ - for (j = 0; j < i; j += PAGE_SIZE) { - if (follow_phys(vma, vma_start + j, 0, &prot, &paddr)) - continue; - - free_pfn_range(paddr, PAGE_SIZE); - } - - return retval; -} - -/* - * track_pfn_vma_new is called when a _new_ pfn mapping is being established - * for physical range indicated by pfn and size. - * - * prot is passed in as a parameter for the new mapping. If the vma has a - * linear pfn mapping for the entire range reserve the entire vma range with - * single reserve_pfn_range call. - * Otherwise, we look t the pfn and size and reserve only the specified range - * page by page. - * - * Note that this function can be called with caller trying to map only a - * subrange/page inside the vma. - */ -int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot, - unsigned long pfn, unsigned long size) -{ - int retval = 0; - unsigned long i, j; - resource_size_t base_paddr; - resource_size_t paddr; - unsigned long vma_start = vma->vm_start; - unsigned long vma_end = vma->vm_end; - unsigned long vma_size = vma_end - vma_start; - - if (!pat_enabled) - return 0; - - if (is_linear_pfn_mapping(vma)) { - /* reserve the whole chunk starting from vm_pgoff */ - paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; - return reserve_pfn_range(paddr, vma_size, prot); - } - - /* reserve page by page using pfn and size */ - base_paddr = (resource_size_t)pfn << PAGE_SHIFT; - for (i = 0; i < size; i += PAGE_SIZE) { - paddr = base_paddr + i; - retval = reserve_pfn_range(paddr, PAGE_SIZE, prot); - if (retval) - goto cleanup_ret; - } - return 0; - -cleanup_ret: - /* Reserve error: Cleanup partial reservation and return error */ - for (j = 0; j < i; j += PAGE_SIZE) { - paddr = base_paddr + j; - free_pfn_range(paddr, PAGE_SIZE); - } - - return retval; -} - -/* - * untrack_pfn_vma is called while unmapping a pfnmap for a region. - * untrack can be called for a specific region indicated by pfn and size or - * can be for the entire vma (in which case size can be zero). - */ -void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, - unsigned long size) -{ - unsigned long i; - resource_size_t paddr; - unsigned long prot; - unsigned long vma_start = vma->vm_start; - unsigned long vma_end = vma->vm_end; - unsigned long vma_size = vma_end - vma_start; - - if (!pat_enabled) - return; - - if (is_linear_pfn_mapping(vma)) { - /* free the whole chunk starting from vm_pgoff */ - paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; - free_pfn_range(paddr, vma_size); - return; - } - - if (size != 0 && size != vma_size) { - /* free page by page, using pfn and size */ - paddr = (resource_size_t)pfn << PAGE_SHIFT; - for (i = 0; i < size; i += PAGE_SIZE) { - paddr = paddr + i; - free_pfn_range(paddr, PAGE_SIZE); - } - } else { - /* free entire vma, page by page, using the pfn from pte */ - for (i = 0; i < vma_size; i += PAGE_SIZE) { - if (follow_phys(vma, vma_start + i, 0, &prot, &paddr)) - continue; - - free_pfn_range(paddr, PAGE_SIZE); - } - } -} - -pgprot_t pgprot_writecombine(pgprot_t prot) -{ - if (pat_enabled) - return __pgprot(pgprot_val(prot) | _PAGE_CACHE_WC); - else - return pgprot_noncached(prot); -} - #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT) /* get Nth element of the linked list */ diff --git a/trunk/include/asm-generic/pgtable.h b/trunk/include/asm-generic/pgtable.h index 72ebe91005a8..ef87f889ef62 100644 --- a/trunk/include/asm-generic/pgtable.h +++ b/trunk/include/asm-generic/pgtable.h @@ -129,10 +129,6 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addres #define move_pte(pte, prot, old_addr, new_addr) (pte) #endif -#ifndef pgprot_writecombine -#define pgprot_writecombine pgprot_noncached -#endif - /* * When walking page tables, get the address of the next boundary, * or the end address of the range if that comes earlier. Although no @@ -293,52 +289,6 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, #define arch_flush_lazy_cpu_mode() do {} while (0) #endif -#ifndef __HAVE_PFNMAP_TRACKING -/* - * Interface that can be used by architecture code to keep track of - * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) - * - * track_pfn_vma_new is called when a _new_ pfn mapping is being established - * for physical range indicated by pfn and size. - */ -static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot, - unsigned long pfn, unsigned long size) -{ - return 0; -} - -/* - * Interface that can be used by architecture code to keep track of - * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) - * - * track_pfn_vma_copy is called when vma that is covering the pfnmap gets - * copied through copy_page_range(). - */ -static inline int track_pfn_vma_copy(struct vm_area_struct *vma) -{ - return 0; -} - -/* - * Interface that can be used by architecture code to keep track of - * memory type of pfn mappings (remap_pfn_range, vm_insert_pfn) - * - * untrack_pfn_vma is called while unmapping a pfnmap for a region. - * untrack can be called for a specific region indicated by pfn and size or - * can be for the entire vma (in which case size can be zero). - */ -static inline void untrack_pfn_vma(struct vm_area_struct *vma, - unsigned long pfn, unsigned long size) -{ -} -#else -extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot, - unsigned long pfn, unsigned long size); -extern int track_pfn_vma_copy(struct vm_area_struct *vma); -extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, - unsigned long size); -#endif - #endif /* !__ASSEMBLY__ */ #endif /* _ASM_GENERIC_PGTABLE_H */ diff --git a/trunk/include/linux/mm.h b/trunk/include/linux/mm.h index d3ddd735e375..ffee2f743418 100644 --- a/trunk/include/linux/mm.h +++ b/trunk/include/linux/mm.h @@ -145,23 +145,6 @@ extern pgprot_t protection_map[16]; #define FAULT_FLAG_WRITE 0x01 /* Fault was a write access */ #define FAULT_FLAG_NONLINEAR 0x02 /* Fault was via a nonlinear mapping */ -/* - * This interface is used by x86 PAT code to identify a pfn mapping that is - * linear over entire vma. This is to optimize PAT code that deals with - * marking the physical region with a particular prot. This is not for generic - * mm use. Note also that this check will not work if the pfn mapping is - * linear for a vma starting at physical address 0. In which case PAT code - * falls back to slow path of reserving physical range page by page. - */ -static inline int is_linear_pfn_mapping(struct vm_area_struct *vma) -{ - return ((vma->vm_flags & VM_PFNMAP) && vma->vm_pgoff); -} - -static inline int is_pfn_mapping(struct vm_area_struct *vma) -{ - return (vma->vm_flags & VM_PFNMAP); -} /* * vm_fault is filled by the the pagefault handler and passed to the vma's @@ -798,8 +781,6 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma); void unmap_mapping_range(struct address_space *mapping, loff_t const holebegin, loff_t const holelen, int even_cows); -int follow_phys(struct vm_area_struct *vma, unsigned long address, - unsigned int flags, unsigned long *prot, resource_size_t *phys); int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write); diff --git a/trunk/mm/memory.c b/trunk/mm/memory.c index f01b7eed6e16..164951c47305 100644 --- a/trunk/mm/memory.c +++ b/trunk/mm/memory.c @@ -669,16 +669,6 @@ int copy_page_range(struct mm_struct *dst_mm, struct mm_struct *src_mm, if (is_vm_hugetlb_page(vma)) return copy_hugetlb_page_range(dst_mm, src_mm, vma); - if (unlikely(is_pfn_mapping(vma))) { - /* - * We do not free on error cases below as remove_vma - * gets called on error from higher level routine - */ - ret = track_pfn_vma_copy(vma); - if (ret) - return ret; - } - /* * We need to invalidate the secondary MMU mappings only when * there could be a permission downgrade on the ptes of the @@ -925,9 +915,6 @@ unsigned long unmap_vmas(struct mmu_gather **tlbp, if (vma->vm_flags & VM_ACCOUNT) *nr_accounted += (end - start) >> PAGE_SHIFT; - if (unlikely(is_pfn_mapping(vma))) - untrack_pfn_vma(vma, 0, 0); - while (start != end) { if (!tlb_start_valid) { tlb_start = start; @@ -1443,7 +1430,6 @@ static int insert_pfn(struct vm_area_struct *vma, unsigned long addr, int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) { - int ret; /* * Technically, architectures with pte_special can avoid all these * restrictions (same for remap_pfn_range). However we would like @@ -1458,15 +1444,7 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, if (addr < vma->vm_start || addr >= vma->vm_end) return -EFAULT; - if (track_pfn_vma_new(vma, vma->vm_page_prot, pfn, PAGE_SIZE)) - return -EINVAL; - - ret = insert_pfn(vma, addr, pfn, vma->vm_page_prot); - - if (ret) - untrack_pfn_vma(vma, pfn, PAGE_SIZE); - - return ret; + return insert_pfn(vma, addr, pfn, vma->vm_page_prot); } EXPORT_SYMBOL(vm_insert_pfn); @@ -1597,17 +1575,14 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, * behaviour that some programs depend on. We mark the "original" * un-COW'ed pages by matching them up with "vma->vm_pgoff". */ - if (addr == vma->vm_start && end == vma->vm_end) + if (is_cow_mapping(vma->vm_flags)) { + if (addr != vma->vm_start || end != vma->vm_end) + return -EINVAL; vma->vm_pgoff = pfn; - else if (is_cow_mapping(vma->vm_flags)) - return -EINVAL; + } vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; - err = track_pfn_vma_new(vma, prot, pfn, PAGE_ALIGN(size)); - if (err) - return -EINVAL; - BUG_ON(addr >= end); pfn -= addr >> PAGE_SHIFT; pgd = pgd_offset(mm, addr); @@ -1619,10 +1594,6 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, if (err) break; } while (pgd++, addr = next, addr != end); - - if (err) - untrack_pfn_vma(vma, pfn, PAGE_ALIGN(size)); - return err; } EXPORT_SYMBOL(remap_pfn_range); @@ -2894,9 +2865,9 @@ int in_gate_area_no_task(unsigned long addr) #endif /* __HAVE_ARCH_GATE_AREA */ #ifdef CONFIG_HAVE_IOREMAP_PROT -int follow_phys(struct vm_area_struct *vma, - unsigned long address, unsigned int flags, - unsigned long *prot, resource_size_t *phys) +static resource_size_t follow_phys(struct vm_area_struct *vma, + unsigned long address, unsigned int flags, + unsigned long *prot) { pgd_t *pgd; pud_t *pud; @@ -2905,26 +2876,24 @@ int follow_phys(struct vm_area_struct *vma, spinlock_t *ptl; resource_size_t phys_addr = 0; struct mm_struct *mm = vma->vm_mm; - int ret = -EINVAL; - if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) - goto out; + VM_BUG_ON(!(vma->vm_flags & (VM_IO | VM_PFNMAP))); pgd = pgd_offset(mm, address); if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) - goto out; + goto no_page_table; pud = pud_offset(pgd, address); if (pud_none(*pud) || unlikely(pud_bad(*pud))) - goto out; + goto no_page_table; pmd = pmd_offset(pud, address); if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) - goto out; + goto no_page_table; /* We cannot handle huge page PFN maps. Luckily they don't exist. */ if (pmd_huge(*pmd)) - goto out; + goto no_page_table; ptep = pte_offset_map_lock(mm, pmd, address, &ptl); if (!ptep) @@ -2939,13 +2908,13 @@ int follow_phys(struct vm_area_struct *vma, phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */ *prot = pgprot_val(pte_pgprot(pte)); - *phys = phys_addr; - ret = 0; unlock: pte_unmap_unlock(ptep, ptl); out: - return ret; + return phys_addr; +no_page_table: + return 0; } int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, @@ -2956,7 +2925,12 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, void *maddr; int offset = addr & (PAGE_SIZE-1); - if (follow_phys(vma, addr, write, &prot, &phys_addr)) + if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) + return -EINVAL; + + phys_addr = follow_phys(vma, addr, write, &prot); + + if (!phys_addr) return -EINVAL; maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot);