diff options
Diffstat (limited to 'arch/x86/mm/pageattr.c')
-rw-r--r-- | arch/x86/mm/pageattr.c | 105 |
1 files changed, 97 insertions, 8 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 7b79f6be4e7d..7d9517abc9af 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -9,6 +9,8 @@ | |||
9 | #include <linux/slab.h> | 9 | #include <linux/slab.h> |
10 | #include <linux/mm.h> | 10 | #include <linux/mm.h> |
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <linux/seq_file.h> | ||
13 | #include <linux/debugfs.h> | ||
12 | 14 | ||
13 | #include <asm/e820.h> | 15 | #include <asm/e820.h> |
14 | #include <asm/processor.h> | 16 | #include <asm/processor.h> |
@@ -17,6 +19,7 @@ | |||
17 | #include <asm/uaccess.h> | 19 | #include <asm/uaccess.h> |
18 | #include <asm/pgalloc.h> | 20 | #include <asm/pgalloc.h> |
19 | #include <asm/proto.h> | 21 | #include <asm/proto.h> |
22 | #include <asm/pat.h> | ||
20 | 23 | ||
21 | /* | 24 | /* |
22 | * The current flushing context - we pass it instead of 5 arguments: | 25 | * The current flushing context - we pass it instead of 5 arguments: |
@@ -28,6 +31,7 @@ struct cpa_data { | |||
28 | int numpages; | 31 | int numpages; |
29 | int flushtlb; | 32 | int flushtlb; |
30 | unsigned long pfn; | 33 | unsigned long pfn; |
34 | unsigned force_split : 1; | ||
31 | }; | 35 | }; |
32 | 36 | ||
33 | #ifdef CONFIG_X86_64 | 37 | #ifdef CONFIG_X86_64 |
@@ -259,6 +263,9 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
259 | int i, do_split = 1; | 263 | int i, do_split = 1; |
260 | unsigned int level; | 264 | unsigned int level; |
261 | 265 | ||
266 | if (cpa->force_split) | ||
267 | return 1; | ||
268 | |||
262 | spin_lock_irqsave(&pgd_lock, flags); | 269 | spin_lock_irqsave(&pgd_lock, flags); |
263 | /* | 270 | /* |
264 | * Check for races, another CPU might have split this page | 271 | * Check for races, another CPU might have split this page |
@@ -693,7 +700,8 @@ static inline int cache_attr(pgprot_t attr) | |||
693 | } | 700 | } |
694 | 701 | ||
695 | static int change_page_attr_set_clr(unsigned long addr, int numpages, | 702 | static int change_page_attr_set_clr(unsigned long addr, int numpages, |
696 | pgprot_t mask_set, pgprot_t mask_clr) | 703 | pgprot_t mask_set, pgprot_t mask_clr, |
704 | int force_split) | ||
697 | { | 705 | { |
698 | struct cpa_data cpa; | 706 | struct cpa_data cpa; |
699 | int ret, cache, checkalias; | 707 | int ret, cache, checkalias; |
@@ -704,7 +712,7 @@ static int change_page_attr_set_clr(unsigned long addr, int numpages, | |||
704 | */ | 712 | */ |
705 | mask_set = canon_pgprot(mask_set); | 713 | mask_set = canon_pgprot(mask_set); |
706 | mask_clr = canon_pgprot(mask_clr); | 714 | mask_clr = canon_pgprot(mask_clr); |
707 | if (!pgprot_val(mask_set) && !pgprot_val(mask_clr)) | 715 | if (!pgprot_val(mask_set) && !pgprot_val(mask_clr) && !force_split) |
708 | return 0; | 716 | return 0; |
709 | 717 | ||
710 | /* Ensure we are PAGE_SIZE aligned */ | 718 | /* Ensure we are PAGE_SIZE aligned */ |
@@ -721,6 +729,7 @@ static int change_page_attr_set_clr(unsigned long addr, int numpages, | |||
721 | cpa.mask_set = mask_set; | 729 | cpa.mask_set = mask_set; |
722 | cpa.mask_clr = mask_clr; | 730 | cpa.mask_clr = mask_clr; |
723 | cpa.flushtlb = 0; | 731 | cpa.flushtlb = 0; |
732 | cpa.force_split = force_split; | ||
724 | 733 | ||
725 | /* No alias checking for _NX bit modifications */ | 734 | /* No alias checking for _NX bit modifications */ |
726 | checkalias = (pgprot_val(mask_set) | pgprot_val(mask_clr)) != _PAGE_NX; | 735 | checkalias = (pgprot_val(mask_set) | pgprot_val(mask_clr)) != _PAGE_NX; |
@@ -759,26 +768,61 @@ out: | |||
759 | static inline int change_page_attr_set(unsigned long addr, int numpages, | 768 | static inline int change_page_attr_set(unsigned long addr, int numpages, |
760 | pgprot_t mask) | 769 | pgprot_t mask) |
761 | { | 770 | { |
762 | return change_page_attr_set_clr(addr, numpages, mask, __pgprot(0)); | 771 | return change_page_attr_set_clr(addr, numpages, mask, __pgprot(0), 0); |
763 | } | 772 | } |
764 | 773 | ||
765 | static inline int change_page_attr_clear(unsigned long addr, int numpages, | 774 | static inline int change_page_attr_clear(unsigned long addr, int numpages, |
766 | pgprot_t mask) | 775 | pgprot_t mask) |
767 | { | 776 | { |
768 | return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask); | 777 | return change_page_attr_set_clr(addr, numpages, __pgprot(0), mask, 0); |
769 | } | 778 | } |
770 | 779 | ||
771 | int set_memory_uc(unsigned long addr, int numpages) | 780 | int _set_memory_uc(unsigned long addr, int numpages) |
772 | { | 781 | { |
773 | return change_page_attr_set(addr, numpages, | 782 | return change_page_attr_set(addr, numpages, |
774 | __pgprot(_PAGE_PCD)); | 783 | __pgprot(_PAGE_CACHE_UC)); |
784 | } | ||
785 | |||
786 | int set_memory_uc(unsigned long addr, int numpages) | ||
787 | { | ||
788 | if (reserve_memtype(addr, addr + numpages * PAGE_SIZE, | ||
789 | _PAGE_CACHE_UC, NULL)) | ||
790 | return -EINVAL; | ||
791 | |||
792 | return _set_memory_uc(addr, numpages); | ||
775 | } | 793 | } |
776 | EXPORT_SYMBOL(set_memory_uc); | 794 | EXPORT_SYMBOL(set_memory_uc); |
777 | 795 | ||
778 | int set_memory_wb(unsigned long addr, int numpages) | 796 | int _set_memory_wc(unsigned long addr, int numpages) |
797 | { | ||
798 | return change_page_attr_set(addr, numpages, | ||
799 | __pgprot(_PAGE_CACHE_WC)); | ||
800 | } | ||
801 | |||
802 | int set_memory_wc(unsigned long addr, int numpages) | ||
803 | { | ||
804 | if (!pat_wc_enabled) | ||
805 | return set_memory_uc(addr, numpages); | ||
806 | |||
807 | if (reserve_memtype(addr, addr + numpages * PAGE_SIZE, | ||
808 | _PAGE_CACHE_WC, NULL)) | ||
809 | return -EINVAL; | ||
810 | |||
811 | return _set_memory_wc(addr, numpages); | ||
812 | } | ||
813 | EXPORT_SYMBOL(set_memory_wc); | ||
814 | |||
815 | int _set_memory_wb(unsigned long addr, int numpages) | ||
779 | { | 816 | { |
780 | return change_page_attr_clear(addr, numpages, | 817 | return change_page_attr_clear(addr, numpages, |
781 | __pgprot(_PAGE_PCD | _PAGE_PWT)); | 818 | __pgprot(_PAGE_CACHE_MASK)); |
819 | } | ||
820 | |||
821 | int set_memory_wb(unsigned long addr, int numpages) | ||
822 | { | ||
823 | free_memtype(addr, addr + numpages * PAGE_SIZE); | ||
824 | |||
825 | return _set_memory_wb(addr, numpages); | ||
782 | } | 826 | } |
783 | EXPORT_SYMBOL(set_memory_wb); | 827 | EXPORT_SYMBOL(set_memory_wb); |
784 | 828 | ||
@@ -809,6 +853,12 @@ int set_memory_np(unsigned long addr, int numpages) | |||
809 | return change_page_attr_clear(addr, numpages, __pgprot(_PAGE_PRESENT)); | 853 | return change_page_attr_clear(addr, numpages, __pgprot(_PAGE_PRESENT)); |
810 | } | 854 | } |
811 | 855 | ||
856 | int set_memory_4k(unsigned long addr, int numpages) | ||
857 | { | ||
858 | return change_page_attr_set_clr(addr, numpages, __pgprot(0), | ||
859 | __pgprot(0), 1); | ||
860 | } | ||
861 | |||
812 | int set_pages_uc(struct page *page, int numpages) | 862 | int set_pages_uc(struct page *page, int numpages) |
813 | { | 863 | { |
814 | unsigned long addr = (unsigned long)page_address(page); | 864 | unsigned long addr = (unsigned long)page_address(page); |
@@ -918,6 +968,45 @@ void kernel_map_pages(struct page *page, int numpages, int enable) | |||
918 | cpa_fill_pool(NULL); | 968 | cpa_fill_pool(NULL); |
919 | } | 969 | } |
920 | 970 | ||
971 | #ifdef CONFIG_DEBUG_FS | ||
972 | static int dpa_show(struct seq_file *m, void *v) | ||
973 | { | ||
974 | seq_puts(m, "DEBUG_PAGEALLOC\n"); | ||
975 | seq_printf(m, "pool_size : %lu\n", pool_size); | ||
976 | seq_printf(m, "pool_pages : %lu\n", pool_pages); | ||
977 | seq_printf(m, "pool_low : %lu\n", pool_low); | ||
978 | seq_printf(m, "pool_used : %lu\n", pool_used); | ||
979 | seq_printf(m, "pool_failed : %lu\n", pool_failed); | ||
980 | |||
981 | return 0; | ||
982 | } | ||
983 | |||
984 | static int dpa_open(struct inode *inode, struct file *filp) | ||
985 | { | ||
986 | return single_open(filp, dpa_show, NULL); | ||
987 | } | ||
988 | |||
989 | static const struct file_operations dpa_fops = { | ||
990 | .open = dpa_open, | ||
991 | .read = seq_read, | ||
992 | .llseek = seq_lseek, | ||
993 | .release = single_release, | ||
994 | }; | ||
995 | |||
996 | int __init debug_pagealloc_proc_init(void) | ||
997 | { | ||
998 | struct dentry *de; | ||
999 | |||
1000 | de = debugfs_create_file("debug_pagealloc", 0600, NULL, NULL, | ||
1001 | &dpa_fops); | ||
1002 | if (!de) | ||
1003 | return -ENOMEM; | ||
1004 | |||
1005 | return 0; | ||
1006 | } | ||
1007 | __initcall(debug_pagealloc_proc_init); | ||
1008 | #endif | ||
1009 | |||
921 | #ifdef CONFIG_HIBERNATION | 1010 | #ifdef CONFIG_HIBERNATION |
922 | 1011 | ||
923 | bool kernel_page_present(struct page *page) | 1012 | bool kernel_page_present(struct page *page) |