Skip to content
Advertisement

The implementation of Linux kernel current macro

Generally speaking, if we want to use current macro in Linux kernel, we should:

#include <asm/current.h>

but there is a asm-generic version:

#include <asm-generic/current.h>

the asm version implements the current macro through per-cpu variable, but asm-generic version implements the current macro through thread_info, these two are totally different. Linux kernel headers’ organization says we should use asm version, which include asm/current.h, but so many blogs or books says x86 use asm-generic version to implement current macro, including Linux Kernel Development, 3rd, 3 Process Management, Storing the Process Descriptor. So, which version the x86 Linux kernel really use, asm or asm-generic? How could I make sure which version the Linux kernel really use?

Advertisement

Answer

The correct header to use is asm/current.h, do not use asm-generic. This applies to anything under asm really. Headers in the asm-generic folder are provided (as the name suggests) as a “generic” default implementation of macros/functions, then each architecture /arch/xxx has its own asm include folder, where if needed it can define the same macros/functions in an architecture-specific way.

This is done both because it could be actually needed (some archs might have an implementation that is not compatible with the generic one) and for performance since there might be a better and more optimized way of achieving the same result under a specific arch.

Indeed, if we look at how each arch defines get_current() or get_current_thread_info() we can see that some of them (e.g. alpha, spark) keep a reference to the current task in the thread_info struct and keep a pointer to the current thread_info in a register for performance. Others directly keep a pointer to current in a register (e.g. powerpc 32bit), and others define a global per-cpu variable (e.g. x86). On x86 in particular, the thread_info struct doesn’t even have a pointer to the current task, it’s a very simple 16-byte structure made to fit in a cache line for performance.

// example from /arch/powerpc/include/asm/current.h

/*
 * We keep `current' in r2 for speed.
 */
register struct task_struct *current asm ("r2");

How could I make sure which version the Linux kernel really use?

Well, let’s just take a simple look:

$ rg '#include.+current.h' | cat
security/landlock/ptrace.c:#include <asm/current.h>       
security/landlock/syscalls.c:#include <asm/current.h>     
sound/pci/rme9652/hdsp.c:#include <asm/current.h>         
sound/pci/rme9652/rme9652.c:#include <asm/current.h>      
net/ipv4/raw.c:#include <asm/current.h>                 
net/core/dev.c:#include <asm/current.h>                   
ipc/msg.c:#include <asm/current.h>                        
fs/quota/quota.c:#include <asm/current.h>                 
drivers/staging/media/atomisp/pci/hmm/hmm_bo.c:#include <asm/current.h>                                             
fs/jfs/ioctl.c:#include <asm/current.h>                   
fs/hugetlbfs/inode.c:#include <asm/current.h>             
drivers/parport/daisy.c:#include <asm/current.h>          
...

As you can see asm/current.h is the only header actually used.

We can also see that (as of v5.14 at least) only arc seems to be using the “generic” version:

$ rg '#include.+generic.+current.h' | cat     
arch/arc/include/asm/current.h:#include <asm-generic/current.h>

many blogs or books says x86 use asm-generic version to implement current macro, including Linux Kernel Development, 3rd

I can only speculate that these resources were written a long time ago and based on pretty old kernel versions, which at the time of writing might have used a different include system (maybe x86 used to use the generic version as well). If not, then those resources are most probably wrong.

9 People found this is helpful
Advertisement