From: Johannes Weiner <hannes@cmpxchg.org>
To: Nhat Pham <nphamcs@gmail.com>
Cc: akpm@linux-foundation.org, chrisl@kernel.org, kasong@tencent.com,
mhocko@kernel.org, roman.gushchin@linux.dev,
shakeel.butt@linux.dev, yosry@kernel.org, david@kernel.org,
muchun.song@linux.dev, shikemeng@huaweicloud.com,
baoquan.he@linux.dev, baohua@kernel.org, youngjun.park@lge.com,
chengming.zhou@linux.dev, ljs@kernel.org, liam@infradead.org,
vbabka@kernel.org, rppt@kernel.org, surenb@google.com,
qi.zheng@linux.dev, axelrasmussen@google.com, yuanchu@google.com,
weixugc@google.com, riel@surriel.com, gourry@gourry.net,
haowenchao22@gmail.com, corbet@lwn.net, kernel-team@meta.com,
linux-mm@kvack.org, linux-kernel@vger.kernel.org,
linux-doc@vger.kernel.org, cgroups@vger.kernel.org
Subject: Re: [PATCH v3 08/11] mm, swap: only charge physical swap entries
Date: Fri, 7 Aug 2026 12:31:42 -0400 [thread overview]
Message-ID: <anYIboHEUZb4fhHv@cmpxchg.org> (raw)
In-Reply-To: <20260806184254.3790858-9-nphamcs@gmail.com>
On Thu, Aug 06, 2026 at 11:42:51AM -0700, Nhat Pham wrote:
> Charge memcg->swap when a vswap entry acquires physical backing rather
> than when it is allocated, so memory.swap.current tracks on-disk swap
> usage. Zswap-backed and zero-filled pages occupy no swap space but were
> charged as though they did.
>
> memory.swap.current therefore no longer counts them, and a cgroup whose
> pages all land in zswap can now reclaim anon memory with memory.swap.max
> set to 0.
>
> Direct-mapped physical swap charging is unchanged.
>
> Signed-off-by: Nhat Pham <nphamcs@gmail.com>
To head off any uncertainty about this: this is exactly what needs to
happen in terms of cgroup semantics.
memory.swap.* are about physical swap space. They track, control, and
enforce fairness for a finite resource that is separate from memory.
When a user switches on vswsap and a bunch of empty pages are stored
inside the zeromap without consuming swapfile space, these counters
must be 0.
When a user switches on vswap to use zswap without a backing file,
these counters must be 0.
When a user switches on vswap to use zswap with writeback, only the
pages that get written to the swapfile must be tracked and controlled
by these counters.
A few inline comments on the implementation:
> @@ -5701,6 +5702,116 @@ int __mem_cgroup_try_charge_swap(struct folio *folio)
> return 0;
> }
>
> +/**
> + * __mem_cgroup_record_swap - record memcg for swap without charging
> + * @folio: folio being added to swap
> + *
> + * Pin the memcg private ID ref and record it in the swap cgroup table
> + * without charging memcg->swap; the charge is deferred to physical-backing
> + * allocation (vswap).
> + */
> +void __mem_cgroup_record_swap(struct folio *folio)
> +{
> + unsigned int nr_pages = folio_nr_pages(folio);
> + struct swap_cluster_info *ci;
> + struct mem_cgroup *memcg;
> + struct obj_cgroup *objcg;
> +
> + if (do_memsw_account())
> + return;
> +
> + objcg = folio_objcg(folio);
> + VM_WARN_ON_ONCE_FOLIO(!objcg, folio);
> + if (!objcg)
> + return;
> +
> + rcu_read_lock();
> + memcg = obj_cgroup_memcg(objcg);
> + if (!folio_test_swapcache(folio)) {
> + rcu_read_unlock();
> + return;
> + }
> +
> + memcg = mem_cgroup_private_id_get_online(memcg, nr_pages);
> + rcu_read_unlock();
> +
> + ci = swap_cluster_get_and_lock(folio);
> + __swap_cgroup_set(ci, swp_cluster_offset(folio->swap), nr_pages,
> + mem_cgroup_private_id(memcg));
> + swap_cluster_unlock(ci);
> +}
> +
> +/**
> + * __mem_cgroup_charge_backing_phys_swap - charge memcg->swap
> + * @memcg: the mem_cgroup to charge (may be NULL)
> + * @nr_pages: number of physical swap pages to charge
> + *
> + * Charge the swap counter when a vswap entry gains physical backing. The
> + * private ID ref is already held (pinned by __mem_cgroup_record_swap() at
> + * vswap allocation), so this only moves the counter.
> + *
> + * Return: 0 on success, -ENOMEM on failure.
> + */
> +int __mem_cgroup_charge_backing_phys_swap(struct mem_cgroup *memcg,
> + unsigned int nr_pages)
> +{
> + struct page_counter *counter;
> +
> + if (do_memsw_account())
> + return 0;
> + if (!memcg)
> + return 0;
> +
> + if (!mem_cgroup_is_root(memcg) &&
> + !page_counter_try_charge(&memcg->swap, nr_pages, &counter)) {
> + memcg_memory_event(memcg, MEMCG_SWAP_MAX);
> + memcg_memory_event(memcg, MEMCG_SWAP_FAIL);
> + return -ENOMEM;
> + }
> + mod_memcg_state(memcg, MEMCG_SWAP, nr_pages);
> + return 0;
> +}
These functions are just __mem_cgroup_try_charge_swap() in two acts :-)
Please refactor this properly:
__mem_cgroup_swap_record()
__mem_cgroup_swap_charge()
> + * __mem_cgroup_uncharge_backing_phys_swap - uncharge memcg->swap counter
> + * @memcg: the mem_cgroup to uncharge (may be NULL)
> + * @nr_pages: number of physical swap pages to uncharge
> + *
> + * Uncharge the swap counter on physical backing release for a vswap entry.
> + * The private ID ref is dropped separately via __mem_cgroup_id_put_swap() when
> + * the vswap entry is freed.
> + */
> +void __mem_cgroup_uncharge_backing_phys_swap(struct mem_cgroup *memcg,
> + unsigned int nr_pages)
Same on the uncharge side...
__mem_cgroup_swap_uncharge()
> +{
> + if (!memcg)
> + return;
> +
> + if (!mem_cgroup_is_root(memcg)) {
> + if (do_memsw_account())
> + page_counter_uncharge(&memcg->memsw, nr_pages);
> + else
> + page_counter_uncharge(&memcg->swap, nr_pages);
> + }
> + mod_memcg_state(memcg, MEMCG_SWAP, -nr_pages);
> +}
> +
> +/**
> + * __mem_cgroup_id_put_swap - drop memcg private ID ref without uncharging
> + * @id: cgroup private id
> + * @nr_pages: number of refs to drop
> + */
> +void __mem_cgroup_id_put_swap(unsigned short id, unsigned int nr_pages)
> +{
> + struct mem_cgroup *memcg;
> +
> + rcu_read_lock();
> + memcg = mem_cgroup_from_private_id(id);
> + if (memcg)
> + mem_cgroup_private_id_put(memcg, nr_pages);
> + rcu_read_unlock();
> +}
__mem_cgroup_swap_put()
and then remove __mem_cgroup_uncharge_swap(). Handle this split the
same way as on the charge path.
> @@ -2116,8 +2117,16 @@ int folio_alloc_swap(struct folio *folio)
> goto again;
> }
>
> - /* Need to call this even if allocation failed, for MEMCG_SWAP_FAIL. */
> - if (unlikely(mem_cgroup_try_charge_swap(folio)))
> + /*
> + * A vswap entry has no physical swap yet, so only record the memcg;
> + * folio_realloc_swap() charges once backing is allocated.
> + *
> + * Need to call this even if allocation failed, for MEMCG_SWAP_FAIL.
> + */
> + if (folio_test_swapcache(folio) &&
> + is_vswap_entry(folio->swap))
> + mem_cgroup_record_swap(folio);
> + else if (unlikely(mem_cgroup_try_charge_swap(folio)))
> swap_cache_del_folio(folio);
This becomes:
if (!vswap && mem_cgroup_swap_try_charge())
abort
mem_cgroup_swap_record()
> @@ -2614,18 +2685,28 @@ void __swap_cluster_free_entries(struct swap_info_struct *si,
> /*
> * Uncharge swap slots by memcg in batches. Consecutive
> * slots with the same cgroup id are uncharged together.
> + * For vswap, only drop the ID ref - physical swap was
> + * already uncharged in __vswap_release_backing above.
> */
> id_cur = __swap_cgroup_clear(ci, ci_off, 1);
> if (batch_id != id_cur) {
> - if (batch_id)
> - mem_cgroup_uncharge_swap(batch_id, ci_off - batch_off);
> + if (batch_id) {
> + if (is_vswap)
> + mem_cgroup_id_put_swap(batch_id, ci_off - batch_off);
> + else
> + mem_cgroup_uncharge_swap(batch_id, ci_off - batch_off);
> + }
And this becomes:
if (!vswap)
mem_cgroup_swap_uncharge()
mem_cgroup_swap_put()
next prev parent reply other threads:[~2026-08-07 16:31 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-06 18:42 [PATCH v3 00/11] Virtual Swap Space (Swap Table Edition) Nhat Pham
2026-08-06 18:42 ` [PATCH v3 01/11] mm, swap: add virtual swap device infrastructure Nhat Pham
2026-08-07 15:49 ` Johannes Weiner
2026-08-06 18:42 ` [PATCH v3 02/11] mm, swap: support zswap and zeroswap as vswap backends Nhat Pham
2026-08-06 18:42 ` [PATCH v3 03/11] mm, swap: prepare the swap IO path for vswap Nhat Pham
2026-08-06 18:42 ` [PATCH v3 04/11] mm, swap: support physical swap as a vswap backend Nhat Pham
2026-08-06 18:42 ` [PATCH v3 05/11] mm, swap: enable THP swapin for vswap entries Nhat Pham
2026-08-06 18:42 ` [PATCH v3 06/11] mm, swap: write back vswap zswap entries to physical swap Nhat Pham
2026-08-06 18:42 ` [PATCH v3 07/11] mm, swap: reclaim physical slots backing cache-only vswap entries Nhat Pham
2026-08-06 18:42 ` [PATCH v3 08/11] mm, swap: only charge physical swap entries Nhat Pham
2026-08-07 16:31 ` Johannes Weiner [this message]
2026-08-06 18:42 ` [PATCH v3 09/11] mm, swap: add debugfs counters for vswap Nhat Pham
2026-08-06 18:42 ` [PATCH v3 10/11] mm, swap: defer memcg_table allocation for physical swap clusters Nhat Pham
2026-08-06 18:42 ` [PATCH v3 11/11] mm, swap: widen swap_info_struct max/pages to unsigned long Nhat Pham
2026-08-07 5:26 ` [syzbot ci] Re: Virtual Swap Space (Swap Table Edition) syzbot ci
2026-08-07 7:21 ` Chris Li
2026-08-07 9:07 ` [PATCH v3 00/11] " Chris Li
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=anYIboHEUZb4fhHv@cmpxchg.org \
--to=hannes@cmpxchg.org \
--cc=akpm@linux-foundation.org \
--cc=axelrasmussen@google.com \
--cc=baohua@kernel.org \
--cc=baoquan.he@linux.dev \
--cc=cgroups@vger.kernel.org \
--cc=chengming.zhou@linux.dev \
--cc=chrisl@kernel.org \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=gourry@gourry.net \
--cc=haowenchao22@gmail.com \
--cc=kasong@tencent.com \
--cc=kernel-team@meta.com \
--cc=liam@infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=nphamcs@gmail.com \
--cc=qi.zheng@linux.dev \
--cc=riel@surriel.com \
--cc=roman.gushchin@linux.dev \
--cc=rppt@kernel.org \
--cc=shakeel.butt@linux.dev \
--cc=shikemeng@huaweicloud.com \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=weixugc@google.com \
--cc=yosry@kernel.org \
--cc=youngjun.park@lge.com \
--cc=yuanchu@google.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