From: Vlastimil Babka <vbabka@suse.cz>
To: Shakeel Butt <shakeel.butt@linux.dev>,
Andrew Morton <akpm@linux-foundation.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>,
Michal Hocko <mhocko@kernel.org>,
Roman Gushchin <roman.gushchin@linux.dev>,
Muchun Song <muchun.song@linux.dev>,
Sebastian Andrzej Siewior <bigeasy@linutronix.de>,
linux-mm@kvack.org, cgroups@vger.kernel.org,
linux-kernel@vger.kernel.org,
Meta kernel team <kernel-team@meta.com>
Subject: Re: [RFC PATCH 10/10] memcg: no more irq disabling for stock locks
Date: Fri, 14 Mar 2025 11:54:34 +0100 [thread overview]
Message-ID: <9e1e3877-55ae-4546-a5c1-08ea730ea638@suse.cz> (raw)
In-Reply-To: <20250314061511.1308152-11-shakeel.butt@linux.dev>
On 3/14/25 07:15, Shakeel Butt wrote:
> Let's switch all memcg_stock locks acquire and release places to not
> disable and enable irqs. There are two still functions (i.e.
> mod_objcg_state() and drain_obj_stock) which needs to disable irqs to
> update the stats on non-RT kernels. For now add a simple wrapper for
> that functionality.
BTW, which part of __mod_objcg_mlstate() really needs disabled irqs and not
just preemption? I see it does rcu_read_lock() anyway, which disables
preemption. Then in __mod_memcg_lruvec_state() we do some __this_cpu_add()
updates. I think these also are fine with just disabled preemption as they
are atomic vs irqs (but don't need LOCK prefix to be atomic vs other cpus
updates).
Is it just memcg_rstat_updated() which does READ_ONCE/WRITE_ONCE? Could we
perhaps just change it to operations where disabled preemption is enough?
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> ---
> mm/memcontrol.c | 83 ++++++++++++++++++++++++++++++++-----------------
> 1 file changed, 54 insertions(+), 29 deletions(-)
>
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index ba5d004049d3..fa28efa298f4 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -1796,22 +1796,17 @@ static bool obj_stock_flush_required(struct memcg_stock_pcp *stock,
> *
> * returns true if successful, false otherwise.
> */
> -static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages,
> - gfp_t gfp_mask)
> +static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
> {
> struct memcg_stock_pcp *stock;
> unsigned int stock_pages;
> - unsigned long flags;
> bool ret = false;
>
> if (nr_pages > MEMCG_CHARGE_BATCH)
> return ret;
>
> - if (!localtry_trylock_irqsave(&memcg_stock.stock_lock, flags)) {
> - if (!gfpflags_allow_spinning(gfp_mask))
> + if (!localtry_trylock(&memcg_stock.stock_lock))
> return ret;
> - localtry_lock_irqsave(&memcg_stock.stock_lock, flags);
> - }
>
> stock = this_cpu_ptr(&memcg_stock);
> stock_pages = READ_ONCE(stock->nr_pages);
> @@ -1820,7 +1815,7 @@ static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages,
> ret = true;
> }
>
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> + localtry_unlock(&memcg_stock.stock_lock);
>
> return ret;
> }
> @@ -1855,7 +1850,6 @@ static void drain_stock(struct memcg_stock_pcp *stock)
> static void drain_local_stock(struct work_struct *dummy)
> {
> struct memcg_stock_pcp *stock;
> - unsigned long flags;
>
> lockdep_assert_once(in_task());
>
> @@ -1864,14 +1858,14 @@ static void drain_local_stock(struct work_struct *dummy)
> * drain_stock races is that we always operate on local CPU stock
> * here with IRQ disabled
> */
> - localtry_lock_irqsave(&memcg_stock.stock_lock, flags);
> + localtry_lock(&memcg_stock.stock_lock);
>
> stock = this_cpu_ptr(&memcg_stock);
> drain_obj_stock(stock);
> drain_stock(stock);
> clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
>
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> + localtry_unlock(&memcg_stock.stock_lock);
> }
>
> /* Should never be called with root_mem_cgroup. */
> @@ -1879,9 +1873,8 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
> {
> struct memcg_stock_pcp *stock;
> unsigned int stock_pages;
> - unsigned long flags;
>
> - if (!localtry_trylock_irqsave(&memcg_stock.stock_lock, flags)) {
> + if (!localtry_trylock(&memcg_stock.stock_lock)) {
> /*
> * In case of unlikely failure to lock percpu stock_lock
> * uncharge memcg directly.
> @@ -1902,7 +1895,7 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
> if (stock_pages > MEMCG_CHARGE_BATCH)
> drain_stock(stock);
>
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> + localtry_unlock(&memcg_stock.stock_lock);
> }
>
> /*
> @@ -1953,17 +1946,12 @@ void drain_all_stock(struct mem_cgroup *root_memcg)
> static int memcg_hotplug_cpu_dead(unsigned int cpu)
> {
> struct memcg_stock_pcp *stock;
> - unsigned long flags;
>
> lockdep_assert_once(in_task());
>
> stock = &per_cpu(memcg_stock, cpu);
>
> - /* drain_obj_stock requires stock_lock */
> - localtry_lock_irqsave(&memcg_stock.stock_lock, flags);
> drain_obj_stock(stock);
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> -
> drain_stock(stock);
>
> return 0;
> @@ -2254,7 +2242,7 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
> unsigned long pflags;
>
> retry:
> - if (consume_stock(memcg, nr_pages, gfp_mask))
> + if (consume_stock(memcg, nr_pages))
> return 0;
>
> if (!gfpflags_allow_spinning(gfp_mask))
> @@ -2757,6 +2745,28 @@ static void replace_stock_objcg(struct memcg_stock_pcp *stock,
> WRITE_ONCE(stock->cached_objcg, objcg);
> }
>
> +static unsigned long rt_lock(void)
> +{
> +#ifdef CONFIG_PREEMPT_RT
> + migrate_disable();
> + return 0;
> +#else
> + unsigned long flags = 0;
> +
> + local_irq_save(flags);
> + return flags;
> +#endif
> +}
> +
> +static void rt_unlock(unsigned long flags)
> +{
> +#ifdef CONFIG_PREEMPT_RT
> + migrate_enable();
> +#else
> + local_irq_restore(flags);
> +#endif
> +}
> +
> static void mod_objcg_state(struct obj_cgroup *objcg, struct pglist_data *pgdat,
> enum node_stat_item idx, int nr)
> {
> @@ -2764,7 +2774,8 @@ static void mod_objcg_state(struct obj_cgroup *objcg, struct pglist_data *pgdat,
> unsigned long flags;
> int *bytes;
>
> - if (!localtry_trylock_irqsave(&memcg_stock.stock_lock, flags)) {
> + if (!localtry_trylock(&memcg_stock.stock_lock)) {
> + /* Do we need mix_rt_[un]lock here too. */
> __mod_objcg_mlstate(objcg, pgdat, idx, nr);
> return;
> }
> @@ -2783,6 +2794,8 @@ static void mod_objcg_state(struct obj_cgroup *objcg, struct pglist_data *pgdat,
> /* Flush the existing cached vmstat data */
> struct pglist_data *oldpg = stock->cached_pgdat;
>
> + flags = rt_lock();
> +
> if (stock->nr_slab_reclaimable_b) {
> __mod_objcg_mlstate(objcg, oldpg, NR_SLAB_RECLAIMABLE_B,
> stock->nr_slab_reclaimable_b);
> @@ -2793,6 +2806,8 @@ static void mod_objcg_state(struct obj_cgroup *objcg, struct pglist_data *pgdat,
> stock->nr_slab_unreclaimable_b);
> stock->nr_slab_unreclaimable_b = 0;
> }
> +
> + rt_unlock(flags);
> stock->cached_pgdat = pgdat;
> }
>
> @@ -2814,19 +2829,21 @@ static void mod_objcg_state(struct obj_cgroup *objcg, struct pglist_data *pgdat,
> nr = 0;
> }
> }
> - if (nr)
> + if (nr) {
> + flags = rt_lock();
> __mod_objcg_mlstate(objcg, pgdat, idx, nr);
> + rt_unlock(flags);
> + }
>
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> + localtry_unlock(&memcg_stock.stock_lock);
> }
>
> static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes)
> {
> struct memcg_stock_pcp *stock;
> - unsigned long flags;
> bool ret = false;
>
> - if (!localtry_trylock_irqsave(&memcg_stock.stock_lock, flags))
> + if (!localtry_trylock(&memcg_stock.stock_lock))
> return ret;
>
> stock = this_cpu_ptr(&memcg_stock);
> @@ -2835,7 +2852,7 @@ static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes)
> ret = true;
> }
>
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> + localtry_unlock(&memcg_stock.stock_lock);
>
> return ret;
> }
> @@ -2843,10 +2860,16 @@ static bool consume_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes)
> static void drain_obj_stock(struct memcg_stock_pcp *stock)
> {
> struct obj_cgroup *old = READ_ONCE(stock->cached_objcg);
> + unsigned long flags;
> + bool locked = stock->nr_bytes || stock->nr_slab_reclaimable_b ||
> + stock->nr_slab_unreclaimable_b;
>
> if (!old)
> return;
>
> + if (locked)
> + flags = rt_lock();
> +
> if (stock->nr_bytes) {
> unsigned int nr_pages = stock->nr_bytes >> PAGE_SHIFT;
> unsigned int nr_bytes = stock->nr_bytes & (PAGE_SIZE - 1);
> @@ -2897,6 +2920,9 @@ static void drain_obj_stock(struct memcg_stock_pcp *stock)
> stock->cached_pgdat = NULL;
> }
>
> + if (locked)
> + rt_unlock(flags);
> +
> WRITE_ONCE(stock->cached_objcg, NULL);
> obj_cgroup_put(old);
> }
> @@ -2920,10 +2946,9 @@ static void refill_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes,
> bool allow_uncharge)
> {
> struct memcg_stock_pcp *stock;
> - unsigned long flags;
> unsigned int nr_pages = 0;
>
> - if (!localtry_trylock_irqsave(&memcg_stock.stock_lock, flags)) {
> + if (!localtry_trylock(&memcg_stock.stock_lock)) {
> atomic_add(nr_bytes, &objcg->nr_charged_bytes);
> return;
> }
> @@ -2940,7 +2965,7 @@ static void refill_obj_stock(struct obj_cgroup *objcg, unsigned int nr_bytes,
> stock->nr_bytes &= (PAGE_SIZE - 1);
> }
>
> - localtry_unlock_irqrestore(&memcg_stock.stock_lock, flags);
> + localtry_unlock(&memcg_stock.stock_lock);
>
> if (nr_pages)
> obj_cgroup_uncharge_pages(objcg, nr_pages);
next prev parent reply other threads:[~2025-03-14 10:54 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-14 6:15 [RFC PATCH 00/10] memcg: stock code cleanups Shakeel Butt
2025-03-14 6:15 ` [RFC PATCH 01/10] memcg: remove root memcg check from refill_stock Shakeel Butt
2025-03-14 6:15 ` [RFC PATCH 02/10] memcg: decouple drain_obj_stock from local stock Shakeel Butt
2025-03-14 9:57 ` Vlastimil Babka
2025-03-14 6:15 ` [RFC PATCH 03/10] memcg: introduce memcg_uncharge Shakeel Butt
2025-03-14 10:01 ` Vlastimil Babka
2025-03-14 6:15 ` [RFC PATCH 04/10] memcg: manually inline __refill_stock Shakeel Butt
2025-03-14 10:05 ` Vlastimil Babka
2025-03-14 6:15 ` [RFC PATCH 05/10] memcg: no refilling stock from obj_cgroup_release Shakeel Butt
2025-03-14 10:09 ` Vlastimil Babka
2025-03-14 11:26 ` Sebastian Andrzej Siewior
2025-03-14 15:25 ` Shakeel Butt
2025-03-14 6:15 ` [RFC PATCH 06/10] memcg: do obj_cgroup_put inside drain_obj_stock Shakeel Butt
2025-03-14 10:17 ` Vlastimil Babka
2025-03-14 11:35 ` Sebastian Andrzej Siewior
2025-03-14 15:29 ` Shakeel Butt
2025-03-14 6:15 ` [RFC PATCH 07/10] memcg: use __mod_memcg_state in drain_obj_stock Shakeel Butt
2025-03-14 10:27 ` Vlastimil Babka
2025-03-14 11:44 ` Sebastian Andrzej Siewior
2025-03-14 11:38 ` Sebastian Andrzej Siewior
2025-03-14 6:15 ` [RFC PATCH 08/10] memcg: assert in_task for couple of local_lock holders Shakeel Butt
2025-03-14 6:15 ` [RFC PATCH 09/10] memcg: trylock stock for objcg Shakeel Butt
2025-03-14 11:47 ` Sebastian Andrzej Siewior
2025-03-14 15:33 ` Shakeel Butt
2025-03-14 6:15 ` [RFC PATCH 10/10] memcg: no more irq disabling for stock locks Shakeel Butt
2025-03-14 10:54 ` Vlastimil Babka [this message]
2025-03-14 11:58 ` Sebastian Andrzej Siewior
2025-03-14 15:55 ` Shakeel Butt
2025-03-14 16:42 ` Sebastian Andrzej Siewior
2025-03-14 17:02 ` Shakeel Butt
2025-03-14 17:38 ` Shakeel Butt
2025-03-14 18:19 ` Sebastian Andrzej Siewior
2025-03-14 13:33 ` [RFC PATCH 00/10] memcg: stock code cleanups Vlastimil Babka
2025-03-14 16:03 ` Shakeel Butt
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=9e1e3877-55ae-4546-a5c1-08ea730ea638@suse.cz \
--to=vbabka@suse.cz \
--cc=akpm@linux-foundation.org \
--cc=bigeasy@linutronix.de \
--cc=cgroups@vger.kernel.org \
--cc=hannes@cmpxchg.org \
--cc=kernel-team@meta.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhocko@kernel.org \
--cc=muchun.song@linux.dev \
--cc=roman.gushchin@linux.dev \
--cc=shakeel.butt@linux.dev \
/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