Commit a6525042bfdfcab128bd91fad264de10fd24a55e

Authored by Linus Torvalds

Merge branch 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  x86 PAT: remove CPA WARN_ON for zero pte
  x86 PAT: return compatible mapping to remap_pfn_range callers
  x86 PAT: change track_pfn_vma_new to take pgprot_t pointer param
  x86 PAT: consolidate old memtype new memtype check into a function
  x86 PAT: remove PFNMAP type on track_pfn_vma_new() error

Showing 6 changed files Side-by-side Diff

arch/x86/include/asm/pgtable.h
... ... @@ -341,6 +341,25 @@
341 341  
342 342 #define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask)
343 343  
  344 +static inline int is_new_memtype_allowed(unsigned long flags,
  345 + unsigned long new_flags)
  346 +{
  347 + /*
  348 + * Certain new memtypes are not allowed with certain
  349 + * requested memtype:
  350 + * - request is uncached, return cannot be write-back
  351 + * - request is write-combine, return cannot be write-back
  352 + */
  353 + if ((flags == _PAGE_CACHE_UC_MINUS &&
  354 + new_flags == _PAGE_CACHE_WB) ||
  355 + (flags == _PAGE_CACHE_WC &&
  356 + new_flags == _PAGE_CACHE_WB)) {
  357 + return 0;
  358 + }
  359 +
  360 + return 1;
  361 +}
  362 +
344 363 #ifndef __ASSEMBLY__
345 364 /* Indicate that x86 has its own track and untrack pfn vma functions */
346 365 #define __HAVE_PFNMAP_TRACKING
arch/x86/mm/pageattr.c
... ... @@ -555,10 +555,12 @@
555 555 if (!pte_val(old_pte)) {
556 556 if (!primary)
557 557 return 0;
558   - WARN(1, KERN_WARNING "CPA: called for zero pte. "
559   - "vaddr = %lx cpa->vaddr = %lx\n", address,
560   - *cpa->vaddr);
561   - return -EINVAL;
  558 +
  559 + /*
  560 + * Special error value returned, indicating that the mapping
  561 + * did not exist at this address.
  562 + */
  563 + return -EFAULT;
562 564 }
563 565  
564 566 if (level == PG_LEVEL_4K) {
... ... @@ -505,6 +505,35 @@
505 505 }
506 506 #endif /* CONFIG_STRICT_DEVMEM */
507 507  
  508 +/*
  509 + * Change the memory type for the physial address range in kernel identity
  510 + * mapping space if that range is a part of identity map.
  511 + */
  512 +static int kernel_map_sync_memtype(u64 base, unsigned long size,
  513 + unsigned long flags)
  514 +{
  515 + unsigned long id_sz;
  516 + int ret;
  517 +
  518 + if (!pat_enabled || base >= __pa(high_memory))
  519 + return 0;
  520 +
  521 + id_sz = (__pa(high_memory) < base + size) ?
  522 + __pa(high_memory) - base :
  523 + size;
  524 +
  525 + ret = ioremap_change_attr((unsigned long)__va(base), id_sz, flags);
  526 + /*
  527 + * -EFAULT return means that the addr was not valid and did not have
  528 + * any identity mapping. That case is a success for
  529 + * kernel_map_sync_memtype.
  530 + */
  531 + if (ret == -EFAULT)
  532 + ret = 0;
  533 +
  534 + return ret;
  535 +}
  536 +
508 537 int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
509 538 unsigned long size, pgprot_t *vma_prot)
510 539 {
... ... @@ -555,9 +584,7 @@
555 584 if (retval < 0)
556 585 return 0;
557 586  
558   - if (((pfn < max_low_pfn_mapped) ||
559   - (pfn >= (1UL<<(32 - PAGE_SHIFT)) && pfn < max_pfn_mapped)) &&
560   - ioremap_change_attr((unsigned long)__va(offset), size, flags) < 0) {
  587 + if (kernel_map_sync_memtype(offset, size, flags)) {
561 588 free_memtype(offset, offset + size);
562 589 printk(KERN_INFO
563 590 "%s:%d /dev/mem ioremap_change_attr failed %s for %Lx-%Lx\n",
564 591  
565 592  
... ... @@ -601,12 +628,13 @@
601 628 * Reserved non RAM regions only and after successful reserve_memtype,
602 629 * this func also keeps identity mapping (if any) in sync with this new prot.
603 630 */
604   -static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot)
  631 +static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
  632 + int strict_prot)
605 633 {
606 634 int is_ram = 0;
607   - int id_sz, ret;
  635 + int ret;
608 636 unsigned long flags;
609   - unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK);
  637 + unsigned long want_flags = (pgprot_val(*vma_prot) & _PAGE_CACHE_MASK);
610 638  
611 639 is_ram = pagerange_is_ram(paddr, paddr + size);
612 640  
613 641  
... ... @@ -625,26 +653,27 @@
625 653 return ret;
626 654  
627 655 if (flags != want_flags) {
628   - free_memtype(paddr, paddr + size);
629   - printk(KERN_ERR
630   - "%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n",
631   - current->comm, current->pid,
632   - cattr_name(want_flags),
633   - (unsigned long long)paddr,
634   - (unsigned long long)(paddr + size),
635   - cattr_name(flags));
636   - return -EINVAL;
  656 + if (strict_prot || !is_new_memtype_allowed(want_flags, flags)) {
  657 + free_memtype(paddr, paddr + size);
  658 + printk(KERN_ERR "%s:%d map pfn expected mapping type %s"
  659 + " for %Lx-%Lx, got %s\n",
  660 + current->comm, current->pid,
  661 + cattr_name(want_flags),
  662 + (unsigned long long)paddr,
  663 + (unsigned long long)(paddr + size),
  664 + cattr_name(flags));
  665 + return -EINVAL;
  666 + }
  667 + /*
  668 + * We allow returning different type than the one requested in
  669 + * non strict case.
  670 + */
  671 + *vma_prot = __pgprot((pgprot_val(*vma_prot) &
  672 + (~_PAGE_CACHE_MASK)) |
  673 + flags);
637 674 }
638 675  
639   - /* Need to keep identity mapping in sync */
640   - if (paddr >= __pa(high_memory))
641   - return 0;
642   -
643   - id_sz = (__pa(high_memory) < paddr + size) ?
644   - __pa(high_memory) - paddr :
645   - size;
646   -
647   - if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) {
  676 + if (kernel_map_sync_memtype(paddr, size, flags)) {
648 677 free_memtype(paddr, paddr + size);
649 678 printk(KERN_ERR
650 679 "%s:%d reserve_pfn_range ioremap_change_attr failed %s "
... ... @@ -689,6 +718,7 @@
689 718 unsigned long vma_start = vma->vm_start;
690 719 unsigned long vma_end = vma->vm_end;
691 720 unsigned long vma_size = vma_end - vma_start;
  721 + pgprot_t pgprot;
692 722  
693 723 if (!pat_enabled)
694 724 return 0;
... ... @@ -702,7 +732,8 @@
702 732 WARN_ON_ONCE(1);
703 733 return -EINVAL;
704 734 }
705   - return reserve_pfn_range(paddr, vma_size, __pgprot(prot));
  735 + pgprot = __pgprot(prot);
  736 + return reserve_pfn_range(paddr, vma_size, &pgprot, 1);
706 737 }
707 738  
708 739 /* reserve entire vma page by page, using pfn and prot from pte */
... ... @@ -710,7 +741,8 @@
710 741 if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
711 742 continue;
712 743  
713   - retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot));
  744 + pgprot = __pgprot(prot);
  745 + retval = reserve_pfn_range(paddr, PAGE_SIZE, &pgprot, 1);
714 746 if (retval)
715 747 goto cleanup_ret;
716 748 }
... ... @@ -741,7 +773,7 @@
741 773 * Note that this function can be called with caller trying to map only a
742 774 * subrange/page inside the vma.
743 775 */
744   -int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
  776 +int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
745 777 unsigned long pfn, unsigned long size)
746 778 {
747 779 int retval = 0;
748 780  
... ... @@ -758,14 +790,14 @@
758 790 if (is_linear_pfn_mapping(vma)) {
759 791 /* reserve the whole chunk starting from vm_pgoff */
760 792 paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
761   - return reserve_pfn_range(paddr, vma_size, prot);
  793 + return reserve_pfn_range(paddr, vma_size, prot, 0);
762 794 }
763 795  
764 796 /* reserve page by page using pfn and size */
765 797 base_paddr = (resource_size_t)pfn << PAGE_SHIFT;
766 798 for (i = 0; i < size; i += PAGE_SIZE) {
767 799 paddr = base_paddr + i;
768   - retval = reserve_pfn_range(paddr, PAGE_SIZE, prot);
  800 + retval = reserve_pfn_range(paddr, PAGE_SIZE, prot, 0);
769 801 if (retval)
770 802 goto cleanup_ret;
771 803 }
... ... @@ -314,17 +314,7 @@
314 314 return retval;
315 315  
316 316 if (flags != new_flags) {
317   - /*
318   - * Do not fallback to certain memory types with certain
319   - * requested type:
320   - * - request is uncached, return cannot be write-back
321   - * - request is uncached, return cannot be write-combine
322   - * - request is write-combine, return cannot be write-back
323   - */
324   - if ((flags == _PAGE_CACHE_UC_MINUS &&
325   - (new_flags == _PAGE_CACHE_WB)) ||
326   - (flags == _PAGE_CACHE_WC &&
327   - new_flags == _PAGE_CACHE_WB)) {
  317 + if (!is_new_memtype_allowed(flags, new_flags)) {
328 318 free_memtype(addr, addr+len);
329 319 return -EINVAL;
330 320 }
include/asm-generic/pgtable.h
... ... @@ -301,7 +301,7 @@
301 301 * track_pfn_vma_new is called when a _new_ pfn mapping is being established
302 302 * for physical range indicated by pfn and size.
303 303 */
304   -static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
  304 +static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
305 305 unsigned long pfn, unsigned long size)
306 306 {
307 307 return 0;
... ... @@ -332,7 +332,7 @@
332 332 {
333 333 }
334 334 #else
335   -extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
  335 +extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
336 336 unsigned long pfn, unsigned long size);
337 337 extern int track_pfn_vma_copy(struct vm_area_struct *vma);
338 338 extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
... ... @@ -1511,6 +1511,7 @@
1511 1511 unsigned long pfn)
1512 1512 {
1513 1513 int ret;
  1514 + pgprot_t pgprot = vma->vm_page_prot;
1514 1515 /*
1515 1516 * Technically, architectures with pte_special can avoid all these
1516 1517 * restrictions (same for remap_pfn_range). However we would like
1517 1518  
... ... @@ -1525,10 +1526,10 @@
1525 1526  
1526 1527 if (addr < vma->vm_start || addr >= vma->vm_end)
1527 1528 return -EFAULT;
1528   - if (track_pfn_vma_new(vma, vma->vm_page_prot, pfn, PAGE_SIZE))
  1529 + if (track_pfn_vma_new(vma, &pgprot, pfn, PAGE_SIZE))
1529 1530 return -EINVAL;
1530 1531  
1531   - ret = insert_pfn(vma, addr, pfn, vma->vm_page_prot);
  1532 + ret = insert_pfn(vma, addr, pfn, pgprot);
1532 1533  
1533 1534 if (ret)
1534 1535 untrack_pfn_vma(vma, pfn, PAGE_SIZE);
1535 1536  
... ... @@ -1671,9 +1672,15 @@
1671 1672  
1672 1673 vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
1673 1674  
1674   - err = track_pfn_vma_new(vma, prot, pfn, PAGE_ALIGN(size));
1675   - if (err)
  1675 + err = track_pfn_vma_new(vma, &prot, pfn, PAGE_ALIGN(size));
  1676 + if (err) {
  1677 + /*
  1678 + * To indicate that track_pfn related cleanup is not
  1679 + * needed from higher level routine calling unmap_vmas
  1680 + */
  1681 + vma->vm_flags &= ~(VM_IO | VM_RESERVED | VM_PFNMAP);
1676 1682 return -EINVAL;
  1683 + }
1677 1684  
1678 1685 BUG_ON(addr >= end);
1679 1686 pfn -= addr >> PAGE_SHIFT;