Commit a6525042bfdfcab128bd91fad264de10fd24a55e
Exists in
master
and in
4 other branches
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) { |
arch/x86/mm/pat.c
... | ... | @@ -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 | } |
arch/x86/pci/i386.c
... | ... | @@ -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, |
mm/memory.c
... | ... | @@ -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; |