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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.