SMP相关内核函数解析

1150阅读 0评论2016-06-01 zhanglin496
分类:LINUX


  1. 一、前言
  2. SMP(Symmetric Multi-Processing),对称多处理结构的简称,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。在这种技术的支持下,一个服务器系统可以同时运行多个处理器,并共享内存和其他的主机资源。像双至强,也就是我们所说的二路,这是在对称处理器系统中最常见的一种(至强MP可以支持到四路,AMD Opteron可以支持1-8 路)。也有少数是16路的。但是一般来讲,SMP结构的机器可扩展性较差,很难做到100个以上多处理器,常规的一般是8个到16 个,不过这对于多数的用户来说已经够用了。在高性能服务器和工作站级主板架构中最为常见,像UNIX服务器可支持最多256个CPU的系统。

  3. 二、smp_processor_id()函数解析
  4. 1.含义
  5. smp_processor_id()其意义在于SMP的情况下,获得当前CPU的ID。如果不是SMP,那么就返回0。在CONFIG_X86_32_SMP的情况下,是一个宏,调用raw_smp_processor_id()宏:

  6. 2.代码
  7. #define smp_processor_id() raw_smp_processor_id()
  8. #define raw_smp_processor_id() (this_cpu_read(cpu_number))
  9. #define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, (pcp))

  10. //通过以上宏调用,即最后调用__pcpu_size_call_return(this_cpu_read_,cpu_number)
  11. 这里cpu_number来自arch/x86/kernel/setup_percpu.c的24行:
  12. DEFINE_PER_CPU(int, cpu_number);
  13. EXPORT_PER_CPU_SYMBOL(cpu_number);
  14. 这个东西不像是c语言全局变量,而是通过两个宏来定义的。要读懂这两个宏,必须对每CPU变量这个概念非常了解,如果还不是很清楚的同学请查阅一下博客“每CPU变量”http://blog.csdn.net/yunsongice/archive/2010/05/18/5605239.aspx。

  15. 其中DEFINE_PER_CPU来自文件include/linux/percpu-defs.h:
  16. #define DEFINE_PER_CPU(type, name) /
  17.        DEFINE_PER_CPU_SECTION(type, name, "")

  18. 该宏静态分配一个每CPU数组,数组名为name,结构类型为type。由于我们没有设置CONFIG_DEBUG_FORCE_WEAK_PER_CPU编译选项,所以DEFINE_PER_CPU_SECTION又被定义为:
  19. #define DEFINE_PER_CPU_SECTION(type, name, sec) /
  20.        __PCPU_ATTRS(sec) PER_CPU_DEF_ATTRIBUTES /
  21.        __typeof__(type) name

  22. 其中,__PCPU_ATTRS(sec)在include/linux/percpu-defs.h中定义:
  23. #define __PCPU_ATTRS(sec) /
  24.        __percpu __attribute__((section(PER_CPU_BASE_SECTION sec))) /
  25.        PER_CPU_ATTRIBUTES

  26. __percpu是个编译扩展类型,大家可以去看看include/linux/compile.h这个文件,里面的__percpu是空的。而传进来的sec也是空的,PER_CPU_ATTRIBUTES也是空的,而上面PER_CPU_DEF_ATTRIBUTES还是空代码,可能都是留给将来内核代码扩展之用的吧,所以DEFINE_PER_CPU(int, cpu_number)展开就是:
  27. __attribute__((section(PER_CPU_BASE_SECTION))) /
  28. __typeof__(int) cpu_number
  29.  
  30. 所以现在只关注PER_CPU_BASE_SECTION,来自include/asm-generic/percpu.h
  31. #ifdef CONFIG_SMP
  32. #define PER_CPU_BASE_SECTION ".data.percpu"
  33. #else
  34. #define PER_CPU_BASE_SECTION ".data"
  35. #endif

  36. gcc支持一种叫做类型识别的技术,通过typeof(x)关键字,获得x的数据类型。而如果是在一个要被一些c文件包含的头文件中获得变量的数据类型,就需要用__typeof__而不是typeof关键字了,比如说我们这里。最后,这里就是声明一个int类型的cpu_number变量,编译的时候把他指向.data.percpu段的开始位置。

  37. #define __pcpu_size_call_return(stem, variable) \
  38. ({     
  39.     //声明变量int pscr_ret__                                                                                \
  40.     typeof(variable) pscr_ret__;
  41.     //验证cpu_number是否为一个percpu变量 \
  42.     __verify_pcpu_ptr(&(variable));
  43.     
  44.     //根据cpu_number的size,调用相应的函数,即调用this_cpu_read_4 \
  45.     switch(sizeof(variable)) { \
  46.     case 1: pscr_ret__ = stem##1(variable);break; \
  47.     case 2: pscr_ret__ = stem##2(variable);break; \
  48.     case 4: pscr_ret__ = stem##4(variable);break; \
  49.     case 8: pscr_ret__ = stem##8(variable);break; \
  50.     default: \
  51.         __bad_size_call_parameter();break;             \
  52.     } \
  53.     pscr_ret__; \
  54. })

  55. //用来验证指针是per-cpu类型
  56. #define __verify_pcpu_ptr(ptr) do { \
  57.     const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
  58.     (void)__vpp_verify; \
  59. }

  60. #define this_cpu_read_1(pcp) percpu_from_op("mov", (pcp), "m"(pcp))
  61. #define this_cpu_read_2(pcp) percpu_from_op("mov", (pcp), "m"(pcp))
  62. #define this_cpu_read_4(pcp) percpu_from_op("mov", (pcp), "m"(pcp))

  63. //调用percpu_from_op("mov", cpu_number, "m" (cpu_number))
  64. #define percpu_from_op(op, var, constraint) \
  65. ({ \
  66.     typeof(var) pfo_ret__; \
  67.     switch (sizeof(var)) { \
  68.     case 1: \
  69.         asm(op "b "__percpu_arg(1)",%0" \
  70.         : "=q" (pfo_ret__) \
  71.         : constraint); \
  72.         break; \
  73.     case 2: \
  74.         asm(op "w "__percpu_arg(1)",%0" \
  75.         : "=r" (pfo_ret__) \
  76.         : constraint); \
  77.         break; \
  78.     case 4: \
  79.         asm(op "l "__percpu_arg(1)",%0" \
  80.         : "=r" (pfo_ret__) \
  81.         : constraint); \
  82.         break; \
  83.     case 8: \
  84.         asm(op "q "__percpu_arg(1)",%0" \
  85.         : "=r" (pfo_ret__) \
  86.         : constraint); \
  87.         break; \
  88.     default: __bad_percpu_size(); \
  89.     } \
  90.     pfo_ret__; \
  91. })

  92. #define __percpu_arg(x) __percpu_prefix "%P" #x
  93. #define __percpu_prefix "%%"__stringify(__percpu_seg)":"
  94. #define __percpu_seg fs

  95. 所以__percpu_arg(x)翻译过来就是:"%%"__stringify(fs)":%P" #x

  96. #define __stringify_1(x...) #x
  97. #define __stringify(x...) __stringify_1(x)

  98. 所以__percpu_arg(1)最终翻译过来就是:
  99. "%%" "fs:%P" "1"

  100. 因此上边的汇编代码翻译过来就是:
  101. asm("movl %%fs:%P1, %0" /
  102.      : "=q" (pfo_ret__) /
  103.      : "m" (var));

  104. 其中pfo_ret__是输出部%0,q,表示寄存器eax、ebx、ecx或edx中的一个,并且变量pfo_ret__存放在这个寄存器中。var就是刚才我们建立的那个临时的汇编变量cpu_number,作为输入部%1。

  105. 还记得“加载全局/中断描述符表”中把__KERNEL_PERCPU段选择子赋给了fs了吗,每个 cpu 的 fs 的内容都不同,fs:cpu_number就获得了当前存放在__KERNEL_PERCPU段中cpu_number偏移的内存中,最后把结果返回给pfo_ret__。所以这个宏最后的结果就是pfo_ret__的值,其返回的是CPU的编号.

  106. 参考:
  107. http://blog.csdn.net/yunsongice/article/details/6130032
  108. http://bbs.chinaunix.net/thread-3767460-1-1.html


  109. //////////////////////////////////////////////////////////////////////////////////////
  110. 三、for_each_online_cpu()内核函数解析
  111. 1.含义
  112. for_each_online_cpu来枚举系统中所有core的id值,也就是常说的cpuid。

  113. 2.代码
  114. #define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask)
  115. #define for_each_cpu(cpu, mask) \
  116.     for ((cpu) = -1;(cpu) = cpumask_next((cpu), (mask)),(cpu) < nr_cpu_ids;)

  117. 从以上代码可以看出依此根据掩码cpu_online_mask和系统core个数nr_cpu_ids来循环取出真正在工作的处理器序号。

  118. const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);

  119. 2.1 to_cpumask
  120. //把cpu_online_bits强制转换成cpumask类型
  121. #define to_cpumask(bitmap) \
  122.     ((struct cpumask *)(1 ? (bitmap):(void *)sizeof(__check_is_bitmap(bitmap))))

  123. typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
  124. #define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)]

  125. //BITS_TO_LONGS宏根据处理器个数分配数组大小
  126. #define BITS_PER_BYTE 8
  127. #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
  128. #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

  129. 所以以上宏调用生成的cpu_online_mask结构是:
  130. struct cpumask
  131. {
  132.     unsigned long bits[1];
  133. }*cpu_online_mask;

  134. 2.2 cpu_online_bits
  135. cpu_online_bits, 用以表示系统真正在工作的处理器个数/状态。当内核管理处理器时主要是通过这个来进行的.
  136. static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;

  137. //通过这个函数可以设置当前cpu是有效的还是无效的,即
  138. void set_cpu_online(unsigned int cpu, bool online)
  139. {
  140.     if (online)
  141.         cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
  142.     else
  143.         cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
  144. }

  145. static inline void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
  146. {
  147.     //#define cpumask_bits(maskp) ((maskp)->bits)
  148.     set_bit(cpumask_check(cpu), cpumask_bits(dstp));
  149. }

  150. static inline void set_bit(int nr, void *addr)
  151. {
  152.     //把addr地址下的内容的第nr位置为1
  153.     asm("btsl %1,%0" : "+m" (*(u32 *)addr) : "Ir" (nr));
  154. }

  155. 2.3 cpumask_next
  156. static inline unsigned int cpumask_next(int n, const struct cpumask *srcp)
  157. {
  158.     /* -1 is a legal arg here. */
  159.     if (n != -1)
  160.         cpumask_check(n);//检查cpu位图
  161.     
  162.     //取下一位数值    
  163.     return find_next_bit(cpumask_bits(srcp), nr_cpumask_bits, n+1);
  164. }

  165. #define cpumask_bits(maskp) ((maskp)->bits)
  166. //系统内核数量
  167. #define nr_cpumask_bits NR_CPUS

  168. unsigned long find_next_bit(const unsigned long *addr, unsigned long size,unsigned long offset)
  169. {
  170.     //#define BITS_PER_LONG 32
  171.     //#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
  172.     //当前要查找的cpu号在baddr地址处的偏移地址
  173.     const unsigned long *p = addr + BITOP_WORD(offset);
  174.     unsigned long result = offset & ~(BITS_PER_LONG-1);//offset & 0xE0
  175.     unsigned long tmp;
  176.     
  177.     if (offset >= size)
  178.         return size;
  179.         
  180.     size -= result;
  181.     //offset整除得到在p地址处的偏移
  182.     offset %= BITS_PER_LONG;
  183.     if (offset) {
  184.         tmp = *(p++);//取得p地址开始处保存的数据,即数组bits数据
  185.         //tmp最低的offset为清零
  186.         tmp &= (~0UL << offset);
  187.         //cpu个数小于32
  188.         if (size < BITS_PER_LONG)
  189.             goto found_first;
  190.         if (tmp)
  191.             goto found_middle;
  192.         size -= BITS_PER_LONG;
  193.         result += BITS_PER_LONG;
  194.     }
  195.     
  196.     //若cpu个数超过32个时
  197.     while (size & ~(BITS_PER_LONG-1)) {
  198.         if ((tmp = *(p++)))
  199.             goto found_middle;
  200.         result += BITS_PER_LONG;
  201.         size -= BITS_PER_LONG;
  202.     }
  203.     if (!size)
  204.         return result;
  205.     tmp = *p;
  206.     
  207. found_first:
  208.     //清楚无效的cpu所占位数
  209.     tmp &= (~0UL >> (BITS_PER_LONG - size));
  210.     if (tmp == 0UL) /* Are any bits set? */
  211.         return result + size; /* Nope. */
  212. found_middle:
  213.     //__ffs找到tmp中第一个不为0的位的序号,加上result就是cpu号
  214.     return result + __ffs(tmp);
  215. }

  216. for_each_cpu () 函数内核实现了两个版本,一个是单处理器版本,一个是多处理器版本,其中他还用到了cpu_present_mask 宏。
  217. 系统中有四种这类的变量分别叫,cpu_present_mask,cpu_online_mask,cpu_active_mask , cpu_possible_mask;
  218. 在Linux内核中默认的SMP是最大支持8CPU,当然你可以加大这个数值。这可以在make menuconfig 中找到相关设置 "CPUS".这四个变量来源于四个属性:
  219. cpu_all_bits ,用以表示在 menuconfig 中设置的NR_CPUS的值是多少。
  220. cpu_possible_bits,表示实际在运行时处理器的CPU个数是多少?
  221. cpu_online_bits, 用以表示系统真正在工作的处理器个数/状态。当内核管理处理器时主要是通过这个来进行的。
  222. cpu_present_bits:用以表示系统中present的处理器数量,不一定所有都是Online的,在支持处理器热插拔的系统中,possible与present的关系为“cpu_possible_map = cpu_present_map + additional_cpus” ,present处理器是指系统固有的处理器个数不是外部插入的。
  223. cpu_active_bits, 表示目前处于可工作状态的处理器个数。

  224. setup_max_cpus
  225. nr_cpu_ids
  226. 在默认情况下都表示CPUS数量。
  227. const struct cpumask *const cpu_possible_mask = to_cpumask(cpu_possible_bits);

  228. /* An arch may set nr_cpu_ids earlier if needed, so this would be redundant */
  229. void __init setup_nr_cpu_ids(void)
  230. {
  231.     nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
  232.  }

  233.  /* this is hard limit */
  234. static int __init nrcpus(char *str)
  235. {
  236.     int nr_cpus;
  237.     
  238.     get_option(&str, &nr_cpus);
  239.     if (nr_cpus > 0 && nr_cpus < nr_cpu_ids)
  240.         nr_cpu_ids = nr_cpus;
  241.     
  242.     return 0;
  243. }
  244. early_param("nr_cpus", nrcpus);

  245. int nr_cpu_ids __read_mostly = NR_CPUS;

  246. #define NR_CPUS CONFIG_NR_CPUS
  247. NR_CPUS是个宏定义,可以在config中配置(CONFIG_NR_CPUS),表示系统中CPU的最大数量

  248. //////////////////////////////////////////////////////////////////////////////////////
  249. 三、smp_call_function_single()内核函数解析
  250. 1.含义
  251. 系统中每个cpu都还拥有各自的一个类型为struct call_single_queue的队列dst(list),smp_call_function_single()会根据目标cpu来获得该队列,把前述的csd作为跨cpu参数传递的方法。不管怎么说吧,跨cpu调用的参数传递方法是用了,然后如果队列dst为空,就调用arch_send_call_function_single_ipi(cpu)给参数所指定的cpu发ipi消息,目标cpu收到该消息进入中断处理函数,那么就调用csd_data->func函数了(其实应该是ipi的中断处理函数处理dst队列中的每个结点,调用每个结点上的func函数指针,所以队列不为空时就没必要再发ipi消息了。

  252. 2.代码
  253. int smp_call_function_single(int cpu, smp_call_func_t func, void *info,int wait)
  254. {
  255.     struct call_single_data d = {
  256.         .flags = 0,
  257.     };
  258.     unsigned long flags;
  259.     int this_cpu;
  260.     int err = 0;
  261.     
  262.     this_cpu = get_cpu();//获得当前cpu号,并且禁止抢占

  263.     /*
  264.     * Can deadlock when called with interrupts disabled.
  265.     * We allow cpu's that are not yet online though, as no one else can
  266.     * send smp call function interrupt to this cpu and as such deadlocks
  267.     * can't happen.
  268.     */
  269.     WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() && !oops_in_progress);
  270.     
  271.     //要指定运行的cpu id和当前cpu id一致的话,就调用func函数运行
  272.     if (cpu == this_cpu) {
  273.         local_irq_save(flags);
  274.         func(info);
  275.         local_irq_restore(flags);
  276.     } else {//不一致,发送ipi中断
  277.         //要运行该func的cpu号必须小于系统中cpu个数,且该cpu online
  278.         if ((unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) {
  279.             /*
  280.             struct call_single_data {
  281.                 struct list_head list;
  282.                 smp_call_func_t func;
  283.                 void *info;
  284.                 u16 flags;
  285.             };
  286.             */
  287.             struct call_single_data *csd = &d;
  288.     
  289.             if (!wait)
  290.                 csd = &__get_cpu_var(csd_data);
  291.             
  292.             //设置csd结构
  293.             csd_lock(csd);
  294.             csd->func = func;
  295.             csd->info = info;//函数参数
  296.             //挂到每cpu队列call_single_queue中,并向指定cpu发送IPI中断
  297.             generic_exec_single(cpu, csd, wait);
  298.         } else {
  299.             err = -ENXIO; /* CPU not online */
  300.         }
  301.     }
  302.     
  303.     put_cpu();
  304.     
  305.     return err;
  306. }

  307. void generic_exec_single(int cpu, struct call_single_data *csd, int wait)
  308. {
  309.     //找到要运行该func的cpu的每cpu变量call_single_queue队列
  310.     struct call_single_queue *dst = &per_cpu(call_single_queue, cpu);
  311.     unsigned long flags;
  312.     int ipi;
  313.     
  314.     raw_spin_lock_irqsave(&dst->lock, flags);
  315.     ipi = list_empty(&dst->list);//if empty return 1
  316.     //把该csd结构挂入每cpu变量call_single_queue队列尾部
  317.     list_add_tail(&csd->list, &dst->list);
  318.     raw_spin_unlock_irqrestore(&dst->lock, flags);
  319.     
  320.     //队列为空,就向该cpu发送IPI中断,让其读取其每cpu队列call_single_queue上的csd结构,进而去执行func函数
  321.     if (ipi)
  322.         arch_send_call_function_single_ipi(cpu);
  323.     
  324.     //若设置了等待标志,则进行等待
  325.     if (wait)
  326.         csd_lock_wait(csd);
  327. }

  328. static void csd_lock_wait(struct call_single_data *csd)
  329. {
  330.     //若csd标志设置了CSD_FLAG_LOCK位,则在这里循环等待。前边并没有设置该标志
  331.     while (csd->flags & CSD_FLAG_LOCK)
  332.         cpu_relax();
  333. }

  334. //全局变量smp_ops也是一个smp_ops结构,在代码arch/x86/kernel/smp.c中被初始化成:
  335. struct smp_ops smp_ops = {
  336.     .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu,
  337.     .smp_prepare_cpus = native_smp_prepare_cpus,
  338.     .smp_cpus_done = native_smp_cpus_done,
  339.     .stop_other_cpus = native_stop_other_cpus,
  340.     .smp_send_reschedule = native_smp_send_reschedule,
  341.     .cpu_up = native_cpu_up,
  342.     .cpu_die = native_cpu_die,
  343.     .cpu_disable = native_cpu_disable,
  344.     .play_dead = native_play_dead,
  345.     .send_call_func_ipi = native_send_call_func_ipi,
  346.     .send_call_func_single_ipi = native_send_call_func_single_ipi,
  347. };

  348. static inline void arch_send_call_function_single_ipi(int cpu)
  349. {
  350.     //调用native_send_call_func_single_ipi
  351.     smp_ops.send_call_func_single_ipi(cpu);
  352. }

  353. void native_send_call_func_single_ipi(int cpu)
  354. {
  355.     apic->send_IPI_mask(cpumask_of(cpu), CALL_FUNCTION_SINGLE_VECTOR);
  356. }

  357. #define cpumask_of(cpu) (get_cpu_mask(cpu))
  358. static inline const struct cpumask *get_cpu_mask(unsigned int cpu)
  359. {
  360.     //cpu是指目标cpu,即要将该IPI中断传给此cpu,通过下面得到通知IPI中断的cpu掩码
  361.     const unsigned long *p = cpu_bit_bitmap[1 + cpu % BITS_PER_LONG];
  362.     p -= cpu / BITS_PER_LONG;
  363.     return to_cpumask(p);
  364. }

  365. const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = {
  366.     MASK_DECLARE_8(0), MASK_DECLARE_8(8),
  367.     MASK_DECLARE_8(16), MASK_DECLARE_8(24),
  368. #if BITS_PER_LONG > 32
  369.     MASK_DECLARE_8(32), MASK_DECLARE_8(40),
  370.     MASK_DECLARE_8(48), MASK_DECLARE_8(56),
  371. #endif
  372. };

  373. #define MASK_DECLARE_1(x) [x+1][0] = (1UL << (x))
  374. #define MASK_DECLARE_2(x) MASK_DECLARE_1(x), MASK_DECLARE_1(x+1)
  375. #define MASK_DECLARE_4(x) MASK_DECLARE_2(x), MASK_DECLARE_2(x+2)
  376. #define MASK_DECLARE_8(x) MASK_DECLARE_4(x), MASK_DECLARE_4(x+4)

  377. //通过以上宏计算得到:
  378. cpu_bit_bitmap[][] = {
  379.     [1][0]=1,
  380.     [2][0]=1<<1,
  381.     [3][0]=1<<2,
  382.     [4][0]=1<<3,
  383.     ......
  384. }

  385. //x86 32位下,apic定义为
  386. struct apic *apic = &apic_default;
  387. static struct apic apic_default = {
  388.  77 .name = "default",
  389.  78 .probe = probe_default,
  390.  79 .acpi_madt_oem_check = NULL,
  391.  80 .apic_id_valid = default_apic_id_valid,
  392.  81 .apic_id_registered = default_apic_id_registered,
  393.  82
  394.  83 .irq_delivery_mode = dest_LowestPrio,
  395.  84 /* logical delivery broadcast to all CPUs: */
  396.  85 .irq_dest_mode = 1,
  397.  86
  398.  87 .target_cpus = default_target_cpus,
  399.  88 .disable_esr = 0,
  400.  89 .dest_logical = APIC_DEST_LOGICAL,
  401.  90 .check_apicid_used = default_check_apicid_used,
  402.  91 .check_apicid_present = default_check_apicid_present,
  403.  92
  404.  93 .vector_allocation_domain = flat_vector_allocation_domain,
  405.  94 .init_apic_ldr = default_init_apic_ldr,
  406.  95
  407.  96 .ioapic_phys_id_map = default_ioapic_phys_id_map,
  408.  97 .setup_apic_routing = setup_apic_flat_routing,
  409.  98 .multi_timer_check = NULL,
  410.  99 .cpu_present_to_apicid = default_cpu_present_to_apicid,
  411. 100 .apicid_to_cpu_present = physid_set_mask_of_physid,
  412. 101 .setup_portio_remap = NULL,
  413. 102 .check_phys_apicid_present = default_check_phys_apicid_present,
  414. 103 .enable_apic_mode = NULL,
  415. 104 .phys_pkg_id = default_phys_pkg_id,
  416. 105 .mps_oem_check = NULL,
  417. 106
  418. 107 .get_apic_id = default_get_apic_id,
  419. 108 .set_apic_id = NULL,
  420. 109 .apic_id_mask = 0x0F << 24,
  421. 110
  422. 111 .cpu_mask_to_apicid_and = flat_cpu_mask_to_apicid_and,
  423. 112
  424. 113 .send_IPI_mask = default_send_IPI_mask_logical,
  425. 114 .send_IPI_mask_allbutself = default_send_IPI_mask_allbutself_logical,
  426. 115 .send_IPI_allbutself = default_send_IPI_allbutself,
  427. 116 .send_IPI_all = default_send_IPI_all,
  428. 117 .send_IPI_self = default_send_IPI_self,
  429. 118
  430. 119 .trampoline_phys_low = DEFAULT_TRAMPOLINE_PHYS_LOW,
  431. 120 .trampoline_phys_high = DEFAULT_TRAMPOLINE_PHYS_HIGH,
  432. 121
  433. 122 .wait_for_init_deassert = default_wait_for_init_deassert,
  434. 123
  435. 124 .smp_callin_clear_local_apic = NULL,
  436. 125 .inquire_remote_apic = default_inquire_remote_apic,
  437. 126
  438. 127 .read = native_apic_mem_read,
  439. 128 .write = native_apic_mem_write,
  440. 129 .eoi_write = native_apic_mem_write,
  441. 130 .icr_read = native_apic_icr_read,
  442. 131 .icr_write = native_apic_icr_write,
  443. 132 .wait_icr_idle = native_apic_wait_icr_idle,
  444. 133 .safe_wait_icr_idle = native_safe_apic_wait_icr_idle,
  445. 134
  446. 135 .x86_32_early_logical_apicid = default_x86_32_early_logical_apicid,
  447. };

  448. void default_send_IPI_mask_logical(const struct cpumask *cpumask, int vector)
  449. {
  450.     //得到相应cpu的掩码,若是cpu1的话,此值应为0x0000 0002
  451.     unsigned long mask = cpumask_bits(cpumask)[0];
  452.     unsigned long flags;
  453.     
  454.     if (!mask)
  455.         return;
  456.     
  457.     local_irq_save(flags);
  458.     WARN_ON(mask & ~cpumask_bits(cpu_online_mask)[0]);
  459.     //#define APIC_DEST_LOGICAL 0x00800
  460.     __default_send_IPI_dest_field(mask, vector, apic->dest_logical);
  461.     local_irq_restore(flags);
  462. }

  463. static inline void __default_send_IPI_dest_field(unsigned int mask, int vector, unsigned int dest)
  464. {
  465.     unsigned long cfg;
  466.     
  467.     //CPU内部APIC有一些控制寄存器,APIC_ICR和APIC_ICR2是其中的两个。要向系统中的一个CPU发出中断请求时,首先要通过apic_wait_icr_idle(),确认或等待APIC_ICR处于空闲状态.
  468.     if (unlikely(vector == NMI_VECTOR))
  469.         safe_apic_wait_icr_idle();
  470.     else
  471.         __xapic_wait_icr_idle();
  472.     
  473.     //prepare target chip field
  474.     cfg = __prepare_ICR2(mask);
  475.     //APIC_ICR2主要用来说明要发送的中断请求的目标
  476.     native_apic_mem_write(APIC_ICR2, cfg);
  477.     
  478.     //program the ICR
  479.     cfg = __prepare_ICR(0, vector, dest);
  480.     
  481.     //Send the IPI. The write to APIC_ICR fires this off.
  482.     native_apic_mem_write(APIC_ICR, cfg);
  483. }
  484. 在SMP系统上,当一个CPU想对另一个CPU发送中断信号时,就在自己的本地APIC的ICR寄存器(Interrupt Command Register,中断命令寄存器)中存放其中断向量,和目标CPU拥有的本地APIC的标识,触发中断。IPI中断信号经由APIC总线传递到目标APIC,那个收到中断的APIC就向自己所属的CPU发送一个中断。

  485. Linux针对IA32的SMP系统定义了五种IPI:
  486. 1, CALL_FUNCTION_VECTOR:发往自己除外的所有CPU,强制它们执行指定的函数;
  487. 2, RESCHEDULE_VECTOR:使被中断的CPU重新调度;
  488. 3, INVLIDATE_TLB_VECTOR:使被中断的CPU废弃自己的TLB缓存内容。
  489. 4, ERROR_APIC_VECTOR:错误中断。
  490. 5, SPUROUS_APIC_VECTOR:假中断

上一篇:Linux内核对per-cpu变量的实现
下一篇:TCP SACK包格式