diff mbox series

[v2,3/4] mm/page_alloc.c: pass all bad reasons to bad_page()

Message ID 20200120030415.15925-4-richardw.yang@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series mm/page_alloc.c: cleanup on check page | expand

Commit Message

Wei Yang Jan. 20, 2020, 3:04 a.m. UTC
Now we can pass all bad reasons to __dump_page().

Signed-off-by: Wei Yang <richardw.yang@linux.intel.com>
---
 mm/page_alloc.c | 52 ++++++++++++++++++++++++++-----------------------
 1 file changed, 28 insertions(+), 24 deletions(-)

Comments

Anshuman Khandual Jan. 20, 2020, 6:33 a.m. UTC | #1
On 01/20/2020 08:34 AM, Wei Yang wrote:
> Now we can pass all bad reasons to __dump_page().
> 
> Signed-off-by: Wei Yang <richardw.yang@linux.intel.com>
> ---
>  mm/page_alloc.c | 52 ++++++++++++++++++++++++++-----------------------
>  1 file changed, 28 insertions(+), 24 deletions(-)
> 
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index a43b9d2482f2..a7b793c739fc 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -609,7 +609,7 @@ static inline int __maybe_unused bad_range(struct zone *zone, struct page *page)
>  }
>  #endif
>  
> -static void bad_page(struct page *page, const char *reason,
> +static void bad_page(struct page *page, int nr, const char **reason,
>  		unsigned long bad_flags)
>  {
>  	static unsigned long resume;
> @@ -638,7 +638,7 @@ static void bad_page(struct page *page, const char *reason,
>  
>  	pr_alert("BUG: Bad page state in process %s  pfn:%05lx\n",
>  		current->comm, page_to_pfn(page));
> -	__dump_page(page, 1, &reason);
> +	__dump_page(page, nr, reason);
>  	bad_flags &= page->flags;
>  	if (bad_flags)
>  		pr_alert("bad because of flags: %#lx(%pGp)\n",
> @@ -1027,27 +1027,25 @@ static inline bool page_expected_state(struct page *page,
>  
>  static void free_pages_check_bad(struct page *page)
>  {
> -	const char *bad_reason;
> -	unsigned long bad_flags;
> -
> -	bad_reason = NULL;
> -	bad_flags = 0;
> +	const char *bad_reason[5];

s/5/NR_BAD_PAGE_REASONS


> +	unsigned long bad_flags = 0;
> +	int nr = 0;
>  
>  	if (unlikely(atomic_read(&page->_mapcount) != -1))
> -		bad_reason = "nonzero mapcount";
> +		bad_reason[nr++] = "nonzero mapcount";
>  	if (unlikely(page->mapping != NULL))
> -		bad_reason = "non-NULL mapping";
> +		bad_reason[nr++] = "non-NULL mapping";
>  	if (unlikely(page_ref_count(page) != 0))
> -		bad_reason = "nonzero _refcount";
> +		bad_reason[nr++] = "nonzero _refcount";
>  	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_FREE)) {
> -		bad_reason = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
> +		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
>  		bad_flags = PAGE_FLAGS_CHECK_AT_FREE;
>  	}
>  #ifdef CONFIG_MEMCG
>  	if (unlikely(page->mem_cgroup))
> -		bad_reason = "page still charged to cgroup";
> +		bad_reason[nr++] = "page still charged to cgroup";
>  #endif
> -	bad_page(page, bad_reason, bad_flags);
> +	bad_page(page, nr, bad_reason, bad_flags);
>  }
>  
>  static inline int free_pages_check(struct page *page)
> @@ -1062,6 +1060,7 @@ static inline int free_pages_check(struct page *page)
>  
>  static int free_tail_pages_check(struct page *head_page, struct page *page)
>  {
> +	const char *reason;
>  	int ret = 1;
>  
>  	/*
> @@ -1078,7 +1077,8 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
>  	case 1:
>  		/* the first tail page: ->mapping may be compound_mapcount() */
>  		if (unlikely(compound_mapcount(page))) {
> -			bad_page(page, "nonzero compound_mapcount", 0);
> +			reason = "nonzero compound_mapcount";
> +			bad_page(page, 1, &reason, 0);
>  			goto out;
>  		}
>  		break;
> @@ -1090,17 +1090,20 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
>  		break;
>  	default:
>  		if (page->mapping != TAIL_MAPPING) {
> -			bad_page(page, "corrupted mapping in tail page", 0);
> +			reason = "corrupted mapping in tail page";
> +			bad_page(page, 1, &reason, 0);
>  			goto out;
>  		}
>  		break;
>  	}
>  	if (unlikely(!PageTail(page))) {
> -		bad_page(page, "PageTail not set", 0);
> +		reason = "PageTail not set";
> +		bad_page(page, 1, &reason, 0);
>  		goto out;
>  	}
>  	if (unlikely(compound_head(page) != head_page)) {
> -		bad_page(page, "compound_head not consistent", 0);
> +		reason = "compound_head not consistent";
> +		bad_page(page, 1, &reason, 0);
>  		goto out;
>  	}
>  	ret = 0;
> @@ -2041,29 +2044,30 @@ static inline void expand(struct zone *zone, struct page *page,
>  
>  static void check_new_page_bad(struct page *page)
>  {
> -	const char *bad_reason = NULL;
> +	const char *bad_reason[5];

s/5/NR_BAD_PAGE_REASONS 

>  	unsigned long bad_flags = 0;
> +	int nr = 0;
>  
>  	if (unlikely(atomic_read(&page->_mapcount) != -1))
> -		bad_reason = "nonzero mapcount";
> +		bad_reason[nr++] = "nonzero mapcount";
>  	if (unlikely(page->mapping != NULL))
> -		bad_reason = "non-NULL mapping";
> +		bad_reason[nr++] = "non-NULL mapping";
>  	if (unlikely(page_ref_count(page) != 0))
> -		bad_reason = "nonzero _refcount";
> +		bad_reason[nr++] = "nonzero _refcount";
>  	if (unlikely(page->flags & __PG_HWPOISON)) {
>  		/* Don't complain about hwpoisoned pages */
>  		page_mapcount_reset(page); /* remove PageBuddy */
>  		return;
>  	}
>  	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
> -		bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
> +		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_PREP flag set";
>  		bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
>  	}
>  #ifdef CONFIG_MEMCG
>  	if (unlikely(page->mem_cgroup))
> -		bad_reason = "page still charged to cgroup";
> +		bad_reason[nr++] = "page still charged to cgroup";
>  #endif
> -	bad_page(page, bad_reason, bad_flags);
> +	bad_page(page, 1, bad_reason, bad_flags);
This should be 'nr' here instead ?

>  }
>  
>  /*
>
Michal Hocko Jan. 20, 2020, 10:22 a.m. UTC | #2
On Mon 20-01-20 11:04:14, Wei Yang wrote:
> Now we can pass all bad reasons to __dump_page().

And we do we want to do that? The dump of the page will tell us the
whole story so a single and the most important reason sounds like a
better implementation. The code is also more subtle because each caller
of the function has to be aware of how many reasons there might be.
Not to mention that you need a room for 5 pointers on the stack and this
and page allocator might be called from deeper call chains.

> Signed-off-by: Wei Yang <richardw.yang@linux.intel.com>
> ---
>  mm/page_alloc.c | 52 ++++++++++++++++++++++++++-----------------------
>  1 file changed, 28 insertions(+), 24 deletions(-)
> 
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index a43b9d2482f2..a7b793c739fc 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -609,7 +609,7 @@ static inline int __maybe_unused bad_range(struct zone *zone, struct page *page)
>  }
>  #endif
>  
> -static void bad_page(struct page *page, const char *reason,
> +static void bad_page(struct page *page, int nr, const char **reason,
>  		unsigned long bad_flags)
>  {
>  	static unsigned long resume;
> @@ -638,7 +638,7 @@ static void bad_page(struct page *page, const char *reason,
>  
>  	pr_alert("BUG: Bad page state in process %s  pfn:%05lx\n",
>  		current->comm, page_to_pfn(page));
> -	__dump_page(page, 1, &reason);
> +	__dump_page(page, nr, reason);
>  	bad_flags &= page->flags;
>  	if (bad_flags)
>  		pr_alert("bad because of flags: %#lx(%pGp)\n",
> @@ -1027,27 +1027,25 @@ static inline bool page_expected_state(struct page *page,
>  
>  static void free_pages_check_bad(struct page *page)
>  {
> -	const char *bad_reason;
> -	unsigned long bad_flags;
> -
> -	bad_reason = NULL;
> -	bad_flags = 0;
> +	const char *bad_reason[5];
> +	unsigned long bad_flags = 0;
> +	int nr = 0;
>  
>  	if (unlikely(atomic_read(&page->_mapcount) != -1))
> -		bad_reason = "nonzero mapcount";
> +		bad_reason[nr++] = "nonzero mapcount";
>  	if (unlikely(page->mapping != NULL))
> -		bad_reason = "non-NULL mapping";
> +		bad_reason[nr++] = "non-NULL mapping";
>  	if (unlikely(page_ref_count(page) != 0))
> -		bad_reason = "nonzero _refcount";
> +		bad_reason[nr++] = "nonzero _refcount";
>  	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_FREE)) {
> -		bad_reason = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
> +		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
>  		bad_flags = PAGE_FLAGS_CHECK_AT_FREE;
>  	}
>  #ifdef CONFIG_MEMCG
>  	if (unlikely(page->mem_cgroup))
> -		bad_reason = "page still charged to cgroup";
> +		bad_reason[nr++] = "page still charged to cgroup";
>  #endif
> -	bad_page(page, bad_reason, bad_flags);
> +	bad_page(page, nr, bad_reason, bad_flags);
>  }
>  
>  static inline int free_pages_check(struct page *page)
> @@ -1062,6 +1060,7 @@ static inline int free_pages_check(struct page *page)
>  
>  static int free_tail_pages_check(struct page *head_page, struct page *page)
>  {
> +	const char *reason;
>  	int ret = 1;
>  
>  	/*
> @@ -1078,7 +1077,8 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
>  	case 1:
>  		/* the first tail page: ->mapping may be compound_mapcount() */
>  		if (unlikely(compound_mapcount(page))) {
> -			bad_page(page, "nonzero compound_mapcount", 0);
> +			reason = "nonzero compound_mapcount";
> +			bad_page(page, 1, &reason, 0);
>  			goto out;
>  		}
>  		break;
> @@ -1090,17 +1090,20 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
>  		break;
>  	default:
>  		if (page->mapping != TAIL_MAPPING) {
> -			bad_page(page, "corrupted mapping in tail page", 0);
> +			reason = "corrupted mapping in tail page";
> +			bad_page(page, 1, &reason, 0);
>  			goto out;
>  		}
>  		break;
>  	}
>  	if (unlikely(!PageTail(page))) {
> -		bad_page(page, "PageTail not set", 0);
> +		reason = "PageTail not set";
> +		bad_page(page, 1, &reason, 0);
>  		goto out;
>  	}
>  	if (unlikely(compound_head(page) != head_page)) {
> -		bad_page(page, "compound_head not consistent", 0);
> +		reason = "compound_head not consistent";
> +		bad_page(page, 1, &reason, 0);
>  		goto out;
>  	}
>  	ret = 0;
> @@ -2041,29 +2044,30 @@ static inline void expand(struct zone *zone, struct page *page,
>  
>  static void check_new_page_bad(struct page *page)
>  {
> -	const char *bad_reason = NULL;
> +	const char *bad_reason[5];
>  	unsigned long bad_flags = 0;
> +	int nr = 0;
>  
>  	if (unlikely(atomic_read(&page->_mapcount) != -1))
> -		bad_reason = "nonzero mapcount";
> +		bad_reason[nr++] = "nonzero mapcount";
>  	if (unlikely(page->mapping != NULL))
> -		bad_reason = "non-NULL mapping";
> +		bad_reason[nr++] = "non-NULL mapping";
>  	if (unlikely(page_ref_count(page) != 0))
> -		bad_reason = "nonzero _refcount";
> +		bad_reason[nr++] = "nonzero _refcount";
>  	if (unlikely(page->flags & __PG_HWPOISON)) {
>  		/* Don't complain about hwpoisoned pages */
>  		page_mapcount_reset(page); /* remove PageBuddy */
>  		return;
>  	}
>  	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
> -		bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
> +		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_PREP flag set";
>  		bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
>  	}
>  #ifdef CONFIG_MEMCG
>  	if (unlikely(page->mem_cgroup))
> -		bad_reason = "page still charged to cgroup";
> +		bad_reason[nr++] = "page still charged to cgroup";
>  #endif
> -	bad_page(page, bad_reason, bad_flags);
> +	bad_page(page, 1, bad_reason, bad_flags);
>  }
>  
>  /*
> -- 
> 2.17.1
>
David Hildenbrand Jan. 20, 2020, 12:19 p.m. UTC | #3
On 20.01.20 11:22, Michal Hocko wrote:
> On Mon 20-01-20 11:04:14, Wei Yang wrote:
>> Now we can pass all bad reasons to __dump_page().
> 
> And we do we want to do that? The dump of the page will tell us the
> whole story so a single and the most important reason sounds like a
> better implementation. The code is also more subtle because each caller
> of the function has to be aware of how many reasons there might be.
> Not to mention that you need a room for 5 pointers on the stack and this
> and page allocator might be called from deeper call chains.
> 

+1, I don't think we want/need this
Wei Yang Jan. 20, 2020, 12:33 p.m. UTC | #4
On Mon, Jan 20, 2020 at 12:03:20PM +0530, Anshuman Khandual wrote:
>
>
>On 01/20/2020 08:34 AM, Wei Yang wrote:
>> Now we can pass all bad reasons to __dump_page().
>> 
>> Signed-off-by: Wei Yang <richardw.yang@linux.intel.com>
>> ---
>>  mm/page_alloc.c | 52 ++++++++++++++++++++++++++-----------------------
>>  1 file changed, 28 insertions(+), 24 deletions(-)
>> 
>> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
>> index a43b9d2482f2..a7b793c739fc 100644
>> --- a/mm/page_alloc.c
>> +++ b/mm/page_alloc.c
>> @@ -609,7 +609,7 @@ static inline int __maybe_unused bad_range(struct zone *zone, struct page *page)
>>  }
>>  #endif
>>  
>> -static void bad_page(struct page *page, const char *reason,
>> +static void bad_page(struct page *page, int nr, const char **reason,
>>  		unsigned long bad_flags)
>>  {
>>  	static unsigned long resume;
>> @@ -638,7 +638,7 @@ static void bad_page(struct page *page, const char *reason,
>>  
>>  	pr_alert("BUG: Bad page state in process %s  pfn:%05lx\n",
>>  		current->comm, page_to_pfn(page));
>> -	__dump_page(page, 1, &reason);
>> +	__dump_page(page, nr, reason);
>>  	bad_flags &= page->flags;
>>  	if (bad_flags)
>>  		pr_alert("bad because of flags: %#lx(%pGp)\n",
>> @@ -1027,27 +1027,25 @@ static inline bool page_expected_state(struct page *page,
>>  
>>  static void free_pages_check_bad(struct page *page)
>>  {
>> -	const char *bad_reason;
>> -	unsigned long bad_flags;
>> -
>> -	bad_reason = NULL;
>> -	bad_flags = 0;
>> +	const char *bad_reason[5];
>
>s/5/NR_BAD_PAGE_REASONS
>
>
>> +	unsigned long bad_flags = 0;
>> +	int nr = 0;
>>  
>>  	if (unlikely(atomic_read(&page->_mapcount) != -1))
>> -		bad_reason = "nonzero mapcount";
>> +		bad_reason[nr++] = "nonzero mapcount";
>>  	if (unlikely(page->mapping != NULL))
>> -		bad_reason = "non-NULL mapping";
>> +		bad_reason[nr++] = "non-NULL mapping";
>>  	if (unlikely(page_ref_count(page) != 0))
>> -		bad_reason = "nonzero _refcount";
>> +		bad_reason[nr++] = "nonzero _refcount";
>>  	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_FREE)) {
>> -		bad_reason = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
>> +		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
>>  		bad_flags = PAGE_FLAGS_CHECK_AT_FREE;
>>  	}
>>  #ifdef CONFIG_MEMCG
>>  	if (unlikely(page->mem_cgroup))
>> -		bad_reason = "page still charged to cgroup";
>> +		bad_reason[nr++] = "page still charged to cgroup";
>>  #endif
>> -	bad_page(page, bad_reason, bad_flags);
>> +	bad_page(page, nr, bad_reason, bad_flags);
>>  }
>>  
>>  static inline int free_pages_check(struct page *page)
>> @@ -1062,6 +1060,7 @@ static inline int free_pages_check(struct page *page)
>>  
>>  static int free_tail_pages_check(struct page *head_page, struct page *page)
>>  {
>> +	const char *reason;
>>  	int ret = 1;
>>  
>>  	/*
>> @@ -1078,7 +1077,8 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
>>  	case 1:
>>  		/* the first tail page: ->mapping may be compound_mapcount() */
>>  		if (unlikely(compound_mapcount(page))) {
>> -			bad_page(page, "nonzero compound_mapcount", 0);
>> +			reason = "nonzero compound_mapcount";
>> +			bad_page(page, 1, &reason, 0);
>>  			goto out;
>>  		}
>>  		break;
>> @@ -1090,17 +1090,20 @@ static int free_tail_pages_check(struct page *head_page, struct page *page)
>>  		break;
>>  	default:
>>  		if (page->mapping != TAIL_MAPPING) {
>> -			bad_page(page, "corrupted mapping in tail page", 0);
>> +			reason = "corrupted mapping in tail page";
>> +			bad_page(page, 1, &reason, 0);
>>  			goto out;
>>  		}
>>  		break;
>>  	}
>>  	if (unlikely(!PageTail(page))) {
>> -		bad_page(page, "PageTail not set", 0);
>> +		reason = "PageTail not set";
>> +		bad_page(page, 1, &reason, 0);
>>  		goto out;
>>  	}
>>  	if (unlikely(compound_head(page) != head_page)) {
>> -		bad_page(page, "compound_head not consistent", 0);
>> +		reason = "compound_head not consistent";
>> +		bad_page(page, 1, &reason, 0);
>>  		goto out;
>>  	}
>>  	ret = 0;
>> @@ -2041,29 +2044,30 @@ static inline void expand(struct zone *zone, struct page *page,
>>  
>>  static void check_new_page_bad(struct page *page)
>>  {
>> -	const char *bad_reason = NULL;
>> +	const char *bad_reason[5];
>
>s/5/NR_BAD_PAGE_REASONS 
>
>>  	unsigned long bad_flags = 0;
>> +	int nr = 0;
>>  
>>  	if (unlikely(atomic_read(&page->_mapcount) != -1))
>> -		bad_reason = "nonzero mapcount";
>> +		bad_reason[nr++] = "nonzero mapcount";
>>  	if (unlikely(page->mapping != NULL))
>> -		bad_reason = "non-NULL mapping";
>> +		bad_reason[nr++] = "non-NULL mapping";
>>  	if (unlikely(page_ref_count(page) != 0))
>> -		bad_reason = "nonzero _refcount";
>> +		bad_reason[nr++] = "nonzero _refcount";
>>  	if (unlikely(page->flags & __PG_HWPOISON)) {
>>  		/* Don't complain about hwpoisoned pages */
>>  		page_mapcount_reset(page); /* remove PageBuddy */
>>  		return;
>>  	}
>>  	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
>> -		bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
>> +		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_PREP flag set";
>>  		bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
>>  	}
>>  #ifdef CONFIG_MEMCG
>>  	if (unlikely(page->mem_cgroup))
>> -		bad_reason = "page still charged to cgroup";
>> +		bad_reason[nr++] = "page still charged to cgroup";
>>  #endif
>> -	bad_page(page, bad_reason, bad_flags);
>> +	bad_page(page, 1, bad_reason, bad_flags);
>This should be 'nr' here instead ?
>

Thanks, I missed this one.

>>  }
>>  
>>  /*
>>
Wei Yang Jan. 21, 2020, 1:49 a.m. UTC | #5
On Mon, Jan 20, 2020 at 01:19:10PM +0100, David Hildenbrand wrote:
>On 20.01.20 11:22, Michal Hocko wrote:
>> On Mon 20-01-20 11:04:14, Wei Yang wrote:
>>> Now we can pass all bad reasons to __dump_page().
>> 
>> And we do we want to do that? The dump of the page will tell us the
>> whole story so a single and the most important reason sounds like a
>> better implementation. The code is also more subtle because each caller
>> of the function has to be aware of how many reasons there might be.
>> Not to mention that you need a room for 5 pointers on the stack and this
>> and page allocator might be called from deeper call chains.
>> 
>
>+1, I don't think we want/need this
>

Well, I am fine with both.

Sounds we have 2 vs 2 voting :-)

>
>-- 
>Thanks,
>
>David / dhildenb
Anshuman Khandual Jan. 21, 2020, 6:08 a.m. UTC | #6
On 01/20/2020 03:52 PM, Michal Hocko wrote:
> On Mon 20-01-20 11:04:14, Wei Yang wrote:
>> Now we can pass all bad reasons to __dump_page().
> And we do we want to do that? The dump of the page will tell us the
> whole story so a single and the most important reason sounds like a
> better implementation. The code is also more subtle because each caller
> of the function has to be aware of how many reasons there might be.
> Not to mention that you need a room for 5 pointers on the stack and this
> and page allocator might be called from deeper call chains.
> 

Two paths which lead to __dump_page(), dump_page() and bad_page().
Callers of dump_page() can give a single reason what they consider the
most important which leads to page dumping. This makes sense but gets
trickier in bad_page() path. At present, free_pages_check_bad() and
check_new_page_bad() has a sequence of 'if' statements which decides
"most important" reason for __dump_page() without much rationale and
similar in case of free_tail_pages_check() as well. As all information
about the page for corresponding reasons are printed with __dump_page()
anyways, do free_pages_check_bad() or check_new_page_bad() really need
to provide any particular single reason ?
Michal Hocko Jan. 21, 2020, 8:47 a.m. UTC | #7
On Tue 21-01-20 11:38:29, Anshuman Khandual wrote:
> 
> 
> On 01/20/2020 03:52 PM, Michal Hocko wrote:
> > On Mon 20-01-20 11:04:14, Wei Yang wrote:
> >> Now we can pass all bad reasons to __dump_page().
> > And we do we want to do that? The dump of the page will tell us the
> > whole story so a single and the most important reason sounds like a
> > better implementation. The code is also more subtle because each caller
> > of the function has to be aware of how many reasons there might be.
> > Not to mention that you need a room for 5 pointers on the stack and this
> > and page allocator might be called from deeper call chains.
> > 
> 
> Two paths which lead to __dump_page(), dump_page() and bad_page().
> Callers of dump_page() can give a single reason what they consider the
> most important which leads to page dumping. This makes sense but gets
> trickier in bad_page() path. At present, free_pages_check_bad() and
> check_new_page_bad() has a sequence of 'if' statements which decides
> "most important" reason for __dump_page() without much rationale and
> similar in case of free_tail_pages_check() as well. As all information
> about the page for corresponding reasons are printed with __dump_page()
> anyways, do free_pages_check_bad() or check_new_page_bad() really need
> to provide any particular single reason ?

Do you see any particular problem with the existing logic? I find a
single reason sufficient and a good lead for what to check most of the
time.
diff mbox series

Patch

diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index a43b9d2482f2..a7b793c739fc 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -609,7 +609,7 @@  static inline int __maybe_unused bad_range(struct zone *zone, struct page *page)
 }
 #endif
 
-static void bad_page(struct page *page, const char *reason,
+static void bad_page(struct page *page, int nr, const char **reason,
 		unsigned long bad_flags)
 {
 	static unsigned long resume;
@@ -638,7 +638,7 @@  static void bad_page(struct page *page, const char *reason,
 
 	pr_alert("BUG: Bad page state in process %s  pfn:%05lx\n",
 		current->comm, page_to_pfn(page));
-	__dump_page(page, 1, &reason);
+	__dump_page(page, nr, reason);
 	bad_flags &= page->flags;
 	if (bad_flags)
 		pr_alert("bad because of flags: %#lx(%pGp)\n",
@@ -1027,27 +1027,25 @@  static inline bool page_expected_state(struct page *page,
 
 static void free_pages_check_bad(struct page *page)
 {
-	const char *bad_reason;
-	unsigned long bad_flags;
-
-	bad_reason = NULL;
-	bad_flags = 0;
+	const char *bad_reason[5];
+	unsigned long bad_flags = 0;
+	int nr = 0;
 
 	if (unlikely(atomic_read(&page->_mapcount) != -1))
-		bad_reason = "nonzero mapcount";
+		bad_reason[nr++] = "nonzero mapcount";
 	if (unlikely(page->mapping != NULL))
-		bad_reason = "non-NULL mapping";
+		bad_reason[nr++] = "non-NULL mapping";
 	if (unlikely(page_ref_count(page) != 0))
-		bad_reason = "nonzero _refcount";
+		bad_reason[nr++] = "nonzero _refcount";
 	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_FREE)) {
-		bad_reason = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
+		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_FREE flag(s) set";
 		bad_flags = PAGE_FLAGS_CHECK_AT_FREE;
 	}
 #ifdef CONFIG_MEMCG
 	if (unlikely(page->mem_cgroup))
-		bad_reason = "page still charged to cgroup";
+		bad_reason[nr++] = "page still charged to cgroup";
 #endif
-	bad_page(page, bad_reason, bad_flags);
+	bad_page(page, nr, bad_reason, bad_flags);
 }
 
 static inline int free_pages_check(struct page *page)
@@ -1062,6 +1060,7 @@  static inline int free_pages_check(struct page *page)
 
 static int free_tail_pages_check(struct page *head_page, struct page *page)
 {
+	const char *reason;
 	int ret = 1;
 
 	/*
@@ -1078,7 +1077,8 @@  static int free_tail_pages_check(struct page *head_page, struct page *page)
 	case 1:
 		/* the first tail page: ->mapping may be compound_mapcount() */
 		if (unlikely(compound_mapcount(page))) {
-			bad_page(page, "nonzero compound_mapcount", 0);
+			reason = "nonzero compound_mapcount";
+			bad_page(page, 1, &reason, 0);
 			goto out;
 		}
 		break;
@@ -1090,17 +1090,20 @@  static int free_tail_pages_check(struct page *head_page, struct page *page)
 		break;
 	default:
 		if (page->mapping != TAIL_MAPPING) {
-			bad_page(page, "corrupted mapping in tail page", 0);
+			reason = "corrupted mapping in tail page";
+			bad_page(page, 1, &reason, 0);
 			goto out;
 		}
 		break;
 	}
 	if (unlikely(!PageTail(page))) {
-		bad_page(page, "PageTail not set", 0);
+		reason = "PageTail not set";
+		bad_page(page, 1, &reason, 0);
 		goto out;
 	}
 	if (unlikely(compound_head(page) != head_page)) {
-		bad_page(page, "compound_head not consistent", 0);
+		reason = "compound_head not consistent";
+		bad_page(page, 1, &reason, 0);
 		goto out;
 	}
 	ret = 0;
@@ -2041,29 +2044,30 @@  static inline void expand(struct zone *zone, struct page *page,
 
 static void check_new_page_bad(struct page *page)
 {
-	const char *bad_reason = NULL;
+	const char *bad_reason[5];
 	unsigned long bad_flags = 0;
+	int nr = 0;
 
 	if (unlikely(atomic_read(&page->_mapcount) != -1))
-		bad_reason = "nonzero mapcount";
+		bad_reason[nr++] = "nonzero mapcount";
 	if (unlikely(page->mapping != NULL))
-		bad_reason = "non-NULL mapping";
+		bad_reason[nr++] = "non-NULL mapping";
 	if (unlikely(page_ref_count(page) != 0))
-		bad_reason = "nonzero _refcount";
+		bad_reason[nr++] = "nonzero _refcount";
 	if (unlikely(page->flags & __PG_HWPOISON)) {
 		/* Don't complain about hwpoisoned pages */
 		page_mapcount_reset(page); /* remove PageBuddy */
 		return;
 	}
 	if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
-		bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
+		bad_reason[nr++] = "PAGE_FLAGS_CHECK_AT_PREP flag set";
 		bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
 	}
 #ifdef CONFIG_MEMCG
 	if (unlikely(page->mem_cgroup))
-		bad_reason = "page still charged to cgroup";
+		bad_reason[nr++] = "page still charged to cgroup";
 #endif
-	bad_page(page, bad_reason, bad_flags);
+	bad_page(page, 1, bad_reason, bad_flags);
 }
 
 /*