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

Add arch_phys_wc_{add, del} to manipulate WC MTRRs if needed

Several drivers currently use mtrr_add through various #ifdef guards
and/or drm wrappers. The vast majority of them want to add WC MTRRs
on x86 systems and don't actually need the MTRR if PAT (i.e.
ioremap_wc, etc) are working.

arch_phys_wc_add and arch_phys_wc_del are new functions, available
on all architectures and configurations, that add WC MTRRs on x86 if
needed (and handle errors) and do nothing at all otherwise. They're
also easier to use than mtrr_add and mtrr_del, so the call sites can
be simplified.

As an added benefit, this will avoid wasting MTRRs and possibly
warning pointlessly on PAT-supporting systems.

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Andy Lutomirski and committed by
Dave Airlie
d0d98eed e81f3d81

+112 -1
+7
arch/x86/include/asm/io.h
··· 345 345 346 346 #define IO_SPACE_LIMIT 0xffff 347 347 348 + #ifdef CONFIG_MTRR 349 + extern int __must_check arch_phys_wc_add(unsigned long base, 350 + unsigned long size); 351 + extern void arch_phys_wc_del(int handle); 352 + #define arch_phys_wc_add arch_phys_wc_add 353 + #endif 354 + 348 355 #endif /* _ASM_X86_IO_H */
+9 -1
arch/x86/include/asm/mtrr.h
··· 26 26 #include <uapi/asm/mtrr.h> 27 27 28 28 29 - /* The following functions are for use by other drivers */ 29 + /* 30 + * The following functions are for use by other drivers that cannot use 31 + * arch_phys_wc_add and arch_phys_wc_del. 32 + */ 30 33 # ifdef CONFIG_MTRR 31 34 extern u8 mtrr_type_lookup(u64 addr, u64 end); 32 35 extern void mtrr_save_fixed_ranges(void *); ··· 48 45 extern void mtrr_bp_restore(void); 49 46 extern int mtrr_trim_uncached_memory(unsigned long end_pfn); 50 47 extern int amd_special_default_mtrr(void); 48 + extern int phys_wc_to_mtrr_index(int handle); 51 49 # else 52 50 static inline u8 mtrr_type_lookup(u64 addr, u64 end) 53 51 { ··· 83 79 } 84 80 static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) 85 81 { 82 + } 83 + static inline int phys_wc_to_mtrr_index(int handle) 84 + { 85 + return -1; 86 86 } 87 87 88 88 #define mtrr_ap_init() do {} while (0)
+71
arch/x86/kernel/cpu/mtrr/main.c
··· 51 51 #include <asm/e820.h> 52 52 #include <asm/mtrr.h> 53 53 #include <asm/msr.h> 54 + #include <asm/pat.h> 54 55 55 56 #include "mtrr.h" 57 + 58 + /* arch_phys_wc_add returns an MTRR register index plus this offset. */ 59 + #define MTRR_TO_PHYS_WC_OFFSET 1000 56 60 57 61 u32 num_var_ranges; 58 62 ··· 527 523 return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); 528 524 } 529 525 EXPORT_SYMBOL(mtrr_del); 526 + 527 + /** 528 + * arch_phys_wc_add - add a WC MTRR and handle errors if PAT is unavailable 529 + * @base: Physical base address 530 + * @size: Size of region 531 + * 532 + * If PAT is available, this does nothing. If PAT is unavailable, it 533 + * attempts to add a WC MTRR covering size bytes starting at base and 534 + * logs an error if this fails. 535 + * 536 + * Drivers must store the return value to pass to mtrr_del_wc_if_needed, 537 + * but drivers should not try to interpret that return value. 538 + */ 539 + int arch_phys_wc_add(unsigned long base, unsigned long size) 540 + { 541 + int ret; 542 + 543 + if (pat_enabled) 544 + return 0; /* Success! (We don't need to do anything.) */ 545 + 546 + ret = mtrr_add(base, size, MTRR_TYPE_WRCOMB, true); 547 + if (ret < 0) { 548 + pr_warn("Failed to add WC MTRR for [%p-%p]; performance may suffer.", 549 + (void *)base, (void *)(base + size - 1)); 550 + return ret; 551 + } 552 + return ret + MTRR_TO_PHYS_WC_OFFSET; 553 + } 554 + EXPORT_SYMBOL(arch_phys_wc_add); 555 + 556 + /* 557 + * arch_phys_wc_del - undoes arch_phys_wc_add 558 + * @handle: Return value from arch_phys_wc_add 559 + * 560 + * This cleans up after mtrr_add_wc_if_needed. 561 + * 562 + * The API guarantees that mtrr_del_wc_if_needed(error code) and 563 + * mtrr_del_wc_if_needed(0) do nothing. 564 + */ 565 + void arch_phys_wc_del(int handle) 566 + { 567 + if (handle >= 1) { 568 + WARN_ON(handle < MTRR_TO_PHYS_WC_OFFSET); 569 + mtrr_del(handle - MTRR_TO_PHYS_WC_OFFSET, 0, 0); 570 + } 571 + } 572 + EXPORT_SYMBOL(arch_phys_wc_del); 573 + 574 + /* 575 + * phys_wc_to_mtrr_index - translates arch_phys_wc_add's return value 576 + * @handle: Return value from arch_phys_wc_add 577 + * 578 + * This will turn the return value from arch_phys_wc_add into an mtrr 579 + * index suitable for debugging. 580 + * 581 + * Note: There is no legitimate use for this function, except possibly 582 + * in printk line. Alas there is an illegitimate use in some ancient 583 + * drm ioctls. 584 + */ 585 + int phys_wc_to_mtrr_index(int handle) 586 + { 587 + if (handle < MTRR_TO_PHYS_WC_OFFSET) 588 + return -1; 589 + else 590 + return handle - MTRR_TO_PHYS_WC_OFFSET; 591 + } 592 + EXPORT_SYMBOL_GPL(phys_wc_to_mtrr_index); 530 593 531 594 /* 532 595 * HACK ALERT!
+25
include/linux/io.h
··· 76 76 #define arch_has_dev_port() (1) 77 77 #endif 78 78 79 + /* 80 + * Some systems (x86 without PAT) have a somewhat reliable way to mark a 81 + * physical address range such that uncached mappings will actually 82 + * end up write-combining. This facility should be used in conjunction 83 + * with pgprot_writecombine, ioremap-wc, or set_memory_wc, since it has 84 + * no effect if the per-page mechanisms are functional. 85 + * (On x86 without PAT, these functions manipulate MTRRs.) 86 + * 87 + * arch_phys_del_wc(0) or arch_phys_del_wc(any error code) is guaranteed 88 + * to have no effect. 89 + */ 90 + #ifndef arch_phys_wc_add 91 + static inline int __must_check arch_phys_wc_add(unsigned long base, 92 + unsigned long size) 93 + { 94 + return 0; /* It worked (i.e. did nothing). */ 95 + } 96 + 97 + static inline void arch_phys_wc_del(int handle) 98 + { 99 + } 100 + 101 + #define arch_phys_wc_add arch_phys_wc_add 102 + #endif 103 + 79 104 #endif /* _LINUX_IO_H */