Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6.git] / arch / x86 / mm / pat.c
index ffc88cc00fda01be5aeabdea047cdd3fc9512d66..7b61036427df69d11a33044d60bc85102931e385 100644 (file)
@@ -618,12 +618,13 @@ void unmap_devmem(unsigned long pfn, unsigned long size, pgprot_t vma_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.
  */
  * 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)
+static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
+                               int strict_prot)
 {
        int is_ram = 0;
        int id_sz, ret;
        unsigned long flags;
 {
        int is_ram = 0;
        int id_sz, ret;
        unsigned long flags;
-       unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK);
+       unsigned long want_flags = (pgprot_val(*vma_prot) & _PAGE_CACHE_MASK);
 
        is_ram = pagerange_is_ram(paddr, paddr + size);
 
 
        is_ram = pagerange_is_ram(paddr, paddr + size);
 
@@ -642,15 +643,24 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot)
                return ret;
 
        if (flags != want_flags) {
                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;
+               if (strict_prot || !is_new_memtype_allowed(want_flags, 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;
+               }
+               /*
+                * We allow returning different type than the one requested in
+                * non strict case.
+                */
+               *vma_prot = __pgprot((pgprot_val(*vma_prot) &
+                                     (~_PAGE_CACHE_MASK)) |
+                                    flags);
        }
 
        /* Need to keep identity mapping in sync */
        }
 
        /* Need to keep identity mapping in sync */
@@ -706,6 +716,7 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
        unsigned long vma_start = vma->vm_start;
        unsigned long vma_end = vma->vm_end;
        unsigned long vma_size = vma_end - vma_start;
        unsigned long vma_start = vma->vm_start;
        unsigned long vma_end = vma->vm_end;
        unsigned long vma_size = vma_end - vma_start;
+       pgprot_t pgprot;
 
        if (!pat_enabled)
                return 0;
 
        if (!pat_enabled)
                return 0;
@@ -719,7 +730,8 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
                        WARN_ON_ONCE(1);
                        return -EINVAL;
                }
                        WARN_ON_ONCE(1);
                        return -EINVAL;
                }
-               return reserve_pfn_range(paddr, vma_size, __pgprot(prot));
+               pgprot = __pgprot(prot);
+               return reserve_pfn_range(paddr, vma_size, &pgprot, 1);
        }
 
        /* reserve entire vma page by page, using pfn and prot from pte */
        }
 
        /* reserve entire vma page by page, using pfn and prot from pte */
@@ -727,7 +739,8 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
                if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
                        continue;
 
                if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
                        continue;
 
-               retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot));
+               pgprot = __pgprot(prot);
+               retval = reserve_pfn_range(paddr, PAGE_SIZE, &pgprot, 1);
                if (retval)
                        goto cleanup_ret;
        }
                if (retval)
                        goto cleanup_ret;
        }
@@ -758,7 +771,7 @@ cleanup_ret:
  * Note that this function can be called with caller trying to map only a
  * subrange/page inside the vma.
  */
  * 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,
+int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
                        unsigned long pfn, unsigned long size)
 {
        int retval = 0;
                        unsigned long pfn, unsigned long size)
 {
        int retval = 0;
@@ -775,14 +788,14 @@ int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
        if (is_linear_pfn_mapping(vma)) {
                /* reserve the whole chunk starting from vm_pgoff */
                paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
        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);
+               return reserve_pfn_range(paddr, vma_size, prot, 0);
        }
 
        /* 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;
        }
 
        /* 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);
+               retval = reserve_pfn_range(paddr, PAGE_SIZE, prot, 0);
                if (retval)
                        goto cleanup_ret;
        }
                if (retval)
                        goto cleanup_ret;
        }