When show_free_areas() iterates over populated memory zones, it
unconditionally acquires zone->lock via spin_lock_irqsave(). If
show_mem() is invoked from an Oops or panic context while the crashing
CPU or a wedged remote CPU already holds zone->lock, or from an NMI or
hardirq context under CONFIG_PREEMPT_RT where spinlock_t is a sleeping
rt_mutex, acquiring zone->lock can self-deadlock or fault before
post-mortem recovery (such as kdump) can proceed.
Check can_spin_trylock() and use spin_trylock_irqsave() when
!can_spin_trylock(), in_nmi(), oops_in_progress, or panic_in_progress()
is active, and emit the zone header after releasing zone->lock,
logging a contention notice and skipping the locked zone rather than
spinning indefinitely.
Fixes: d999bd9392de ("panic: add options to print system info when panic
happens")
Signed-off-by: Aaron Tomlin <[email protected]>
---
mm/show_mem.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/mm/show_mem.c b/mm/show_mem.c
index b938cbcd774a..77098077523a 100644
--- a/mm/show_mem.c
+++ b/mm/show_mem.c
@@ -367,10 +367,17 @@ static void show_free_areas(unsigned int filter, const
nodemask_t *nodemask,
continue;
if (show_mem_node_skip(filter, zone_to_nid(zone), nodemask))
continue;
- show_node(zone);
- printk(KERN_CONT "%s: ", zone->name);
- spin_lock_irqsave(&zone->lock, flags);
+ if (!can_spin_trylock() || in_nmi() ||
+ oops_in_progress || panic_in_progress()) {
+ if (!can_spin_trylock() ||
+ !spin_trylock_irqsave(&zone->lock, flags)) {
+ pr_warn("zone %s lock contended\n", zone->name);
+ continue;
+ }
+ } else {
+ spin_lock_irqsave(&zone->lock, flags);
+ }
for (order = 0; order < NR_PAGE_ORDERS; order++) {
struct free_area *area = &zone->free_area[order];
int type;
@@ -385,6 +392,8 @@ static void show_free_areas(unsigned int filter, const
nodemask_t *nodemask,
}
}
spin_unlock_irqrestore(&zone->lock, flags);
+ show_node(zone);
+ printk(KERN_CONT "%s: ", zone->name);
for (order = 0; order < NR_PAGE_ORDERS; order++) {
printk(KERN_CONT "%lu*%lukB ",
nr[order], K(1UL) << order);
--
2.55.0