Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+100 -50
+19
arch/x86/include/asm/pgtable.h
··· 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
+6 -4
arch/x86/mm/pageattr.c
··· 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) {
+61 -29
arch/x86/mm/pat.c
··· 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 { ··· 584 555 if (retval < 0) 585 556 return 0; 586 557 587 - if (((pfn < max_low_pfn_mapped) || 588 - (pfn >= (1UL<<(32 - PAGE_SHIFT)) && pfn < max_pfn_mapped)) && 589 - ioremap_change_attr((unsigned long)__va(offset), size, flags) < 0) { 558 + if (kernel_map_sync_memtype(offset, size, flags)) { 590 559 free_memtype(offset, offset + size); 591 560 printk(KERN_INFO 592 561 "%s:%d /dev/mem ioremap_change_attr failed %s for %Lx-%Lx\n", ··· 628 601 * Reserved non RAM regions only and after successful reserve_memtype, 629 602 * this func also keeps identity mapping (if any) in sync with this new prot. 630 603 */ 631 - static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot) 604 + static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot, 605 + int strict_prot) 632 606 { 633 607 int is_ram = 0; 634 - int id_sz, ret; 608 + int ret; 635 609 unsigned long flags; 636 - unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK); 610 + unsigned long want_flags = (pgprot_val(*vma_prot) & _PAGE_CACHE_MASK); 637 611 638 612 is_ram = pagerange_is_ram(paddr, paddr + size); 639 613 ··· 653 625 return ret; 654 626 655 627 if (flags != want_flags) { 656 - free_memtype(paddr, paddr + size); 657 - printk(KERN_ERR 658 - "%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n", 659 - current->comm, current->pid, 660 - cattr_name(want_flags), 661 - (unsigned long long)paddr, 662 - (unsigned long long)(paddr + size), 663 - cattr_name(flags)); 664 - return -EINVAL; 628 + if (strict_prot || !is_new_memtype_allowed(want_flags, flags)) { 629 + free_memtype(paddr, paddr + size); 630 + printk(KERN_ERR "%s:%d map pfn expected mapping type %s" 631 + " for %Lx-%Lx, got %s\n", 632 + current->comm, current->pid, 633 + cattr_name(want_flags), 634 + (unsigned long long)paddr, 635 + (unsigned long long)(paddr + size), 636 + cattr_name(flags)); 637 + return -EINVAL; 638 + } 639 + /* 640 + * We allow returning different type than the one requested in 641 + * non strict case. 642 + */ 643 + *vma_prot = __pgprot((pgprot_val(*vma_prot) & 644 + (~_PAGE_CACHE_MASK)) | 645 + flags); 665 646 } 666 647 667 - /* Need to keep identity mapping in sync */ 668 - if (paddr >= __pa(high_memory)) 669 - return 0; 670 - 671 - id_sz = (__pa(high_memory) < paddr + size) ? 672 - __pa(high_memory) - paddr : 673 - size; 674 - 675 - if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) { 648 + if (kernel_map_sync_memtype(paddr, size, flags)) { 676 649 free_memtype(paddr, paddr + size); 677 650 printk(KERN_ERR 678 651 "%s:%d reserve_pfn_range ioremap_change_attr failed %s " ··· 718 689 unsigned long vma_start = vma->vm_start; 719 690 unsigned long vma_end = vma->vm_end; 720 691 unsigned long vma_size = vma_end - vma_start; 692 + pgprot_t pgprot; 721 693 722 694 if (!pat_enabled) 723 695 return 0; ··· 732 702 WARN_ON_ONCE(1); 733 703 return -EINVAL; 734 704 } 735 - return reserve_pfn_range(paddr, vma_size, __pgprot(prot)); 705 + pgprot = __pgprot(prot); 706 + return reserve_pfn_range(paddr, vma_size, &pgprot, 1); 736 707 } 737 708 738 709 /* reserve entire vma page by page, using pfn and prot from pte */ ··· 741 710 if (follow_phys(vma, vma_start + i, 0, &prot, &paddr)) 742 711 continue; 743 712 744 - retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot)); 713 + pgprot = __pgprot(prot); 714 + retval = reserve_pfn_range(paddr, PAGE_SIZE, &pgprot, 1); 745 715 if (retval) 746 716 goto cleanup_ret; 747 717 } ··· 773 741 * Note that this function can be called with caller trying to map only a 774 742 * subrange/page inside the vma. 775 743 */ 776 - int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot, 744 + int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, 777 745 unsigned long pfn, unsigned long size) 778 746 { 779 747 int retval = 0; ··· 790 758 if (is_linear_pfn_mapping(vma)) { 791 759 /* reserve the whole chunk starting from vm_pgoff */ 792 760 paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; 793 - return reserve_pfn_range(paddr, vma_size, prot); 761 + return reserve_pfn_range(paddr, vma_size, prot, 0); 794 762 } 795 763 796 764 /* reserve page by page using pfn and size */ 797 765 base_paddr = (resource_size_t)pfn << PAGE_SHIFT; 798 766 for (i = 0; i < size; i += PAGE_SIZE) { 799 767 paddr = base_paddr + i; 800 - retval = reserve_pfn_range(paddr, PAGE_SIZE, prot); 768 + retval = reserve_pfn_range(paddr, PAGE_SIZE, prot, 0); 801 769 if (retval) 802 770 goto cleanup_ret; 803 771 }
+1 -11
arch/x86/pci/i386.c
··· 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 }
+2 -2
include/asm-generic/pgtable.h
··· 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 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,
+11 -4
mm/memory.c
··· 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 ··· 1526 1525 1527 1526 if (addr < vma->vm_start || addr >= vma->vm_end) 1528 1527 return -EFAULT; 1529 - if (track_pfn_vma_new(vma, vma->vm_page_prot, pfn, PAGE_SIZE)) 1528 + if (track_pfn_vma_new(vma, &pgprot, pfn, PAGE_SIZE)) 1530 1529 return -EINVAL; 1531 1530 1532 - ret = insert_pfn(vma, addr, pfn, vma->vm_page_prot); 1531 + ret = insert_pfn(vma, addr, pfn, pgprot); 1533 1532 1534 1533 if (ret) 1535 1534 untrack_pfn_vma(vma, pfn, PAGE_SIZE); ··· 1672 1671 1673 1672 vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; 1674 1673 1675 - err = track_pfn_vma_new(vma, prot, pfn, PAGE_ALIGN(size)); 1676 - if (err) 1674 + err = track_pfn_vma_new(vma, &prot, pfn, PAGE_ALIGN(size)); 1675 + if (err) { 1676 + /* 1677 + * To indicate that track_pfn related cleanup is not 1678 + * needed from higher level routine calling unmap_vmas 1679 + */ 1680 + vma->vm_flags &= ~(VM_IO | VM_RESERVED | VM_PFNMAP); 1677 1681 return -EINVAL; 1682 + } 1678 1683 1679 1684 BUG_ON(addr >= end); 1680 1685 pfn -= addr >> PAGE_SHIFT;