在Linux内核中(linux-4.14.12/mm/slab.c#3389), 乍一看下边的代码,貌似L3389有bug,于是我就绕偶兴趣地阅读了一下local_irq_save/local_irq_restore的源代码。
/* linux-4.14.12/mm/slab.c#3389 */ 3377 static __always_inline void * 3378 slab_alloc(struct kmem_cache *cachep, gfp_t flags, unsigned long caller) 3379 { 3380 unsigned long save_flags; 3381 void *objp; .... 3389 local_irq_save(save_flags); 3390 objp = __do_cache_alloc(cachep, flags); 3391 local_irq_restore(save_flags); .... 3399 return objp; 3400 }
在L3380和L3389中, 如果local_irq_save()是一个函数,必然存在着bug, 因为需要把save_flags的变量地址传给local_irq_save()才对。
3380 unsigned long save_flags; .... 3389 local_irq_save(save_flags);
但是,local_irq_save()和local_irq_restore()不是函数,而是宏,这样就没有bug了。
1. local_irq_save()和local_irq_restore()的实现
/* linux-4.14.12/include/linux/irqflags.h#139 */ 105 #ifdef CONFIG_TRACE_IRQFLAGS ... 110 #define local_irq_save(flags) 111 do { 112 raw_local_irq_save(flags); 113 trace_hardirqs_off(); 114 } while (0) 115 116 117 #define local_irq_restore(flags) 118 do { 119 if (raw_irqs_disabled_flags(flags)) { 120 raw_local_irq_restore(flags); 121 trace_hardirqs_off(); 122 } else { 123 trace_hardirqs_on(); 124 raw_local_irq_restore(flags); 125 } 126 } while (0) ... 135 #else /* !CONFIG_TRACE_IRQFLAGS */ ... 139 #define local_irq_save(flags) 140 do { 141 raw_local_irq_save(flags); 142 } while (0) 143 #define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0) ... 146 #endif /* CONFIG_TRACE_IRQFLAGS */
为简单起见,我们只关注!CONFIG_TRACE_IRQFLAGS分支就好了,
139 #define local_irq_save(flags) 140 do { 141 raw_local_irq_save(flags); 142 } while (0) 143 #define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)
于是, 我们可以认为, locale_irq_save()/local_irq_restore()等同于:
#define local_irq_save(flags) raw_local_irq_save(flags) #define local_irq_restore(flags) raw_local_irq_restore(flags)
2. raw_local_irq_save()和raw_local_irq_restore()的实现
/* linux-4.14.12/include/linux/irqflags.h#78 */ 78 #define raw_local_irq_save(flags) 79 do { 80 typecheck(unsigned long, flags); 81 flags = arch_local_irq_save(); 82 } while (0) 83 #define raw_local_irq_restore(flags) 84 do { 85 typecheck(unsigned long, flags); 86 arch_local_irq_restore(flags); 87 } while (0)
关于宏typecheck()不做解释,因为很直观,就是保证flags的类型必须是unsigned long。于是,raw_local_irq_save()和raw_local_irq_restore()等同于:
#define raw_local_irq_save(flags) flags = arch_local_irq_save() #define raw_local_irq_restore(flags) arch_local_irq_restore(flags)
下面以x86为例说明arch_local_irq_save()和arch_local_irq_restore()这两个函数的实现。
3. arch_local_irq_save()和arch_local_irq_restore()这两个函数在x86上的实现
/* linux-4.14.12/arch/x86/include/asm/irqflags.h#70 */ 70 static inline notrace unsigned long arch_local_save_flags(void) 71 { 72 return native_save_fl(); 73 } 74 75 static inline notrace void arch_local_irq_restore(unsigned long flags) 76 { 77 native_restore_fl(flags); 78 }
显然,只需要看明白native_save_fl()和native_restore_fl()的具体实现就好了。
3.1 native_save_fl()的实现
/* linux-4.14.12/arch/x86/include/asm/irqflags.h#16 */ 16 static inline unsigned long native_save_fl(void) 17 { 18 unsigned long flags; 19 20 /* 21 * "=rm" is safe here, because "pop" adjusts the stack before 22 * it evaluates its effective address -- this is part of the 23 * documented behavior of the "pop" instruction. 24 */ 25 asm volatile("# __raw_save_flags\n\t" 26 "pushf ; pop %0" 27 : "=rm" (flags) 28 : /* no input */ 29 : "memory"); 30 31 return flags; 32 }
这是一段内嵌的汇编代码,后面写一个简单的demo再解释。
3.2 native_restore_fl()的实现
。。。未完待续。。。。