理解Linux内核之中断保存与恢复

Posted veli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解Linux内核之中断保存与恢复相关的知识,希望对你有一定的参考价值。

在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()的实现

 

 

。。。未完待续。。。。

以上是关于理解Linux内核之中断保存与恢复的主要内容,如果未能解决你的问题,请参考以下文章

Linux(内核剖析):22---中断之中断控制接口(禁止/激活/屏蔽中断)

Linux内核设计基础之中断处理

(笔记)Linux内核学习之中断推后处理机制

Linux内核中断之中断申请接口

Linux(内核剖析):21---中断之中断上下文中断处理机制的实现/proc/interrupts

Windows内核之中断门提权