diff mbox series

[7/9] memcg: use __mod_memcg_state in drain_obj_stock

Message ID 20250315174930.1769599-8-shakeel.butt@linux.dev (mailing list archive)
State New
Headers show
Series memcg: cleanup per-cpu stock | expand

Commit Message

Shakeel Butt March 15, 2025, 5:49 p.m. UTC
For non-PREEMPT_RT kernels, drain_obj_stock() is always called with irq
disabled, so we can use __mod_memcg_state() instead of
mod_memcg_state(). For PREEMPT_RT, we need to add memcg_stats_[un]lock
in __mod_memcg_state().

Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 mm/memcontrol.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

Comments

Vlastimil Babka March 17, 2025, 8:56 p.m. UTC | #1
On 3/15/25 18:49, Shakeel Butt wrote:
> For non-PREEMPT_RT kernels, drain_obj_stock() is always called with irq
> disabled, so we can use __mod_memcg_state() instead of
> mod_memcg_state(). For PREEMPT_RT, we need to add memcg_stats_[un]lock
> in __mod_memcg_state().
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

I've asked in the RFC and from Sebastian's answer I think my question was
misunderstod, so let me try again.

After this patch we'll have from mod_memcg_state():

mod_memcg_state()
  local_irq_save(flags);
  __mod_memcg_state()
    memcg_stats_lock(); <- new and unnecessary?

Instead of modifying __mod_memcg_state() we could be more targetted and just
do in drain_obj_stock():

memcg_stats_lock();
__mod_memcg_state();
memcg_stats_unlock();

Am I missing something?

> ---
>  mm/memcontrol.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 3c4de384b5a0..dfe9c2eb7816 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -707,10 +707,12 @@ void __mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx,
>  	if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx))
>  		return;
>  
> +	memcg_stats_lock();
>  	__this_cpu_add(memcg->vmstats_percpu->state[i], val);
>  	val = memcg_state_val_in_pages(idx, val);
>  	memcg_rstat_updated(memcg, val);
>  	trace_mod_memcg_state(memcg, idx, val);
> +	memcg_stats_unlock();
>  }
>  
>  #ifdef CONFIG_MEMCG_V1
> @@ -2845,7 +2847,7 @@ static void drain_obj_stock(struct memcg_stock_pcp *stock)
>  
>  			memcg = get_mem_cgroup_from_objcg(old);
>  
> -			mod_memcg_state(memcg, MEMCG_KMEM, -nr_pages);
> +			__mod_memcg_state(memcg, MEMCG_KMEM, -nr_pages);
>  			memcg1_account_kmem(memcg, -nr_pages);
>  			if (!mem_cgroup_is_root(memcg))
>  				memcg_uncharge(memcg, nr_pages);
Shakeel Butt March 17, 2025, 9:54 p.m. UTC | #2
On Mon, Mar 17, 2025 at 09:56:39PM +0100, Vlastimil Babka wrote:
> On 3/15/25 18:49, Shakeel Butt wrote:
> > For non-PREEMPT_RT kernels, drain_obj_stock() is always called with irq
> > disabled, so we can use __mod_memcg_state() instead of
> > mod_memcg_state(). For PREEMPT_RT, we need to add memcg_stats_[un]lock
> > in __mod_memcg_state().
> > 
> > Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> > Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> 
> I've asked in the RFC and from Sebastian's answer I think my question was
> misunderstod, so let me try again.
> 
> After this patch we'll have from mod_memcg_state():
> 
> mod_memcg_state()
>   local_irq_save(flags);
>   __mod_memcg_state()
>     memcg_stats_lock(); <- new and unnecessary?
> 
> Instead of modifying __mod_memcg_state() we could be more targetted and just
> do in drain_obj_stock():
> 
> memcg_stats_lock();
> __mod_memcg_state();
> memcg_stats_unlock();
> 
> Am I missing something?

This seems unnecessary because this patch is adding the first user of
__mod_memcg_state() but I think maintainability is better with
memcg_stats_[un]lock() inside __mod_memcg_state().

Let's take the example of __mod_memcg_lruvec_state(). It is being
called from places where non-RT kernel, the irqs are disabled through
spin_lock_irq*, so on RT kernel, the irq would not be disabled and
thus explicitly need preemption disabled. What if in future
__mod_memcg_state() is being used by a caller which assumes preemption
is disabled through irq disable. The RT kernel would be buggy there.

I am not sure if it is easy to force the future users to explicitly add
memcg_stats_[un]lock() across the call to __mod_memcg_state().
Roman Gushchin March 18, 2025, 1:13 a.m. UTC | #3
On Sat, Mar 15, 2025 at 10:49:28AM -0700, Shakeel Butt wrote:
> For non-PREEMPT_RT kernels, drain_obj_stock() is always called with irq
> disabled, so we can use __mod_memcg_state() instead of
> mod_memcg_state(). For PREEMPT_RT, we need to add memcg_stats_[un]lock
> in __mod_memcg_state().
> 
> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
>  mm/memcontrol.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 3c4de384b5a0..dfe9c2eb7816 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -707,10 +707,12 @@ void __mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx,
>  	if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx))
>  		return;
>  
> +	memcg_stats_lock();
>  	__this_cpu_add(memcg->vmstats_percpu->state[i], val);
>  	val = memcg_state_val_in_pages(idx, val);
>  	memcg_rstat_updated(memcg, val);
>  	trace_mod_memcg_state(memcg, idx, val);
> +	memcg_stats_unlock();
>  }
>  
>  #ifdef CONFIG_MEMCG_V1
> @@ -2845,7 +2847,7 @@ static void drain_obj_stock(struct memcg_stock_pcp *stock)

VM_WARN_ON_IRQS_ENABLED() ?

Reviewed-by: Roman Gushchin <roman.gushchin@linux.dev>
Vlastimil Babka March 18, 2025, 7:50 a.m. UTC | #4
On 3/18/25 02:13, Roman Gushchin wrote:
> On Sat, Mar 15, 2025 at 10:49:28AM -0700, Shakeel Butt wrote:
>> For non-PREEMPT_RT kernels, drain_obj_stock() is always called with irq
>> disabled, so we can use __mod_memcg_state() instead of
>> mod_memcg_state(). For PREEMPT_RT, we need to add memcg_stats_[un]lock
>> in __mod_memcg_state().
>> 
>> Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
>> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>> ---
>>  mm/memcontrol.c | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>> 
>> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
>> index 3c4de384b5a0..dfe9c2eb7816 100644
>> --- a/mm/memcontrol.c
>> +++ b/mm/memcontrol.c
>> @@ -707,10 +707,12 @@ void __mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx,
>>  	if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx))
>>  		return;
>>  
>> +	memcg_stats_lock();
>>  	__this_cpu_add(memcg->vmstats_percpu->state[i], val);
>>  	val = memcg_state_val_in_pages(idx, val);
>>  	memcg_rstat_updated(memcg, val);
>>  	trace_mod_memcg_state(memcg, idx, val);
>> +	memcg_stats_unlock();
>>  }
>>  
>>  #ifdef CONFIG_MEMCG_V1
>> @@ -2845,7 +2847,7 @@ static void drain_obj_stock(struct memcg_stock_pcp *stock)
> 
> VM_WARN_ON_IRQS_ENABLED() ?

It's part of memcg_stats_lock()

> Reviewed-by: Roman Gushchin <roman.gushchin@linux.dev>
Vlastimil Babka March 18, 2025, 8:02 a.m. UTC | #5
On 3/17/25 22:54, Shakeel Butt wrote:
> On Mon, Mar 17, 2025 at 09:56:39PM +0100, Vlastimil Babka wrote:
>> On 3/15/25 18:49, Shakeel Butt wrote:
>> > For non-PREEMPT_RT kernels, drain_obj_stock() is always called with irq
>> > disabled, so we can use __mod_memcg_state() instead of
>> > mod_memcg_state(). For PREEMPT_RT, we need to add memcg_stats_[un]lock
>> > in __mod_memcg_state().
>> > 
>> > Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
>> > Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>> 
>> I've asked in the RFC and from Sebastian's answer I think my question was
>> misunderstod, so let me try again.
>> 
>> After this patch we'll have from mod_memcg_state():
>> 
>> mod_memcg_state()
>>   local_irq_save(flags);
>>   __mod_memcg_state()
>>     memcg_stats_lock(); <- new and unnecessary?
>> 
>> Instead of modifying __mod_memcg_state() we could be more targetted and just
>> do in drain_obj_stock():
>> 
>> memcg_stats_lock();
>> __mod_memcg_state();
>> memcg_stats_unlock();
>> 
>> Am I missing something?
> 
> This seems unnecessary because this patch is adding the first user of
> __mod_memcg_state()

You mean first other user than mod_memcg_state() itself.

> but I think maintainability is better with
> memcg_stats_[un]lock() inside __mod_memcg_state().
> 
> Let's take the example of __mod_memcg_lruvec_state(). It is being
> called from places where non-RT kernel, the irqs are disabled through
> spin_lock_irq*, so on RT kernel, the irq would not be disabled and
> thus explicitly need preemption disabled. What if in future
> __mod_memcg_state() is being used by a caller which assumes preemption
> is disabled through irq disable. The RT kernel would be buggy there.
> 
> I am not sure if it is easy to force the future users to explicitly add
> memcg_stats_[un]lock() across the call to __mod_memcg_state().

I see the point. Well least memcg_stats_lock() isn't expensive, and it's a
no-op on non-debug !RT anyway.

Acked-by: Vlastimil Babka <vbabka@suse.cz>
diff mbox series

Patch

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 3c4de384b5a0..dfe9c2eb7816 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -707,10 +707,12 @@  void __mod_memcg_state(struct mem_cgroup *memcg, enum memcg_stat_item idx,
 	if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx))
 		return;
 
+	memcg_stats_lock();
 	__this_cpu_add(memcg->vmstats_percpu->state[i], val);
 	val = memcg_state_val_in_pages(idx, val);
 	memcg_rstat_updated(memcg, val);
 	trace_mod_memcg_state(memcg, idx, val);
+	memcg_stats_unlock();
 }
 
 #ifdef CONFIG_MEMCG_V1
@@ -2845,7 +2847,7 @@  static void drain_obj_stock(struct memcg_stock_pcp *stock)
 
 			memcg = get_mem_cgroup_from_objcg(old);
 
-			mod_memcg_state(memcg, MEMCG_KMEM, -nr_pages);
+			__mod_memcg_state(memcg, MEMCG_KMEM, -nr_pages);
 			memcg1_account_kmem(memcg, -nr_pages);
 			if (!mem_cgroup_is_root(memcg))
 				memcg_uncharge(memcg, nr_pages);