diff mbox series

mm: don't convert the page to folio before splitting in split_huge_page()

Message ID 20240902124931.506061-2-kernel@pankajraghav.com (mailing list archive)
State New
Headers show
Series mm: don't convert the page to folio before splitting in split_huge_page() | expand

Commit Message

Pankaj Raghav (Samsung) Sept. 2, 2024, 12:49 p.m. UTC
From: Pankaj Raghav <p.raghav@samsung.com>

Sven reported that a commit from bs > ps series was breaking the ksm ltp
test[1].

split_huge_page() takes precisely a page that is locked, and it also
expects the folio that contains that page to be locked after that
huge page has been split. The changes introduced converted the page to
folio, and passed the head page to be split, which might not be locked,
resulting in a kernel panic.

This commit fixes it by always passing the correct page to be split from
split_huge_page() with the appropriate minimum order for splitting.

[1] https://lore.kernel.org/linux-xfs/yt9dttf3r49e.fsf@linux.ibm.com/
Reported-by: Sven Schnelle <svens@linux.ibm.com>
Fixes: fd031210c9ce ("mm: split a folio in minimum folio order chunks")
Signed-off-by: Pankaj Raghav <p.raghav@samsung.com>
---
This applies to the vfs.blocksize branch on the vfs tree.

@Christian, Stephen already sent a mail saying that there is a conflict
with these changes and mm-unstable. For now, I have based these patches
out of your tree. Let me know if you need the same patch based on
linux-next.

 include/linux/huge_mm.h | 16 +++++++++++++++-
 mm/huge_memory.c        | 21 +++++++++++++--------
 2 files changed, 28 insertions(+), 9 deletions(-)

Comments

David Hildenbrand Sept. 2, 2024, 1:35 p.m. UTC | #1
On 02.09.24 14:49, Pankaj Raghav (Samsung) wrote:
> From: Pankaj Raghav <p.raghav@samsung.com>
> 
> Sven reported that a commit from bs > ps series was breaking the ksm ltp
> test[1].
> 
> split_huge_page() takes precisely a page that is locked, and it also
> expects the folio that contains that page to be locked after that
> huge page has been split. The changes introduced converted the page to
> folio, and passed the head page to be split, which might not be locked,
> resulting in a kernel panic.
> 
> This commit fixes it by always passing the correct page to be split from
> split_huge_page() with the appropriate minimum order for splitting.

Looks reasonable.
Matthew Wilcox Sept. 2, 2024, 2 p.m. UTC | #2
On Mon, Sep 02, 2024 at 02:49:32PM +0200, Pankaj Raghav (Samsung) wrote:
> From: Pankaj Raghav <p.raghav@samsung.com>
> 
> Sven reported that a commit from bs > ps series was breaking the ksm ltp
> test[1].
> 
> split_huge_page() takes precisely a page that is locked, and it also
> expects the folio that contains that page to be locked after that
> huge page has been split. The changes introduced converted the page to
> folio, and passed the head page to be split, which might not be locked,
> resulting in a kernel panic.
> 
> This commit fixes it by always passing the correct page to be split from
> split_huge_page() with the appropriate minimum order for splitting.

This should be folded into the patch that is broken, not be a separate
fix commit, otherwise it introduces a bisection hazard which are to be
avoided when possible.

> [1] https://lore.kernel.org/linux-xfs/yt9dttf3r49e.fsf@linux.ibm.com/
> Reported-by: Sven Schnelle <svens@linux.ibm.com>
> Fixes: fd031210c9ce ("mm: split a folio in minimum folio order chunks")
> Signed-off-by: Pankaj Raghav <p.raghav@samsung.com>
> ---
> This applies to the vfs.blocksize branch on the vfs tree.
> 
> @Christian, Stephen already sent a mail saying that there is a conflict
> with these changes and mm-unstable. For now, I have based these patches
> out of your tree. Let me know if you need the same patch based on
> linux-next.
> 
>  include/linux/huge_mm.h | 16 +++++++++++++++-
>  mm/huge_memory.c        | 21 +++++++++++++--------
>  2 files changed, 28 insertions(+), 9 deletions(-)
> 
> diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
> index 7c50aeed0522..7a570e0437c9 100644
> --- a/include/linux/huge_mm.h
> +++ b/include/linux/huge_mm.h
> @@ -319,10 +319,24 @@ unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long add
>  bool can_split_folio(struct folio *folio, int *pextra_pins);
>  int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
>  		unsigned int new_order);
> +int min_order_for_split(struct folio *folio);
>  int split_folio_to_list(struct folio *folio, struct list_head *list);
>  static inline int split_huge_page(struct page *page)
>  {
> -	return split_folio(page_folio(page));
> +	struct folio *folio = page_folio(page);
> +	int ret = min_order_for_split(folio);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * split_huge_page() locks the page before splitting and
> +	 * expects the same page that has been split to be locked when
> +	 * returned. split_folio(page_folio(page)) cannot be used here
> +	 * because it converts the page to folio and passes the head
> +	 * page to be split.
> +	 */
> +	return split_huge_page_to_list_to_order(page, NULL, ret);
>  }
>  void deferred_split_folio(struct folio *folio);
>  
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index c29af9451d92..9931ff1d9a9d 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -3297,12 +3297,10 @@ int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
>  	return ret;
>  }
>  
> -int split_folio_to_list(struct folio *folio, struct list_head *list)
> +int min_order_for_split(struct folio *folio)
>  {
> -	unsigned int min_order = 0;
> -
>  	if (folio_test_anon(folio))
> -		goto out;
> +		return 0;
>  
>  	if (!folio->mapping) {
>  		if (folio_test_pmd_mappable(folio))
> @@ -3310,10 +3308,17 @@ int split_folio_to_list(struct folio *folio, struct list_head *list)
>  		return -EBUSY;
>  	}
>  
> -	min_order = mapping_min_folio_order(folio->mapping);
> -out:
> -	return split_huge_page_to_list_to_order(&folio->page, list,
> -							min_order);
> +	return mapping_min_folio_order(folio->mapping);
> +}
> +
> +int split_folio_to_list(struct folio *folio, struct list_head *list)
> +{
> +	int ret = min_order_for_split(folio);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	return split_huge_page_to_list_to_order(&folio->page, list, ret);
>  }
>  
>  void __folio_undo_large_rmappable(struct folio *folio)
> -- 
> 2.44.1
> 
>
Christian Brauner Sept. 2, 2024, 2:21 p.m. UTC | #3
On Mon, Sep 02, 2024 at 03:00:37PM GMT, Matthew Wilcox wrote:
> On Mon, Sep 02, 2024 at 02:49:32PM +0200, Pankaj Raghav (Samsung) wrote:
> > From: Pankaj Raghav <p.raghav@samsung.com>
> > 
> > Sven reported that a commit from bs > ps series was breaking the ksm ltp
> > test[1].
> > 
> > split_huge_page() takes precisely a page that is locked, and it also
> > expects the folio that contains that page to be locked after that
> > huge page has been split. The changes introduced converted the page to
> > folio, and passed the head page to be split, which might not be locked,
> > resulting in a kernel panic.
> > 
> > This commit fixes it by always passing the correct page to be split from
> > split_huge_page() with the appropriate minimum order for splitting.
> 
> This should be folded into the patch that is broken, not be a separate
> fix commit, otherwise it introduces a bisection hazard which are to be
> avoided when possible.

Patch folded into "mm: split a folio in minimum folio order chunks"
with the Link to this patch. Please double-check.
Pankaj Raghav (Samsung) Sept. 2, 2024, 2:48 p.m. UTC | #4
On Mon, Sep 02, 2024 at 04:21:09PM +0200, Christian Brauner wrote:
> On Mon, Sep 02, 2024 at 03:00:37PM GMT, Matthew Wilcox wrote:
> > On Mon, Sep 02, 2024 at 02:49:32PM +0200, Pankaj Raghav (Samsung) wrote:
> > > From: Pankaj Raghav <p.raghav@samsung.com>
> > > 
> > > Sven reported that a commit from bs > ps series was breaking the ksm ltp
> > > test[1].
> > > 
> > > split_huge_page() takes precisely a page that is locked, and it also
> > > expects the folio that contains that page to be locked after that
> > > huge page has been split. The changes introduced converted the page to
> > > folio, and passed the head page to be split, which might not be locked,
> > > resulting in a kernel panic.
> > > 
> > > This commit fixes it by always passing the correct page to be split from
> > > split_huge_page() with the appropriate minimum order for splitting.
> > 
> > This should be folded into the patch that is broken, not be a separate
> > fix commit, otherwise it introduces a bisection hazard which are to be
> > avoided when possible.
> 
> Patch folded into "mm: split a folio in minimum folio order chunks"
> with the Link to this patch. Please double-check.
Thanks a lot!

I still don't see it upstream[1]. Maybe it is yet to be pushed?

[1] https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git/commit/?h=vfs.blocksize&id=fd031210c9ceb399db1dea001c6a5e98f3b4e2e7
Christian Brauner Sept. 2, 2024, 7:08 p.m. UTC | #5
On Mon, Sep 02, 2024 at 02:48:41PM GMT, Pankaj Raghav (Samsung) wrote:
> On Mon, Sep 02, 2024 at 04:21:09PM +0200, Christian Brauner wrote:
> > On Mon, Sep 02, 2024 at 03:00:37PM GMT, Matthew Wilcox wrote:
> > > On Mon, Sep 02, 2024 at 02:49:32PM +0200, Pankaj Raghav (Samsung) wrote:
> > > > From: Pankaj Raghav <p.raghav@samsung.com>
> > > > 
> > > > Sven reported that a commit from bs > ps series was breaking the ksm ltp
> > > > test[1].
> > > > 
> > > > split_huge_page() takes precisely a page that is locked, and it also
> > > > expects the folio that contains that page to be locked after that
> > > > huge page has been split. The changes introduced converted the page to
> > > > folio, and passed the head page to be split, which might not be locked,
> > > > resulting in a kernel panic.
> > > > 
> > > > This commit fixes it by always passing the correct page to be split from
> > > > split_huge_page() with the appropriate minimum order for splitting.
> > > 
> > > This should be folded into the patch that is broken, not be a separate
> > > fix commit, otherwise it introduces a bisection hazard which are to be
> > > avoided when possible.
> > 
> > Patch folded into "mm: split a folio in minimum folio order chunks"
> > with the Link to this patch. Please double-check.
> Thanks a lot!
> 
> I still don't see it upstream[1]. Maybe it is yet to be pushed?
> 
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git/commit/?h=vfs.blocksize&id=fd031210c9ceb399db1dea001c6a5e98f3b4e2e7

Pushed now.
Pankaj Raghav (Samsung) Sept. 2, 2024, 7:35 p.m. UTC | #6
> > > > This should be folded into the patch that is broken, not be a separate
> > > > fix commit, otherwise it introduces a bisection hazard which are to be
> > > > avoided when possible.
> > > 
> > > Patch folded into "mm: split a folio in minimum folio order chunks"
> > > with the Link to this patch. Please double-check.
> > Thanks a lot!
> > 
> > I still don't see it upstream[1]. Maybe it is yet to be pushed?
> > 
> > [1] https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git/commit/?h=vfs.blocksize&id=fd031210c9ceb399db1dea001c6a5e98f3b4e2e7
> 
> Pushed now.

I can see it now. Thanks Christian. :)

This patch has a merge conflict in linux-next. It should be a trivial
merge but let me know if you want me to send a patch for it.
Christian Brauner Sept. 3, 2024, 7:53 a.m. UTC | #7
On Mon, Sep 02, 2024 at 07:35:20PM GMT, Pankaj Raghav (Samsung) wrote:
> > > > > This should be folded into the patch that is broken, not be a separate
> > > > > fix commit, otherwise it introduces a bisection hazard which are to be
> > > > > avoided when possible.
> > > > 
> > > > Patch folded into "mm: split a folio in minimum folio order chunks"
> > > > with the Link to this patch. Please double-check.
> > > Thanks a lot!
> > > 
> > > I still don't see it upstream[1]. Maybe it is yet to be pushed?
> > > 
> > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git/commit/?h=vfs.blocksize&id=fd031210c9ceb399db1dea001c6a5e98f3b4e2e7
> > 
> > Pushed now.
> 
> I can see it now. Thanks Christian. :)
> 
> This patch has a merge conflict in linux-next. It should be a trivial
> merge but let me know if you want me to send a patch for it.

No need, Linus will usually just sort those out.
diff mbox series

Patch

diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 7c50aeed0522..7a570e0437c9 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -319,10 +319,24 @@  unsigned long thp_get_unmapped_area_vmflags(struct file *filp, unsigned long add
 bool can_split_folio(struct folio *folio, int *pextra_pins);
 int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 		unsigned int new_order);
+int min_order_for_split(struct folio *folio);
 int split_folio_to_list(struct folio *folio, struct list_head *list);
 static inline int split_huge_page(struct page *page)
 {
-	return split_folio(page_folio(page));
+	struct folio *folio = page_folio(page);
+	int ret = min_order_for_split(folio);
+
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * split_huge_page() locks the page before splitting and
+	 * expects the same page that has been split to be locked when
+	 * returned. split_folio(page_folio(page)) cannot be used here
+	 * because it converts the page to folio and passes the head
+	 * page to be split.
+	 */
+	return split_huge_page_to_list_to_order(page, NULL, ret);
 }
 void deferred_split_folio(struct folio *folio);
 
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index c29af9451d92..9931ff1d9a9d 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -3297,12 +3297,10 @@  int split_huge_page_to_list_to_order(struct page *page, struct list_head *list,
 	return ret;
 }
 
-int split_folio_to_list(struct folio *folio, struct list_head *list)
+int min_order_for_split(struct folio *folio)
 {
-	unsigned int min_order = 0;
-
 	if (folio_test_anon(folio))
-		goto out;
+		return 0;
 
 	if (!folio->mapping) {
 		if (folio_test_pmd_mappable(folio))
@@ -3310,10 +3308,17 @@  int split_folio_to_list(struct folio *folio, struct list_head *list)
 		return -EBUSY;
 	}
 
-	min_order = mapping_min_folio_order(folio->mapping);
-out:
-	return split_huge_page_to_list_to_order(&folio->page, list,
-							min_order);
+	return mapping_min_folio_order(folio->mapping);
+}
+
+int split_folio_to_list(struct folio *folio, struct list_head *list)
+{
+	int ret = min_order_for_split(folio);
+
+	if (ret < 0)
+		return ret;
+
+	return split_huge_page_to_list_to_order(&folio->page, list, ret);
 }
 
 void __folio_undo_large_rmappable(struct folio *folio)