setup_per_cpu_areas()初始化per-cpu数据。
static void __init setup_per_cpu_areas(void) { unsigned long size, i; char *ptr; unsigned long nr_possible_cpus = num_possible_cpus(); /* Copy section for each CPU (we discard the original) */ size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE); ptr = alloc_bootmem_pages(size * nr_possible_cpus); for_each_possible_cpu(i) { __per_cpu_offset[i] = ptr - __per_cpu_start; memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start); ptr += size; } }
下面的三个变量在vmlinux.lds中定义
__per_cpu_load = .; __per_cpu_start = .; *(.data.percpu.page_aligned) *(.data.percpu) *(.data.percpu.shared_aligned) __per_cpu_end = .;
在init/main.c中定义如下变量
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly; EXPORT_SYMBOL(__per_cpu_offset);
PERCPU_ENOUGH_ROOM是为每个cpu分配的副本空间大小,其中__per_cpu_end-__per_cpu_start为静态分配的percpu变量空间,
PERCPU_MODULE_RESERVE=8K位动态分配的percpu变量空间。
#define PERCPU_ENOUGH_ROOM \ ALIGN(__per_cpu_end - __per_cpu_start, SMP_CACHE_BYTES) + PERCPU_MODULE_RESERVE #define PERCPU_MODULE_RESERVE (8 << 10)
percpu变量静态声明
#define DEFINE_PER_CPU(type, name) DEFINE_PER_CPU_SECTION(type, name, "") #define DEFINE_PER_CPU_SECTION(type, name, section) \ __attribute__((__section__(PER_CPU_BASE_SECTION section))) PER_CPU_ATTRIBUTES PER_CPU_DEF_ATTRIBUTES __typeof__(type) per_cpu__##name #define PER_CPU_BASE_SECTION ".data.percpu"
由于静态变量的特殊性,没有头文件声明percpu变量,用DEFINE_PER_CPU宏定义的变量想在其它C文件中使用,需要使用DECLARE_PER_CPU宏声明
#define DECLARE_PER_CPU(type, name) \ DECLARE_PER_CPU_SECTION(type, name, "") #define DECLARE_PER_CPU_SECTION(type, name, section) extern __attribute__((__section__(PER_CPU_BASE_SECTION section))) PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
使用per_cpu宏获取静态存储的percpu变量
#define per_cpu(var, cpu) \ (*SHIFT_PERCPU_PTR(&per_cpu_var(var), per_cpu_offset(cpu))) #define per_cpu_offset(x) (__per_cpu_offset[x]) #define per_cpu_var(var) per_cpu__##var #define SHIFT_PERCPU_PTR(__p, __offset) RELOC_HIDE((__p), (__offset)) #define RELOC_HIDE(ptr, off) \ ({ unsigned long __ptr; __asm__ ("" : "=r"(__ptr) : "0"(ptr)); (typeof(ptr)) (__ptr + (off)); })