update_task_scan_period() scales the scan period with two ratios: local/(local + remote) and private/(private + shared). The existing guard only checks local + shared. That normally implies that both ratio denominators are non-zero because the locality and type counters are updated from the same hint faults. Memory hotplug can make that assumption fragile because task_numa_placement() folds the private/shared buffers by walking online nodes, while the locality counters were already charged when the fault was recorded. The divide-by-zero was reproduced on v7.3-rc1 in a QEMU guest by hot-removing a NUMA node after NUMA balancing had collected fault feedback. The next NUMA hint fault enters task_numa_placement() with locality feedback that was charged before node1 was removed. The per-node private/shared buffers for node1 are skipped by for_each_online_node(), so update_task_scan_period() can see local > 0 while private and shared are both 0. The old local + shared guard lets that state pass, and the subsequent private/(private + shared) calculation divides by zero. Keep the slow-scan fallback for empty or unusable samples, but test the actual denominators before calculating the ratios. Fixes: 37ec97deb3a8 ("sched/numa: Slow down scan rate if shared faults dominate") Signed-off-by: Li Zhe --- kernel/sched/fair.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1053437933ee..76532b8202dc 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3698,6 +3698,8 @@ static void update_task_scan_period(struct task_struct *p, unsigned long remote = p->numa_faults_locality[0]; unsigned long local = p->numa_faults_locality[1]; + unsigned long locality_faults = local + remote; + unsigned long type_faults = private + shared; /* * If there were no record hinting faults then either the task is @@ -3706,7 +3708,7 @@ static void update_task_scan_period(struct task_struct *p, * migration then it implies we are migrating too quickly or the local * node is overloaded. In either case, scan slower */ - if (local + shared == 0 || p->numa_faults_locality[2]) { + if (!locality_faults || !type_faults || p->numa_faults_locality[2]) { p->numa_scan_period = min(p->numa_scan_period_max, p->numa_scan_period << 1); @@ -3723,8 +3725,8 @@ static void update_task_scan_period(struct task_struct *p, * >= NUMA_PERIOD_THRESHOLD scan period increases (scan slower) */ period_slot = DIV_ROUND_UP(p->numa_scan_period, NUMA_PERIOD_SLOTS); - lr_ratio = (local * NUMA_PERIOD_SLOTS) / (local + remote); - ps_ratio = (private * NUMA_PERIOD_SLOTS) / (private + shared); + lr_ratio = (local * NUMA_PERIOD_SLOTS) / locality_faults; + ps_ratio = (private * NUMA_PERIOD_SLOTS) / type_faults; if (ps_ratio >= NUMA_PERIOD_THRESHOLD) { /* -- 2.20.1