tools headers UAPI: Sync drm/i915_drm.h with the kernel sources
[linux-2.6-microblaze.git] / mm / memory.c
index c32318d..5da9640 100644 (file)
@@ -4798,28 +4798,68 @@ out:
        return ret;
 }
 
+/**
+ * generic_access_phys - generic implementation for iomem mmap access
+ * @vma: the vma to access
+ * @addr: userspace addres, not relative offset within @vma
+ * @buf: buffer to read/write
+ * @len: length of transfer
+ * @write: set to FOLL_WRITE when writing, otherwise reading
+ *
+ * This is a generic implementation for &vm_operations_struct.access for an
+ * iomem mapping. This callback is used by access_process_vm() when the @vma is
+ * not page based.
+ */
 int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
                        void *buf, int len, int write)
 {
        resource_size_t phys_addr;
        unsigned long prot = 0;
        void __iomem *maddr;
-       int offset = addr & (PAGE_SIZE-1);
+       pte_t *ptep, pte;
+       spinlock_t *ptl;
+       int offset = offset_in_page(addr);
+       int ret = -EINVAL;
+
+       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP)))
+               return -EINVAL;
+
+retry:
+       if (follow_pte(vma->vm_mm, addr, &ptep, &ptl))
+               return -EINVAL;
+       pte = *ptep;
+       pte_unmap_unlock(ptep, ptl);
 
-       if (follow_phys(vma, addr, write, &prot, &phys_addr))
+       prot = pgprot_val(pte_pgprot(pte));
+       phys_addr = (resource_size_t)pte_pfn(pte) << PAGE_SHIFT;
+
+       if ((write & FOLL_WRITE) && !pte_write(pte))
                return -EINVAL;
 
        maddr = ioremap_prot(phys_addr, PAGE_ALIGN(len + offset), prot);
        if (!maddr)
                return -ENOMEM;
 
+       if (follow_pte(vma->vm_mm, addr, &ptep, &ptl))
+               goto out_unmap;
+
+       if (!pte_same(pte, *ptep)) {
+               pte_unmap_unlock(ptep, ptl);
+               iounmap(maddr);
+
+               goto retry;
+       }
+
        if (write)
                memcpy_toio(maddr + offset, buf, len);
        else
                memcpy_fromio(buf, maddr + offset, len);
+       ret = len;
+       pte_unmap_unlock(ptep, ptl);
+out_unmap:
        iounmap(maddr);
 
-       return len;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(generic_access_phys);
 #endif