/*
*使用一个数组,来记录对于每个特定的CPU的变量的地址偏移量。
*有些CPU有自己独特的技术来实现。
*/
#ifndef __per_cpu_offset
extern unsigned long __per_cpu_offset[NR_CPUS];
#define per_cpu_offset(x) (__per_cpu_offset[x])
#endif
/*
*使用标准方式声明多CPU共存变量时,其实是声明了per_cpu__varname这个变量名的变量。
*/
#define per_cpu_var(var) per_cpu__##var
/*
* 加入偏移量来取得最终的某CPU变量的位置。
*
* S390有独有的技术来实现这种偏移功能。
*/
#ifndef SHIFT_PERCPU_PTR
#define SHIFT_PERCPU_PTR(__p, __offset) RELOC_HIDE((__p), (__offset))
#endif
/*
*RELOC_HIDE 使用ptr变量类型指针后加入相就的偏移量。
*/
#ifndef RELOC_HIDE
# define RELOC_HIDE(ptr, off) /
({ unsigned long __ptr; /
__ptr = (unsigned long) (ptr); /
(typeof(ptr)) (__ptr + (off)); })
#endif
我们首先来看一个功能很特殊的头文件: stringify.h
#ifndef __LINUX_STRINGIFY_H
#define __LINUX_STRINGIFY_H
/*
* 多级字符串化,可以使用宏的参数本身变为一个宏. 如,当编译-DFOO=bar时, __stringify(FOO)
* 将会其转化为宏"bar".可能有些编译器是不支持的。
*/
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#endif /* !__LINUX_STRINGIFY_H */
#ifdef CONFIG_X86_64
#define __percpu_seg gs
#define __percpu_mov_op movq
#else
#define __percpu_seg fs
#define __percpu_mov_op movl
#endif
#ifdef CONFIG_SMP
#define __percpu_arg(x) "%%"__stringify(__percpu_seg)":%P" #x
#define __my_cpu_offset percpu_read(this_cpu_off)
#else
#define __percpu_arg(x) "%P" #x
#endif
在多CPU环境下,__percpu_arg(x) 输出的内容是:
32bit CPU:
%%fs:%P#x
64bit CPU:
%%gs:%P#x
让我们再来看一段汇编代码,用来执行某一种操作,设计的比较精巧:
/*我们可以运行一条指令(两参数),第一个参数是val,第二参数是多CPU变量存储段寄存器fs定位地址,输出结果在var变量中*/
extern void __bad_percpu_size(void);
#define percpu_to_op(op, var, val) /
do { /
typedef typeof(var) T__; / gcc功能扩展,得到了一个变量的类型。
if (0) { /
T__ tmp__; /
tmp__ = (val); /
} /
switch (sizeof(var)) { /
case 1: /
asm(op "b %1,"__percpu_arg(0) /
: "+m" (var) /
: "qi" ((T__)(val))); /
break; /
case 2: /
asm(op "w %1,"__percpu_arg(0) /
: "+m" (var) /
: "ri" ((T__)(val))); /
break; /
case 4: /
asm(op "l %1,"__percpu_arg(0) /
: "+m" (var) /
: "ri" ((T__)(val))); /
break; /
case 8: /
asm(op "q %1,"__percpu_arg(0) /
: "+m" (var) /
: "re" ((T__)(val))); /
break; /
default: __bad_percpu_size(); /
} /
} while (0)
/*
* 当percpu_read_stable()允许多CPU变量被缓存时,当访问这一变量,具体的值将会被缓存,percpu_read() 使gcc装载缓存的变量
* percpu_read_stable() 是非常高效的,同时这种缓存也跨CPU的。
* get_current() 和 get_thread_info() 来得到用户信息的方式都为
* 单线程独立的方式,它就是由多CPU方式来实现的,因此在不同的CPU下都是稳定的。
*/
#define percpu_read(var) percpu_from_op("mov", per_cpu__##var, /
"m" (per_cpu__##var))
/*
* 必须是一个左操作数,如果不是,将有语法错误
*/
#define get_cpu_var(var) (*({ /
extern int simple_identifier_##var(void); /
preempt_disable(); /
&__get_cpu_var(var); }))
#define put_cpu_var(var) preempt_enable()
/*
* 进行无左操作数指令的优化函数
*
* var可以是cpu变量的全部和局部内容,如:char,int 或 long, percpu_read() 计算左值
*
* 这个函数保证了写重入是原子操作,这里用了原子指令,而没有使用重入保护。
*/
#ifndef percpu_read
# define percpu_read(var) /
({ /
typeof(per_cpu_var(var)) __tmp_var__; /
__tmp_var__ = get_cpu_var(var); /
put_cpu_var(var); /
__tmp_var__; /
})
#endif
/*
* smp_processor_id(): get the current CPU ID.
*
* if DEBUG_PREEMPT is enabled the we check whether it is
* used in a preemption-safe way. (smp_processor_id() is safe
* if it's used in a preemption-off critical section, or in
* a thread that is bound to the current CPU.)
*
* NOTE: raw_smp_processor_id() is for internal use only
* (smp_processor_id() is the preferred variant), but in rare
* instances it might also be used to turn off false positives
* (i.e. smp_processor_id() use that the debugging code reports but
* which use for some reason is legal). Don't use this to hack around
* the warning message, as your code might not work under PREEMPT.
*/
#ifdef CONFIG_DEBUG_PREEMPT
extern unsigned int debug_smp_processor_id(void);
# define smp_processor_id() debug_smp_processor_id()
#else
# define smp_processor_id() raw_smp_processor_id()
#endif
/*
* 计算当前CPU的变量的偏移量。
* 有此系统架构将提供__my_cpu_offset来提供高效性。
*/
#ifndef __my_cpu_offset
#define __my_cpu_offset per_cpu_offset(raw_smp_processor_id())
#endif
#ifdef CONFIG_DEBUG_PREEMPT
#define my_cpu_offset per_cpu_offset(smp_processor_id())
#else
#define my_cpu_offset __my_cpu_offset
#endif
/*
*可以看到per_cpu找出了某一个CPU的变量
*__get_cpu_var和__raw_get_cpu_var功能一样,得到当前CPU的偏移量。
*/
#define per_cpu(var, cpu) /
(*SHIFT_PERCPU_PTR(&per_cpu_var(var), per_cpu_offset(cpu)))
#define __get_cpu_var(var) /
(*SHIFT_PERCPU_PTR(&per_cpu_var(var), my_cpu_offset))
#define __raw_get_cpu_var(var) /
(*SHIFT_PERCPU_PTR(&per_cpu_var(var), __my_cpu_offset))