On Wed, Mar 31, 2021 at 07:42:42PM +0200, Thomas Gleixner wrote: > On Wed, Mar 31 2021 at 12:01, Mel Gorman wrote: > > On Wed, Mar 31, 2021 at 11:55:56AM +0200, Thomas Gleixner wrote: > > @@ -887,13 +887,11 @@ void cpu_vm_stats_fold(int cpu) > > > > pzstats = per_cpu_ptr(zone->per_cpu_zonestats, cpu); > > > > - preempt_disable(); > > for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) > > if (pzstats->vm_stat_diff[i]) { > > int v; > > > > - v = pzstats->vm_stat_diff[i]; > > - pzstats->vm_stat_diff[i] = 0; > > + v = this_cpu_xchg(pzstats->vm_stat_diff[i], 0); > > Confused. pzstats is not a percpu pointer. zone->per_cpu_zonestats is. > > But @cpu is not necessarily the current CPU. > I was drinking drain cleaner instead of coffee. The code was also broken to begin with. drain_pages() is draining pagesets of a local or dead CPU. For a local CPU, disabling IRQs prevent an IRQ arriving during the drain, trying to allocate a page and potentially corrupt the local pageset -- ok. zone_pcp_reset is accessing a remote CPUs pageset, freeing the percpu pointer and resetting it to boot_pageset. zone_pcp_reset calling local_irq_save() does not offer any special protection against drain_pages because there are two separate IRQs involved. This particular patch may have no reason to touch zone_pcp_reset, cpu_vm_stats_fold or drain_zonestat at all but I need to think about it more tomorrow. -- Mel Gorman SUSE Labs