I was debugging linux boot and tried to understand how these percpu variables work in arm64. For test, I added a function called read_pkcontext1 which returns the percpu variable printk_context. (This value is used for printk) And I found something I can't understand.
(this is from linux 5.4.21)
==== kernel/printk/printk_safe.c ====
int read_pkcontext1(void) /* function I added for test */
{
return this_cpu_read(printk_context);
}
==== include/linux/percpu-defs.h ====
/*
* Operations with implied preemption/interrupt protection. These
* operations can be used without worrying about preemption or interrupt.
*/
#define this_cpu_read(pcp) __pcpu_size_call_return(this_cpu_read_, pcp)
==== include/linux/percpu-defs.h ====
#define __pcpu_size_call_return(stem, variable) \
({ \
typeof(variable) pscr_ret__; \
__verify_pcpu_ptr(&(variable)); \
switch(sizeof(variable)) { \
case 1: pscr_ret__ = stem##1(variable); break; \
case 2: pscr_ret__ = stem##2(variable); break; \
case 4: pscr_ret__ = stem##4(variable); break; \
case 8: pscr_ret__ = stem##8(variable); break; \
default: \
__bad_size_call_parameter(); break; \
} \
pscr_ret__; \
})
This is result of aarch64-none-elf-objdump -S vmlinux for read_pkcontext1 function and the used funtions inside (with optimization off).
ffffffc0100f0dc0 <read_pkcontext1>:
void write_pkcontext(void);
#pragma GCC push_options
#pragma GCC optimize ("O0")
int read_pkcontext1(void)
{
ffffffc0100f0dc0: a9bd7bfd stp x29, x30, [sp, #-48]!
ffffffc0100f0dc4: 910003fd mov x29, sp
return this_cpu_read(printk_context);
ffffffc0100f0dc8: f9000fff str xzr, [sp, #24]
ffffffc0100f0dcc: 52800020 mov w0, #0x1 // #1
ffffffc0100f0dd0: 94000018 bl ffffffc0100f0e30 <__preempt_count_add>
... skip ...
ffffffc0100f0e30 <__preempt_count_add>:
ffffffc0100f0e30: d5384101 mrs x1, sp_el0
ffffffc0100f0e34: b9401022 ldr w2, [x1, #16]
pc += val;
ffffffc0100f0e38: 0b020000 add w0, w0, w2
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
ffffffc0100f0e3c: b9001020 str w0, [x1, #16]
}
ffffffc0100f0e40: d65f03c0 ret
In the code above, it calls __preempt_count_add with w0 = #1 (incrementing preemption count), and __preempt_count_add functions is adding the value (w0) to a variable at sp + #16 and writes it back. So this variable in stack looks like the preemption count. (I guess this prevents preemption). My quetion is : when was this value in stack defined and initialized? I couldn't find it in the linux source. (using qemu, I see this value is seen to be 1 and incremented to 2 after the __preempt_count_add. Of course it is decrement back to 1 after the percpu variable access.)