Re: [PATCH v3 2/9] mm/swapfile: add batched version of folio_put_swap
From: Kairui Song
Date: Tue Sep 29 2026 - 05:28:56 EST
On Thu, Sep 24, 2026 at 9:15 PM Dev Jain <dev.jain@xxxxxxx> wrote:
>
> Add folio_put_swap_pages to handle a batch of consecutive pages. Note
> that folio_put_swap already can handle a subset of this: nr_pages == 1 and
> nr_pages == folio_nr_pages(folio). Generalize this to any nr_pages.
>
> Currently we have a not-so-nice logic of passing in subpage == NULL if
> we mean to exercise the logic on the entire folio, and subpage != NULL if
> we want to exercise the logic on only that subpage. Remove this
> indirection: the caller invokes folio_put_swap_pages() if it wants to
> operate on a range of pages in the folio (i.e nr_pages may be anything
> between 1 and folio_nr_pages()), and invokes folio_put_swap() if it
> wants to operate on the entire folio.
>
> Acked-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
> Reviewed-by: Barry Song <baohua@xxxxxxxxxx>
> Signed-off-by: Dev Jain <dev.jain@xxxxxxx>
> ---
> mm/memory.c | 6 +++---
> mm/rmap.c | 4 ++--
> mm/shmem.c | 6 +++---
> mm/swap.h | 17 +++++++++++++++--
> mm/swapfile.c | 21 ++++++++++-----------
> 5 files changed, 33 insertions(+), 21 deletions(-)
>
> diff --git a/mm/memory.c b/mm/memory.c
> index 338fce99e7119..a7e979bfc814b 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -5267,7 +5267,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
> if (unlikely(folio != swapcache)) {
> folio_add_new_anon_rmap(folio, vma, address, RMAP_EXCLUSIVE);
> folio_add_lru_vma(folio, vma);
> - folio_put_swap(swapcache, NULL);
> + folio_put_swap(swapcache);
> } else if (!folio_test_anon(folio)) {
> /*
> * We currently only expect !anon folios that are fully
> @@ -5276,12 +5276,12 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
> VM_WARN_ON_ONCE_FOLIO(folio_nr_pages(folio) != nr_pages, folio);
> VM_WARN_ON_ONCE_FOLIO(folio_mapped(folio), folio);
> folio_add_new_anon_rmap(folio, vma, address, rmap_flags);
> - folio_put_swap(folio, NULL);
> + folio_put_swap(folio);
> } else {
> VM_WARN_ON_ONCE(nr_pages != 1 && nr_pages != folio_nr_pages(folio));
> folio_add_anon_rmap_ptes(folio, page, nr_pages, vma, address,
> rmap_flags);
> - folio_put_swap(folio, nr_pages == 1 ? page : NULL);
> + folio_put_swap_pages(folio, page, nr_pages);
> }
>
> VM_BUG_ON(!folio_test_anon(folio) ||
> diff --git a/mm/rmap.c b/mm/rmap.c
> index a70dbc8d544e1..fa9fc8374fc26 100644
> --- a/mm/rmap.c
> +++ b/mm/rmap.c
> @@ -2159,13 +2159,13 @@ static bool ttu_anon_swapbacked_folio(struct vm_area_struct *vma,
> * so we'll not check/care.
> */
> if (arch_unmap_one(mm, vma, address, pteval) < 0) {
> - folio_put_swap(folio, page);
> + folio_put_swap_pages(folio, page, 1);
> return false;
> }
>
> /* See folio_try_share_anon_rmap(): clear PTE first. */
> if (anon_exclusive && folio_try_share_anon_rmap_pte(folio, page)) {
> - folio_put_swap(folio, page);
> + folio_put_swap_pages(folio, page, 1);
> return false;
> }
>
> diff --git a/mm/shmem.c b/mm/shmem.c
> index d5bd9506ce500..5f46061c6320c 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -1935,7 +1935,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
> /* Swap entry might be erased by racing shmem_free_swap() */
> if (!error) {
> shmem_recalc_inode(inode, 0, -nr_pages);
> - folio_put_swap(folio, NULL);
> + folio_put_swap(folio);
> }
>
> /*
> @@ -2368,7 +2368,7 @@ static void shmem_set_folio_swapin_error(struct inode *inode, pgoff_t index,
>
> nr_pages = folio_nr_pages(folio);
> folio_wait_writeback(folio);
> - folio_put_swap(folio, NULL);
> + folio_put_swap(folio);
> swap_cache_del_folio(folio);
> /*
> * Don't treat swapin error folio as alloced. Otherwise inode->i_blocks
> @@ -2598,7 +2598,7 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
> if (sgp == SGP_WRITE)
> folio_mark_accessed(folio);
>
> - folio_put_swap(folio, NULL);
> + folio_put_swap(folio);
> swap_cache_del_folio(folio);
> folio_mark_dirty(folio);
> put_swap_device(si);
> diff --git a/mm/swap.h b/mm/swap.h
> index 47290510de264..7ae915f594034 100644
> --- a/mm/swap.h
> +++ b/mm/swap.h
> @@ -246,7 +246,8 @@ extern int swap_retry_table_alloc(swp_entry_t entry, gfp_t gfp);
> int folio_alloc_swap(struct folio *folio);
> int folio_dup_swap_pages(struct folio *folio, struct page *page,
> unsigned long nr_pages);
> -void folio_put_swap(struct folio *folio, struct page *page);
> +void folio_put_swap_pages(struct folio *folio, struct page *page,
> + unsigned long nr_pages);
>
> /* For internal use */
> extern void __swap_cluster_free_entries(struct swap_info_struct *si,
> @@ -375,7 +376,8 @@ static inline int folio_dup_swap_pages(struct folio *folio, struct page *page,
> return -EINVAL;
> }
>
> -static inline void folio_put_swap(struct folio *folio, struct page *page)
> +static inline void folio_put_swap_pages(struct folio *folio, struct page *page,
> + unsigned long nr_pages)
> {
> }
>
> @@ -478,6 +480,17 @@ static inline int folio_dup_swap(struct folio *folio)
> folio_nr_pages(folio));
> }
>
> +/**
> + * folio_put_swap() - Decrease swap count of all swap entries of a folio.
> + * @folio: folio with swap entries bound.
> + *
> + * See folio_put_swap_pages() for more information.
> + */
> +static inline void folio_put_swap(struct folio *folio)
> +{
> + folio_put_swap_pages(folio, folio_page(folio, 0), folio_nr_pages(folio));
> +}
> +
> extern const struct swap_ops swap_bdev_ops;
>
> int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio,
> diff --git a/mm/swapfile.c b/mm/swapfile.c
> index 75167b8580cc0..8b47e7f1e1931 100644
> --- a/mm/swapfile.c
> +++ b/mm/swapfile.c
> @@ -1831,27 +1831,25 @@ int folio_dup_swap_pages(struct folio *folio, struct page *page,
> }
>
> /**
> - * folio_put_swap() - Decrease swap count of swap entries of a folio.
> + * folio_put_swap_pages() - Decrease swap count of swap entries of a folio.
> * @folio: folio with swap entries bounded, must be in swap cache and locked.
> - * @page: if not NULL, only decrease the swap count of this page.
> + * @page: the first page in the folio to decrease the swap count for.
> + * @nr_pages: the number of pages in the folio to decrease the swap count for.
> *
> * This won't free the swap slots even if swap count drops to zero, they are
> * still pinned by the swap cache. User may call folio_free_swap to free them.
> * Context: Caller must ensure the folio is locked and in the swap cache.
> */
> -void folio_put_swap(struct folio *folio, struct page *page)
> +void folio_put_swap_pages(struct folio *folio, struct page *page,
> + unsigned long nr_pages)
> {
> - swp_entry_t entry = folio->swap;
> - unsigned long nr_pages = folio_nr_pages(folio);
> + swp_entry_t entry = folio_page_swap_entry(folio, page);
> struct swap_info_struct *si = __swap_entry_to_info(entry);
> + unsigned long idx = folio_page_idx(folio, page);
>
> VM_WARN_ON_FOLIO(!folio_test_locked(folio), folio);
> VM_WARN_ON_FOLIO(!folio_test_swapcache(folio), folio);
> -
> - if (page) {
> - entry = folio_page_swap_entry(folio, page);
> - nr_pages = 1;
> - }
> + VM_WARN_ON_FOLIO(idx + nr_pages > folio_nr_pages(folio), folio);
>
> swap_put_entries_cluster(si, swp_offset(entry), nr_pages, false);
> }
> @@ -2535,7 +2533,8 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
>
> setpte:
> set_pte_at(vma->vm_mm, addr, pte, new_pte);
> - folio_put_swap(swapcache, folio_file_page(swapcache, swp_offset(entry)));
> + folio_put_swap_pages(swapcache,
> + folio_file_page(swapcache, swp_offset(entry)), 1);
> out:
> if (pte)
> pte_unmap_unlock(pte, ptl);
> --
> 2.43.0
Reviewed-by: Kairui Song <kasong@xxxxxxxxxxx>