Skip to content

Commit 3c9b84f

Browse files
Gavin Shantorvalds
authored andcommitted
mm/debug_vm_pgtable: introduce struct pgtable_debug_args
Patch series "mm/debug_vm_pgtable: Enhancements", v6. There are a couple of issues with current implementations and this series tries to resolve the issues: (a) All needed information are scattered in variables, passed to various test functions. The code is organized in pretty much relaxed fashion. (b) The page isn't allocated from buddy during page table entry modifying tests. The page can be invalid, conflicting to the implementations of set_xxx_at() on ARM64. The target page is accessed so that the iCache can be flushed when execution permission is given on ARM64. Besides, the target page can be unmapped and accessing to it causes kernel crash. "struct pgtable_debug_args" is introduced to address issue (a). For issue (b), the used page is allocated from buddy in page table entry modifying tests. The corresponding tets will be skipped if we fail to allocate the (huge) page. For other test cases, the original page around to kernel symbol (@start_kernel) is still used. The patches are organized as below. PATCH[2-10] could be combined to one patch, but it will make the review harder: PATCH[1] introduces "struct pgtable_debug_args" as place holder of all needed information. With it, the old and new implementation can coexist. PATCH[2-10] uses "struct pgtable_debug_args" in various test functions. PATCH[11] removes the unused code for old implementation. PATCH[12] fixes the issue of corrupted page flag for ARM64 This patch (of 6): In debug_vm_pgtable(), there are many local variables introduced to track the needed information and they are passed to the functions for various test cases. It'd better to introduce a struct as place holder for these information. With it, what the tests functions need is the struct. In this way, the code is simplified and easier to be maintained. Besides, set_xxx_at() could access the data on the corresponding pages in the page table modifying tests. So the accessed pages in the tests should have been allocated from buddy. Otherwise, we're accessing pages that aren't owned by us. This causes issues like page flag corruption or kernel crash on accessing unmapped page when CONFIG_DEBUG_PAGEALLOC is enabled. This introduces "struct pgtable_debug_args". The struct is initialized and destroyed, but the information in the struct isn't used yet. It will be used in subsequent patches. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Gavin Shan <[email protected]> Reviewed-by: Anshuman Khandual <[email protected]> Tested-by: Christophe Leroy <[email protected]> [powerpc 8xx] Tested-by: Gerald Schaefer <[email protected]> [s390] Cc: Anshuman Khandual <[email protected]> Cc: Aneesh Kumar K.V <[email protected]> Cc: Qian Cai <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Will Deacon <[email protected]> Cc: Vineet Gupta <[email protected]> Cc: Chunyu Hu <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 4bdffd2 commit 3c9b84f

File tree

1 file changed

+269
-1
lines changed

1 file changed

+269
-1
lines changed

mm/debug_vm_pgtable.c

Lines changed: 269 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,37 @@
5858
#define RANDOM_ORVALUE (GENMASK(BITS_PER_LONG - 1, 0) & ~ARCH_SKIP_MASK)
5959
#define RANDOM_NZVALUE GENMASK(7, 0)
6060

61+
struct pgtable_debug_args {
62+
struct mm_struct *mm;
63+
struct vm_area_struct *vma;
64+
65+
pgd_t *pgdp;
66+
p4d_t *p4dp;
67+
pud_t *pudp;
68+
pmd_t *pmdp;
69+
pte_t *ptep;
70+
71+
p4d_t *start_p4dp;
72+
pud_t *start_pudp;
73+
pmd_t *start_pmdp;
74+
pgtable_t start_ptep;
75+
76+
unsigned long vaddr;
77+
pgprot_t page_prot;
78+
pgprot_t page_prot_none;
79+
80+
bool is_contiguous_page;
81+
unsigned long pud_pfn;
82+
unsigned long pmd_pfn;
83+
unsigned long pte_pfn;
84+
85+
unsigned long fixed_pgd_pfn;
86+
unsigned long fixed_p4d_pfn;
87+
unsigned long fixed_pud_pfn;
88+
unsigned long fixed_pmd_pfn;
89+
unsigned long fixed_pte_pfn;
90+
};
91+
6192
static void __init pte_basic_tests(unsigned long pfn, int idx)
6293
{
6394
pgprot_t prot = protection_map[idx];
@@ -955,8 +986,239 @@ static unsigned long __init get_random_vaddr(void)
955986
return random_vaddr;
956987
}
957988

989+
static void __init destroy_args(struct pgtable_debug_args *args)
990+
{
991+
struct page *page = NULL;
992+
993+
/* Free (huge) page */
994+
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
995+
IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
996+
has_transparent_hugepage() &&
997+
args->pud_pfn != ULONG_MAX) {
998+
if (args->is_contiguous_page) {
999+
free_contig_range(args->pud_pfn,
1000+
(1 << (HPAGE_PUD_SHIFT - PAGE_SHIFT)));
1001+
} else {
1002+
page = pfn_to_page(args->pud_pfn);
1003+
__free_pages(page, HPAGE_PUD_SHIFT - PAGE_SHIFT);
1004+
}
1005+
1006+
args->pud_pfn = ULONG_MAX;
1007+
args->pmd_pfn = ULONG_MAX;
1008+
args->pte_pfn = ULONG_MAX;
1009+
}
1010+
1011+
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
1012+
has_transparent_hugepage() &&
1013+
args->pmd_pfn != ULONG_MAX) {
1014+
if (args->is_contiguous_page) {
1015+
free_contig_range(args->pmd_pfn, (1 << HPAGE_PMD_ORDER));
1016+
} else {
1017+
page = pfn_to_page(args->pmd_pfn);
1018+
__free_pages(page, HPAGE_PMD_ORDER);
1019+
}
1020+
1021+
args->pmd_pfn = ULONG_MAX;
1022+
args->pte_pfn = ULONG_MAX;
1023+
}
1024+
1025+
if (args->pte_pfn != ULONG_MAX) {
1026+
page = pfn_to_page(args->pte_pfn);
1027+
__free_pages(page, 0);
1028+
1029+
args->pte_pfn = ULONG_MAX;
1030+
}
1031+
1032+
/* Free page table entries */
1033+
if (args->start_ptep) {
1034+
pte_free(args->mm, args->start_ptep);
1035+
mm_dec_nr_ptes(args->mm);
1036+
}
1037+
1038+
if (args->start_pmdp) {
1039+
pmd_free(args->mm, args->start_pmdp);
1040+
mm_dec_nr_pmds(args->mm);
1041+
}
1042+
1043+
if (args->start_pudp) {
1044+
pud_free(args->mm, args->start_pudp);
1045+
mm_dec_nr_puds(args->mm);
1046+
}
1047+
1048+
if (args->start_p4dp)
1049+
p4d_free(args->mm, args->start_p4dp);
1050+
1051+
/* Free vma and mm struct */
1052+
if (args->vma)
1053+
vm_area_free(args->vma);
1054+
1055+
if (args->mm)
1056+
mmdrop(args->mm);
1057+
}
1058+
1059+
static struct page * __init
1060+
debug_vm_pgtable_alloc_huge_page(struct pgtable_debug_args *args, int order)
1061+
{
1062+
struct page *page = NULL;
1063+
1064+
#ifdef CONFIG_CONTIG_ALLOC
1065+
if (order >= MAX_ORDER) {
1066+
page = alloc_contig_pages((1 << order), GFP_KERNEL,
1067+
first_online_node, NULL);
1068+
if (page) {
1069+
args->is_contiguous_page = true;
1070+
return page;
1071+
}
1072+
}
1073+
#endif
1074+
1075+
if (order < MAX_ORDER)
1076+
page = alloc_pages(GFP_KERNEL, order);
1077+
1078+
return page;
1079+
}
1080+
1081+
static int __init init_args(struct pgtable_debug_args *args)
1082+
{
1083+
struct page *page = NULL;
1084+
phys_addr_t phys;
1085+
int ret = 0;
1086+
1087+
/*
1088+
* Initialize the debugging data.
1089+
*
1090+
* __P000 (or even __S000) will help create page table entries with
1091+
* PROT_NONE permission as required for pxx_protnone_tests().
1092+
*/
1093+
memset(args, 0, sizeof(*args));
1094+
args->vaddr = get_random_vaddr();
1095+
args->page_prot = vm_get_page_prot(VMFLAGS);
1096+
args->page_prot_none = __P000;
1097+
args->is_contiguous_page = false;
1098+
args->pud_pfn = ULONG_MAX;
1099+
args->pmd_pfn = ULONG_MAX;
1100+
args->pte_pfn = ULONG_MAX;
1101+
args->fixed_pgd_pfn = ULONG_MAX;
1102+
args->fixed_p4d_pfn = ULONG_MAX;
1103+
args->fixed_pud_pfn = ULONG_MAX;
1104+
args->fixed_pmd_pfn = ULONG_MAX;
1105+
args->fixed_pte_pfn = ULONG_MAX;
1106+
1107+
/* Allocate mm and vma */
1108+
args->mm = mm_alloc();
1109+
if (!args->mm) {
1110+
pr_err("Failed to allocate mm struct\n");
1111+
ret = -ENOMEM;
1112+
goto error;
1113+
}
1114+
1115+
args->vma = vm_area_alloc(args->mm);
1116+
if (!args->vma) {
1117+
pr_err("Failed to allocate vma\n");
1118+
ret = -ENOMEM;
1119+
goto error;
1120+
}
1121+
1122+
/*
1123+
* Allocate page table entries. They will be modified in the tests.
1124+
* Lets save the page table entries so that they can be released
1125+
* when the tests are completed.
1126+
*/
1127+
args->pgdp = pgd_offset(args->mm, args->vaddr);
1128+
args->p4dp = p4d_alloc(args->mm, args->pgdp, args->vaddr);
1129+
if (!args->p4dp) {
1130+
pr_err("Failed to allocate p4d entries\n");
1131+
ret = -ENOMEM;
1132+
goto error;
1133+
}
1134+
args->start_p4dp = p4d_offset(args->pgdp, 0UL);
1135+
WARN_ON(!args->start_p4dp);
1136+
1137+
args->pudp = pud_alloc(args->mm, args->p4dp, args->vaddr);
1138+
if (!args->pudp) {
1139+
pr_err("Failed to allocate pud entries\n");
1140+
ret = -ENOMEM;
1141+
goto error;
1142+
}
1143+
args->start_pudp = pud_offset(args->p4dp, 0UL);
1144+
WARN_ON(!args->start_pudp);
1145+
1146+
args->pmdp = pmd_alloc(args->mm, args->pudp, args->vaddr);
1147+
if (!args->pmdp) {
1148+
pr_err("Failed to allocate pmd entries\n");
1149+
ret = -ENOMEM;
1150+
goto error;
1151+
}
1152+
args->start_pmdp = pmd_offset(args->pudp, 0UL);
1153+
WARN_ON(!args->start_pmdp);
1154+
1155+
if (pte_alloc(args->mm, args->pmdp)) {
1156+
pr_err("Failed to allocate pte entries\n");
1157+
ret = -ENOMEM;
1158+
goto error;
1159+
}
1160+
args->start_ptep = pmd_pgtable(READ_ONCE(*args->pmdp));
1161+
WARN_ON(!args->start_ptep);
1162+
1163+
/*
1164+
* PFN for mapping at PTE level is determined from a standard kernel
1165+
* text symbol. But pfns for higher page table levels are derived by
1166+
* masking lower bits of this real pfn. These derived pfns might not
1167+
* exist on the platform but that does not really matter as pfn_pxx()
1168+
* helpers will still create appropriate entries for the test. This
1169+
* helps avoid large memory block allocations to be used for mapping
1170+
* at higher page table levels in some of the tests.
1171+
*/
1172+
phys = __pa_symbol(&start_kernel);
1173+
args->fixed_pgd_pfn = __phys_to_pfn(phys & PGDIR_MASK);
1174+
args->fixed_p4d_pfn = __phys_to_pfn(phys & P4D_MASK);
1175+
args->fixed_pud_pfn = __phys_to_pfn(phys & PUD_MASK);
1176+
args->fixed_pmd_pfn = __phys_to_pfn(phys & PMD_MASK);
1177+
args->fixed_pte_pfn = __phys_to_pfn(phys & PAGE_MASK);
1178+
WARN_ON(!pfn_valid(args->fixed_pte_pfn));
1179+
1180+
/*
1181+
* Allocate (huge) pages because some of the tests need to access
1182+
* the data in the pages. The corresponding tests will be skipped
1183+
* if we fail to allocate (huge) pages.
1184+
*/
1185+
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
1186+
IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD) &&
1187+
has_transparent_hugepage()) {
1188+
page = debug_vm_pgtable_alloc_huge_page(args,
1189+
HPAGE_PUD_SHIFT - PAGE_SHIFT);
1190+
if (page) {
1191+
args->pud_pfn = page_to_pfn(page);
1192+
args->pmd_pfn = args->pud_pfn;
1193+
args->pte_pfn = args->pud_pfn;
1194+
return 0;
1195+
}
1196+
}
1197+
1198+
if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
1199+
has_transparent_hugepage()) {
1200+
page = debug_vm_pgtable_alloc_huge_page(args, HPAGE_PMD_ORDER);
1201+
if (page) {
1202+
args->pmd_pfn = page_to_pfn(page);
1203+
args->pte_pfn = args->pmd_pfn;
1204+
return 0;
1205+
}
1206+
}
1207+
1208+
page = alloc_pages(GFP_KERNEL, 0);
1209+
if (page)
1210+
args->pte_pfn = page_to_pfn(page);
1211+
1212+
return 0;
1213+
1214+
error:
1215+
destroy_args(args);
1216+
return ret;
1217+
}
1218+
9581219
static int __init debug_vm_pgtable(void)
9591220
{
1221+
struct pgtable_debug_args args;
9601222
struct vm_area_struct *vma;
9611223
struct mm_struct *mm;
9621224
pgd_t *pgdp;
@@ -970,9 +1232,13 @@ static int __init debug_vm_pgtable(void)
9701232
unsigned long vaddr, pte_aligned, pmd_aligned;
9711233
unsigned long pud_aligned, p4d_aligned, pgd_aligned;
9721234
spinlock_t *ptl = NULL;
973-
int idx;
1235+
int idx, ret;
9741236

9751237
pr_info("Validating architecture page table helpers\n");
1238+
ret = init_args(&args);
1239+
if (ret)
1240+
return ret;
1241+
9761242
prot = vm_get_page_prot(VMFLAGS);
9771243
vaddr = get_random_vaddr();
9781244
mm = mm_alloc();
@@ -1127,6 +1393,8 @@ static int __init debug_vm_pgtable(void)
11271393
mm_dec_nr_pmds(mm);
11281394
mm_dec_nr_ptes(mm);
11291395
mmdrop(mm);
1396+
1397+
destroy_args(&args);
11301398
return 0;
11311399
}
11321400
late_initcall(debug_vm_pgtable);

0 commit comments

Comments
 (0)