From: Youngjun Park <youngjun.park@lge.com>
To: Kairui Song <ryncsn@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
Chris Li <chrisl@kernel.org>, Kairui Song <kasong@tencent.com>,
Kemeng Shi <shikemeng@huaweicloud.com>,
Nhat Pham <nphamcs@gmail.com>, Baoquan He <baoquan.he@linux.dev>,
Barry Song <baohua@kernel.org>,
Jianyue Wu <wujianyue000@gmail.com>,
her0gyugyu@gmail.com, linux-mm@kvack.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/4] mm, swap: give hibernation swap slots their own swap table entry type
Date: Sun, 9 Aug 2026 13:02:33 +0900 [thread overview]
Message-ID: <anf72SvjPu0yt5wT@yjaykim-PowerEdge-T330> (raw)
In-Reply-To: <ancjiz6-qyC0ki4O@KASONG-MC4>
On Sat, Aug 08, 2026 at 09:25:47PM +0800, Kairui Song wrote:
> On Fri, Aug 07, 2026 at 04:06:34AM +0800, Youngjun Park wrote:
> > swap_alloc_hibernation_slot() stores a fake shadow in the slot it hands
> > out. An anon slot swapped out with no workingset shadow looks exactly the
> > same, so nothing in mm can tell the two apart.
> >
> > Give hibernation slots their own type. Bit 4 and every bit above it are
> > set, the same shape as SWP_TB_BAD. Bits 0 to 3 are taken by the shadow,
> > PFN, pointer and bad marks, so bit 4 is the first free one. Neither type
> > holds data, so the value alone says what it is.
> >
> > The entry has no swap count. Hibernation only allocates and frees a slot,
> > so a count would never change. swap_free_hibernation_slot() frees the slot
> > directly, there is no count to put first.
> >
> > The next patch needs these slots to stop looking like shadows.
> >
> > Suggested-by: Kairui Song <kasong@tencent.com>
> > Link: https://lore.kernel.org/linux-mm/abp7aDgYLrxF3Me8@KASONG-MC4/
> > Signed-off-by: Youngjun Park <youngjun.park@lge.com>
> > ---
> > mm/swap_table.h | 12 ++++++++++++
> > mm/swapfile.c | 13 +++++++------
> > 2 files changed, 19 insertions(+), 6 deletions(-)
> >
> > diff --git a/mm/swap_table.h b/mm/swap_table.h
> > index e6613e62f8d0..c1c516bcc17e 100644
> > --- a/mm/swap_table.h
> > +++ b/mm/swap_table.h
> > @@ -30,6 +30,7 @@ struct swap_memcg_table {
> > * PFN: |SWAP_COUNT|Z|------ PFN -------|10| - Cached slot
> > * Pointer: |----------- Pointer ----------|100| - (Unused)
> > * Bad: |------------- 1 -------------|1000| - Bad slot
> > + * Hibern: |------------ 1 -------------|10000| - Hibernation slot
>
> Nice!
>
> Just one idea, would it be nicer if we have:
> * Hibern: | 0 |------- 1 -------------|10000| - Hibernation slot
>
> Or:
> * Hibern: |0..001|------- 1 -------------|10000| - Hibernation slot
>
> That way if we accidentally used __swp_tb_get_count, it return a actual
> meaningful value instead of MAX. Either 0 - the slot is not used as
> a countable ordinary slot, or 1 - the slot has one user: hibernation.
>
> Maybe 0 is better at least for the intermediate commit, see below.
>
> >
> > +static inline bool swp_tb_is_hibernation(unsigned long swp_tb)
> > +{
> > + return swp_tb == SWP_TB_HIB;
> > +}
> > +
> > static inline bool swp_tb_is_countable(unsigned long swp_tb)
> > {
> > return (swp_tb_is_shadow(swp_tb) || swp_tb_is_folio(swp_tb) ||
> > diff --git a/mm/swapfile.c b/mm/swapfile.c
> > index f5dfc7e59191..a337387f7431 100644
> > --- a/mm/swapfile.c
> > +++ b/mm/swapfile.c
> > @@ -928,7 +928,7 @@ static bool __swap_cluster_alloc_entries(struct swap_info_struct *si,
> > * upon folio unmap.
> > *
> > * Else, it's a exclusive order 0 allocation for hibernation.
> > - * The slot starts with count == 1 and never increases.
> > + * The slot carries no swap count and is freed by offset.
> > */
> > if (likely(folio)) {
> > order = folio_order(folio);
> > @@ -940,8 +940,8 @@ static bool __swap_cluster_alloc_entries(struct swap_info_struct *si,
> > order = 0;
> > nr_pages = 1;
> > swap_cluster_assert_empty(ci, ci_off, 1, false);
> > - /* Fake shadow placeholder with no flag, hibernation does not use the zeromap */
> > - __swap_table_set(ci, ci_off, __swp_tb_mk_count(shadow_to_swp_tb(NULL, 0), 1));
> > + /* Exclusively owned by hibernation, must never enter the swap cache */
> > + __swap_table_set(ci, ci_off, SWP_TB_HIB);
> > } else {
> > /* Allocation without folio is only possible with hibernation */
> > WARN_ON_ONCE(1);
> > @@ -1929,9 +1929,11 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,
> > old_tb = __swap_table_get(ci, ci_off);
> > /*
> > * Freeing is done after release of the last swap count
> > - * ref, or after swap cache is dropped
> > + * ref, or after swap cache is dropped. A hibernation slot
> > + * has no count and is freed directly by its owner.
> > */
> > - VM_WARN_ON(!swp_tb_is_shadow(old_tb) || __swp_tb_get_count(old_tb) > 1);
> > + VM_WARN_ON(!swp_tb_is_hibernation(old_tb) &&
> > + (!swp_tb_is_shadow(old_tb) || __swp_tb_get_count(old_tb) > 1));
> >
> > /* Resetting the slot to NULL also clears the inline flags. */
> > __swap_table_set(ci, ci_off, null_to_swp_tb());
> > @@ -2201,7 +2203,6 @@ void swap_free_hibernation_slot(swp_entry_t entry)
> > pgoff_t offset = swp_offset(entry);
> >
> > ci = swap_cluster_lock(si, offset);
> > - __swap_cluster_put_entry(ci, offset % SWAPFILE_CLUSTER);
> > /*
> > * A slot with a folio in the swap cache is freed when the folio
> > * leaves the cache, the same rule swap_put_entries_cluster() follows.
>
> This idea is right, but is the patch in the right order? If readahead
> tried to add a folio to a hibernate slot by accident, seems nothing
> blocks that in the current patch, and that PFN slot will have a (MAX)
> count value, and considered countable? If the that folio is somehow
> reclaimed, we got a corrupted shadow (hib type is gone)?
>
> If we have the count part of a hibernation slot be 0,
> __swap_cache_add_check will fail natively, seems there will be no
> such risk. A few existing helpers can also help catch potential
> wrong freeing of hibernation slot. (underflow check).
>
> The layout can be changed again afterwards.
>
> How do you think?
Yeah whole thing you addressed make sense.
I will follow your guide & review and send the patch soon :)
Youngjun
next prev parent reply other threads:[~2026-08-09 4:02 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 19:06 [PATCH 0/4] mm, swap: keep hibernation swap slots out of the swap cache Youngjun Park
2026-08-06 19:06 ` [PATCH 1/4] mm, swap: don't free a hibernation slot that is in " Youngjun Park
2026-08-08 12:09 ` Kairui Song
2026-08-08 13:26 ` Kairui Song
2026-08-09 3:59 ` Youngjun Park
2026-08-06 19:06 ` [PATCH 2/4] mm, swap: give hibernation swap slots their own swap table entry type Youngjun Park
2026-08-08 13:25 ` Kairui Song
2026-08-09 4:02 ` Youngjun Park [this message]
2026-08-06 19:06 ` [PATCH 3/4] mm, swap: only allow swapped-out slots into the swap cache Youngjun Park
2026-08-06 19:06 ` [PATCH 4/4] mm, swap: drop the swap cache guard and reclaim in swap_free_hibernation_slot() Youngjun Park
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=anf72SvjPu0yt5wT@yjaykim-PowerEdge-T330 \
--to=youngjun.park@lge.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baoquan.he@linux.dev \
--cc=chrisl@kernel.org \
--cc=her0gyugyu@gmail.com \
--cc=kasong@tencent.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=nphamcs@gmail.com \
--cc=ryncsn@gmail.com \
--cc=shikemeng@huaweicloud.com \
--cc=wujianyue000@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox