percpu相关-SHIFT_PERCPU_PTR this_cpu_ptr(ptr)等
include/linux/percpu-defs.h
1、 SHIFT_PERCPU_PTR
230 ~ 231 定义了 SHIFT_PERCPU_PTR ,输入 __p 和 __offset ,直接使用 RELOC_HIDE 对两者进行相加。 RELOC_HIDE 见 https://www.cnblogs.com/zhangzhiwei122/p/16053900.html
2、per_cpu_ptr(ptr,cpu)
233 ~ 236 利用 per_cpu_offset 将cpu 转换为 offset,然后使用 shift_percpu_ptr 得到 offset 之后的对象的指针。 per_cpu_offset 定义在 include/asm-generic/percpu.h
3、raw_cpu_ptr(ptr)
使用 当前cpu 的offset 来偏移 ptr
4、this_cpu_ptr(ptr)
raw_cpu_ptr 的直接调用。或 CONFIG_DEBUG_PREEMPT
225/* 226 * Add an offset to a pointer but keep the pointer as-is. Use RELOC_HIDE() 227 * to prevent the compiler from making incorrect assumptions about the 228 * pointer value. The weird cast keeps both GCC and sparse happy. 229 */ 230#define SHIFT_PERCPU_PTR(__p, __offset) \ 231 RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset)) 232 233#define per_cpu_ptr(ptr, cpu) \ 234({ \ 235 __verify_pcpu_ptr(ptr); \ 236 SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu))); \ 237}) 238 239#define raw_cpu_ptr(ptr) \ 240({ \ 241 __verify_pcpu_ptr(ptr); \ 242 arch_raw_cpu_ptr(ptr); \ 243}) 244 245#ifdef CONFIG_DEBUG_PREEMPT 246#define this_cpu_ptr(ptr) \ 247({ \ 248 __verify_pcpu_ptr(ptr); \ 249 SHIFT_PERCPU_PTR(ptr, my_cpu_offset); \ 250}) 251#else 252#define this_cpu_ptr(ptr) raw_cpu_ptr(ptr) 253#endif
include/asm-generic/percpu.h
1、per_cpu_offset
使用 __per_cpu_offset 数组,得到 指定cpu 的offset
21 行 - per_cpu_offset ,以 cpu 为 index, 取 __per_cpu_offset 数组的值
11/* 12 * per_cpu_offset() is the offset that has to be added to a 13 * percpu variable to get to the instance for a certain processor. 14 * 15 * Most arches use the __per_cpu_offset array for those offsets but 16 * some arches have their own ways of determining the offset (x86_64, s390). 17 */ 18#ifndef __per_cpu_offset 19extern unsigned long __per_cpu_offset[NR_CPUS]; 20 21#define per_cpu_offset(x) (__per_cpu_offset[x]) 22#endif
my_cpu_offset
30 ~ 37 ,利用 raw_smp_processor_id 得到 当前运行 cpu id [0,1,2,……] ,然后使用 per_cpu_offset 得到 当前cpu 变量区域 的offset .
arch_raw_cpu_ptr(ptr)
43 ~ 45 利用 my_cpu_offset 得到 当前 cpu 的offset,然后使用 SHIFT_PERCPU_PTR (ptr, offset)对 ptr 进行偏移。
24/* 25 * Determine the offset for the currently active processor. 26 * An arch may define __my_cpu_offset to provide a more effective 27 * means of obtaining the offset to the per cpu variables of the 28 * current processor. 29 */ 30#ifndef __my_cpu_offset 31#define __my_cpu_offset per_cpu_offset(raw_smp_processor_id()) 32#endif 33#ifdef CONFIG_DEBUG_PREEMPT 34#define my_cpu_offset per_cpu_offset(smp_processor_id()) 35#else 36#define my_cpu_offset __my_cpu_offset 37#endif 38 39/* 40 * Arch may define arch_raw_cpu_ptr() to provide more efficient address 41 * translations for raw_cpu_ptr(). 42 */ 43#ifndef arch_raw_cpu_ptr 44#define arch_raw_cpu_ptr(ptr) SHIFT_PERCPU_PTR(ptr, __my_cpu_offset) 45#endif
注意:asm-generic/percpu.h 之所以是 generic 的,就是可以被 特定 arch 实现覆盖的。 30 ~ 32 中,只有等 __my_cpu_offset 没有arch 实现时,采用 generic 的实现,arm64下面有自己的实现
__my_cpu_offset 在arm64下
30 行的 #ifndef __my_cpu_offset , 在arm64 下面这个宏被定义 。
arch/arm64/include/asm/percpu.h
14 ~ 20 行,set_my_cpu_offset 是,直接写入到 CPU 的 tpidr_el1 寄存器里面。
31 ~ 44 ,__my_cpu_offset 读取时,直接从 cpu 的 tpidr_el1 寄存器里面读出。
tpidr_el1 寄存器信息,见 https://www.cnblogs.com/zhangzhiwei122/p/16051676.html 最下面
或
14static inline void set_my_cpu_offset(unsigned long off) 15{ 16 asm volatile(ALTERNATIVE("msr tpidr_el1, %0", 17 "msr tpidr_el2, %0", 18 ARM64_HAS_VIRT_HOST_EXTN) 19 :: "r" (off) : "memory"); 20} 30 31static inline unsigned long __kern_my_cpu_offset(void) 32{ 33 unsigned long off; 34 35 /* 36 * We want to allow caching the value, so avoid using volatile and 37 * instead use a fake stack read to hazard against barrier(). 38 */ 39 asm(ALTERNATIVE("mrs %0, tpidr_el1", 40 "mrs %0, tpidr_el2", 41 ARM64_HAS_VIRT_HOST_EXTN) 42 : "=r" (off) : 43 "Q" (*(const unsigned long *)current_stack_pointer)); 44 45 return off; 46} 47 48#ifdef __KVM_NVHE_HYPERVISOR__ 49#define __my_cpu_offset __hyp_my_cpu_offset() 50#else 51#define __my_cpu_offset __kern_my_cpu_offset() 52#endif