Currently, page_table_check when detects errors panics kernel. Instead, print a warning, and panic only when specifically requested via kernel parameter: page_table_check=panic Signed-off-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx> --- mm/page_table_check.c | 53 +++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/mm/page_table_check.c b/mm/page_table_check.c index 665ece0d55d4..881f19d0714c 100644 --- a/mm/page_table_check.c +++ b/mm/page_table_check.c @@ -17,13 +17,37 @@ struct page_table_check { static bool __page_table_check_enabled __initdata = IS_ENABLED(CONFIG_PAGE_TABLE_CHECK_ENFORCED); +static bool __page_table_check_panic; DEFINE_STATIC_KEY_TRUE(page_table_check_disabled); EXPORT_SYMBOL(page_table_check_disabled); +#define PAGE_TABLE_CHECK_BUG(v) \ + do { \ + bool __bug = !!(v); \ + \ + if (__page_table_check_panic) \ + BUG_ON(__bug); \ + else if (WARN_ON_ONCE(__bug)) \ + static_branch_enable(&page_table_check_disabled); \ + } while (false) + static int __init early_page_table_check_param(char *buf) { - return strtobool(buf, &__page_table_check_enabled); + int rc = strtobool(buf, &__page_table_check_enabled); + + if (rc) { + if (!strcmp(buf, "panic")) { + __page_table_check_enabled = true; + __page_table_check_panic = true; + rc = 0; + } + } + + if (rc) + pr_warn("Invalid option string: '%s'\n", buf); + + return rc; } early_param("page_table_check", early_page_table_check_param); @@ -48,7 +72,8 @@ struct page_ext_operations page_table_check_ops = { static struct page_table_check *get_page_table_check(struct page_ext *page_ext) { - BUG_ON(!page_ext); + PAGE_TABLE_CHECK_BUG(!page_ext); + return (void *)(page_ext) + page_table_check_ops.offset; } @@ -75,11 +100,11 @@ static void page_table_check_clear(struct mm_struct *mm, unsigned long addr, struct page_table_check *ptc = get_page_table_check(page_ext); if (anon) { - BUG_ON(atomic_read(&ptc->file_map_count)); - BUG_ON(atomic_dec_return(&ptc->anon_map_count) < 0); + PAGE_TABLE_CHECK_BUG(atomic_read(&ptc->file_map_count)); + PAGE_TABLE_CHECK_BUG(atomic_dec_return(&ptc->anon_map_count) < 0); } else { - BUG_ON(atomic_read(&ptc->anon_map_count)); - BUG_ON(atomic_dec_return(&ptc->file_map_count) < 0); + PAGE_TABLE_CHECK_BUG(atomic_read(&ptc->anon_map_count)); + PAGE_TABLE_CHECK_BUG(atomic_dec_return(&ptc->file_map_count) < 0); } page_ext = page_ext_next(page_ext); } @@ -102,7 +127,7 @@ static void page_table_check_set(struct mm_struct *mm, unsigned long addr, if (!pfn_valid(pfn)) return; - BUG_ON(is_zero_pfn(pfn) && rw); + PAGE_TABLE_CHECK_BUG(!is_zero_pfn(pfn) && rw); page = pfn_to_page(pfn); page_ext = lookup_page_ext(page); @@ -112,11 +137,11 @@ static void page_table_check_set(struct mm_struct *mm, unsigned long addr, struct page_table_check *ptc = get_page_table_check(page_ext); if (anon) { - BUG_ON(atomic_read(&ptc->file_map_count)); - BUG_ON(atomic_inc_return(&ptc->anon_map_count) > 1 && rw); + PAGE_TABLE_CHECK_BUG(atomic_read(&ptc->file_map_count)); + PAGE_TABLE_CHECK_BUG(atomic_inc_return(&ptc->anon_map_count) > 1 && rw); } else { - BUG_ON(atomic_read(&ptc->anon_map_count)); - BUG_ON(atomic_inc_return(&ptc->file_map_count) < 0); + PAGE_TABLE_CHECK_BUG(atomic_read(&ptc->anon_map_count)); + PAGE_TABLE_CHECK_BUG(atomic_inc_return(&ptc->file_map_count) < 0); } page_ext = page_ext_next(page_ext); } @@ -131,12 +156,12 @@ void __page_table_check_zero(struct page *page, unsigned int order) struct page_ext *page_ext = lookup_page_ext(page); unsigned long i; - BUG_ON(!page_ext); + PAGE_TABLE_CHECK_BUG(!page_ext); for (i = 0; i < (1ul << order); i++) { struct page_table_check *ptc = get_page_table_check(page_ext); - BUG_ON(atomic_read(&ptc->anon_map_count)); - BUG_ON(atomic_read(&ptc->file_map_count)); + PAGE_TABLE_CHECK_BUG(atomic_read(&ptc->anon_map_count)); + PAGE_TABLE_CHECK_BUG(atomic_read(&ptc->file_map_count)); page_ext = page_ext_next(page_ext); } } -- 2.37.2.789.g6183377224-goog