多CPU支持下的 percpu 变量分析

/*

 *使用一个数组,来记录对于每个特定的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))

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值