linux
"Warning due to a call to kmalloc() with flags __GFP_WAIT and interrupts enabled
The SLAB allocator is initialized by start_kernel() with interrupts disabled. Later in this process, setup_cpu_cache() performs the per-CPU kmalloc cache initialization, and will try to allocate memory for these caches passing the GFP_KERNEL flags. These flags include __GFP_WAIT, which allows the process to sleep while waiting for memory to be available. Since, interrupts are disabled during SLAB initialization, this may lead to a deadlock. Enabling LOCKDEP and other debugging options will detect and report this situation."
Bug fixed by commit eb91f1d0a53
Type | WeakAssertionViolation |
Config | "SLAB && LOCKDEP && TRACE_IRQFLAGS && PROVE_LOCKING && NUMA" (5th degree) |
Fix-in | code |
Location | kernel/ |
#if defined(CONFIG_LOCKDEP) && defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) #include <assert.h> #endif #include <stdbool.h> #define ___GFP_WAIT 0x10u #define ___GFP_IO 0x40u #define ___GFP_FS 0x80u #define ___GFP_HIGH 0x20u #define __GFP_HIGH ((gfp_t)___GFP_HIGH) #define __GFP_WAIT ((gfp_t)___GFP_WAIT) /* Can wait and reschedule? */ #define __GFP_IO ((gfp_t)___GFP_IO) /* Can start physical IO? */ #define __GFP_FS ((gfp_t)___GFP_FS) /* Can call down to low-level FS? */ #define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS) #define GFP_ATOMIC (__GFP_HIGH) #define GFP_NOWAIT (GFP_ATOMIC & ~__GFP_HIGH) typedef int gfp_t; bool irqs_disabled = false; void local_irq_disable(void) { irqs_disabled = true; } #if defined(CONFIG_LOCKDEP) && defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) static void __lockdep_trace_alloc(gfp_t gfp_mask) { if (!(gfp_mask & __GFP_WAIT)) return; if (!(gfp_mask & __GFP_FS)) return; assert(!irqs_disabled); } void lockdep_trace_alloc(gfp_t gfp_mask) { __lockdep_trace_alloc(gfp_mask); } #else void lockdep_trace_alloc(gfp_t gfp_mask) { } #endif #ifdef CONFIG_SLAB #ifdef CONFIG_NUMA void __cache_alloc_node(gfp_t flags) { lockdep_trace_alloc(flags); } void kmem_cache_alloc_node(gfp_t flags) { __cache_alloc_node(flags); } #ifdef CONFIG_KMEMTRACE void kmem_cache_alloc_node_notrace(gfp_t flags) { __cache_alloc_node(flags); } #else void kmem_cache_alloc_node_notrace(gfp_t flags) { kmem_cache_alloc_node(flags); } #endif #endif /* CONFIG_NUMA */ #ifdef CONFIG_NUMA static void kmalloc_node(gfp_t gfp_mask) { kmem_cache_alloc_node_notrace(gfp_mask); } #endif #if !defined(CONFIG_NUMA) void kmalloc_node() { return; } #endif static int setup_cpu_cache() { kmalloc_node(GFP_KERNEL); } void kmem_cache_create() { setup_cpu_cache(); } void kmem_cache_init(void) { #ifdef CONFIG_SLAB kmem_cache_create(); #endif } #else void kmem_cache_init(void) { } #endif static void mm_init(void) { kmem_cache_init(); } int main() { local_irq_disable(); mm_init(); return 0; }
diff --git a/simple/eb91f1d.c b/simple/eb91f1d.c --- a/simple/eb91f1d.c +++ b/simple/eb91f1d.c @@ -90,7 +90,7 @@ static int setup_cpu_cache() { - kmalloc_node(GFP_KERNEL); + kmalloc_node(GFP_NOWAIT); } void kmem_cache_create()
#if defined(CONFIG_LOCKDEP) && defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) #include <assert.h> #endif #include <stdbool.h> #define ___GFP_WAIT 0x10u #define ___GFP_IO 0x40u #define ___GFP_FS 0x80u #define __GFP_WAIT ((gfp_t)___GFP_WAIT) /* Can wait and reschedule? */ #define __GFP_IO ((gfp_t)___GFP_IO) /* Can start physical IO? */ #define __GFP_FS ((gfp_t)___GFP_FS) /* Can call down to low-level FS? */ #define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS) typedef int gfp_t; bool irqs_disabled = false; int main() { // local_irq_disable(); irqs_disabled = true; // mm_init(); #ifdef CONFIG_SLAB // kmalloc_node(GFP_KERNEL); #ifdef CONFIG_NUMA #ifdef CONFIG_KMEMTRACE #if defined(CONFIG_LOCKDEP) && defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING) if (!(GFP_KERNEL & __GFP_WAIT)) return; if (!(GFP_KERNEL & __GFP_FS)) return; assert(!irqs_disabled); #else #endif #else #endif #endif #if !defined(CONFIG_NUMA) return; #endif #else #endif return 0; }
// for !KMEMTRACE . call init/main.c:552:start_kernel() . 573: local_irq_disable(); . 609: mm_init(); .. call init/main.c:540:mm_init() .. 548: kmem_cache_init(); ... call mm/slab.c:1444:kmem_cache_init() .... call mm/slab.c:2154:kmem_cache_create() .... 2287: if (slab_is_available()) .... 2288: gfp = GFP_KERNEL; // the bug occurs because GFP_KERNEL implies __GFP_WAIT // no problem if gfp = GFP_NOWAIT .... 2396: if (setup_cpu_cache(cachep, gfp)) ..... call mm/slab.c:2070:setup_cpu_cache() ..... 2104: kmalloc_node(sizeof(struct kmem_list3), ..... 2105: GFP_KERNEL, node); ...... [NUMA] call include/linux/slab_def.h:102:kmalloc_node() ...... 129: ret = kmem_cache_alloc_node_notrace(cachep, flags, node); ....... [!KMEMTRACE] call include/linux/slab_def.h:93:kmem_cache_alloc_node_notrace() ........ [NUMA] call mm/slab.c:3657:kmem_cache_alloc_node() ........ 3659: void *ret = __cache_alloc_node(cachep, flags, nodeid, ...) ......... call mm/slab.c:3351:__cache_alloc_node() ......... 3357: lockdep_trace_alloc(flags); .......... [TRACE_IRQFLAGS,PROVE_LOCKING] call kernel/lockdep.c:2290:lockdep_trace_alloc() .......... 2300: __lockdep_trace_alloc() ........... call kernel/lockdep.c:2263:__lockdep_trace_alloc() ........... ERROR 2282: if (DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags))) // gfp == GFP_WAIT is required to reach this WARN_ON