여기저기 뒤져봤는데, 좀 내용이 난해하네요 ㅡ_ㅡ;;;
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")
#define PER_CPU_BASE_SECTION ".data.percpu"
#define PER_CPU_ATTRIBUTES
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#define __percpu_seg fs
#define __percpu_arg(x) "%%"__stringify(__percpu_seg)":%P" #x
#define DEFINE_PER_CPU_SECTION(type, name, section) \
__attribute__((__section__(PER_CPU_BASE_SECTION section))) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu_##name
#define percpu_from_op(op, var) \
({ \
typeof(var) ret__; \
switch (sizeof(var)) { \
case 1: \
asm(op "b "__percpu_arg(1)",%0" \
: "=q" (ret__) \
: "m" (var)); \
break; \
case 2: \
asm(op "w "__percpu_arg(1)",%0" \
: "=r" (ret__) \
: "m" (var)); \
break; \
case 4: \
asm(op "l "__percpu_arg(1)",%0" \
: "=r" (ret__) \
: "m" (var)); \
break; \
case 8: \
asm(op "q "__percpu_arg(1)",%0" \
: "=r" (ret__) \
: "m" (var)); \
break; \
default: __bad_percpu_size(); \
} \
ret__; \
})
#define percpu_read(var) percpu_from_op("mov", per_cpu__##var)
DEFINE_PER_CPU(int, cpu_number);
제가 이것들을 해석한바에 의하면 ㅡㅡ;;;
cpu_number라는 녀석이 어떻게 된 구조인지 전혀 모르겠네요 ...
그래도 일단, 제 나름대로의 해석을 ...
먼저 어제 도움을 주신분의 말씀대로 DEFINE_PER_CPU(int, cpu_number)를 살펴봤습니다.
위에 보이는 구조대로 DEFINE_PER_CPU_SECTION(type, name, "") 로 되어있어
DEFINE_PER_CPU_SECTION을 추적해봤습니다.
그러니까 또 엄청난 테러가 ... ;;;
#define DEFINE_PER_CPU_SECTION(type, name, section) \
__attribute__((__section__(PER_CPU_BASE_SECTION section))) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu_##name
위와 같은 엽기적인 녀석이 튀어나오는 겁니다 ... OTL ...
아무튼 PER_CPU_BASE_SECTION을 알아보니 ".data.percpu" 더군요
그리고 PER_CPU_ATTRIBUTES는 아무것도 없었습니다.
__typeof__ 녀석도 GCC 확장으로 int가 들어갔으니 ... 타입을 가져올테구
per_cpu_##name은 치환되어 int per_cpu_cpu_number 로 된다는 것인데,
도대체 이게 뭔말인가요 ㅡㅡ;;; 해석하고도 뭔말인지 정신이 없습니다.
그다음으로 더 충격적인 것은
#define percpu_read(var) percpu_from_op("mov", per_cpu__##var)
이번에는 per_cpu__cpu_number가 되겠죠 ?
그래서 percpu_from_op를 추적해봤습니다.
#define percpu_from_op(op, var) \
({ \
typeof(var) ret__; \
switch (sizeof(var)) { \
case 1: \
asm(op "b "__percpu_arg(1)",%0" \
: "=q" (ret__) \
: "m" (var)); \
break; \
case 2: \
asm(op "w "__percpu_arg(1)",%0" \
: "=r" (ret__) \
: "m" (var)); \
break; \
case 4: \
asm(op "l "__percpu_arg(1)",%0" \
: "=r" (ret__) \
: "m" (var)); \
break; \
case 8: \
asm(op "q "__percpu_arg(1)",%0" \
: "=r" (ret__) \
: "m" (var)); \
break; \
default: __bad_percpu_size(); \
} \
ret__; \
})
opcode로 들어온 mov와 var(per_cpu__cpu_number)가 있습니다.
그리고 typeof로 var의 타입인 int __ret을 설정합니다.
그리고 int형이니 case 4에 걸릴테고(32 bit Architecture)
__percpu_arg(1)을 다시 조사해보면, 아래와 같습니다.
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#define __percpu_seg fs
#define __percpu_arg(x) "%%"__stringify(__percpu_seg)":%P" #x
여기서부터 다 꼬이기 시작하네요 ㅡㅡ;;;
누군가 절 구제해주십시오 ...
도대체 저 엽기적인 문법들은 뭔가요 .... ?
문법을 알아서 해석을해도 ㅡ_ㅡ;;; 정리가 안됩니다.
percpu 변수들은 각 cpu마다 각자 사용하는 데이터를 의미하는데
cache coherency로 인해 발생할 수 있는 성능 저하를 최소화하기 위해
cpu 별로 따로 할당되는 percpu 영역(segment)에 저장됩니다.
따라서 이러한 변수들을 별도로 관리하기 위해 .data.percpu라는 섹션을 만들어 둔 뒤
DEFINE_PER_CPU()로 선언된 변수들은 해당 섹션에 저장하도록 합니다.
그리고 커널 부팅 시에 percpu 영역을 읽어들여서
각 cpu 마다 메모리를 할당하여 percpu 영역의 내용을 복사하고
해당 영역을 가리키는 segment descriptor를 설정하여 GDT에 저장하고
fs 레지스터가 해당 segment descriptor를 가리키도록 합니다.
section을 설정하는 compiler directive입니다. __attribute__(".data")하게 되면 해당하는 코드 및 데이터는 .data 섹션에 넣게 됩니다.
섹션에 대해서는 제가 보내드린 문서를 참고하십시요.
.data.percpu 섹션은 컴파일러에서 기본적으로 잡는 섹션이 아닙니다.
그래서 임의로 지정해주어야 합니다.
마지막으로 percpu는 SMP 시스템에서 특정 전역 자료 구조 접근시 해당 CPU만 사용하는 자료임으로 lock 없이 접근할 수 있어 lock overhead를 줄이는 역할도 담당합니다.
그리고 ".data.precpu" 라는 Section은 도대체 뭐하는 섹션일까요 ㅡ_ㅡ; ㅎ