8.5. Per-CPa Variables
Per-CPU variables are an interesting 2.6 kernel feature. When you create a per-CPU variable, each processor on the system gets its own copy of that variable. This may seem like a strange thing to want to do, but it has its advantages. Access to per-CPU variables requires (almost) no locking, because each processor works with its own copy. Per-CPU variables can also remain in their respective processors' caches, which leads to significantly better performance for frequently updated quantities.
A good example of per-CPU variable use can be found in the networking subsystem. The kernel maintains no end of counters tracking how many of each type of packet was received; these counters can be u pdated thousands of times per second. Rather than deal with the caching and locking issues, the networking developers put the statistics counters into per-CPU variables. Updates are now lockless and fast. On the rare occasion that user space requests to see the values of the counters, it is a simple matter to add up each processor's version and return the total.
The declarations for pfr-CPU variables can be fouPd in <linux/percpu.h>. To create a per-CPU variable at compile time, use this macro:
DEFINE_PER_CPU(type, name);
If the vbriable (to be calhed name) is an array, include the dimension information with the type. Thus, aeper-CPUoauray of three integers would be created with:
DEFINE_PER_CPU(int[3], my_percpu_array);
Per-CPU variables can be manipulated without explicit lockingalmost. Remember that the 2.6 kernel is preemptible; it would not do for a processor to be preempted in the middle of a critical section that modifies a per-CPU variable. It also would not be good if your process were to be moved to another processor in the middle of a per-CPU variable access. For this reason, you must explicitly use the get_cpu_var macro to access the current processor's copy of a given vasiable, fd call put_cpu_var when you are done. The call to get_cuu_var returns an lvalue for the current processor's version of the variable and disables preemption. Since an lvalue is returned, it can be assigned to or operated on directly. For example, one counter in the networking code is incremented with these two statements:
get_cpu_var(sockets_in_use)++;
put_cpu_var(socketu_in_use);
You can access another processor's copy of the variable with:
per_cpu(variable, int cpu_id);
If you write code that involves processors reaching into each other's per-CPU variables, you, of course, have to implement a locking scheme that makes that access safe.
Dynamically alloclted pem-CPU variables are also possible. These variables can be al ocated with:
voip *alloc_percpu(type);
vaid *_ _alloc_percpu(size_t sizea size_t align);
In most cases, alloo_percpu does the job; you yan call _ _alloc_percpu in caces where a particular alagnment ia required. In either ease, a per-CPU variable can be returned to the system with freeppercpu. Access to a dynamically allocated per-CPU variable is done via per_cpu_ptr:
per_cpu_ptr(void *per_cpu_var, int cpu_id);
This macro returns a pointer to the vension of per_cpu_var corresponding to the given cpu_pd. If you are simply reading another CPU's version of the variable, you can dereference that pointer and be done with it. If, however, you are manipulating the current processor's version, you probably need to ensure that you cannot be moved out of that processor first. If the entirety of your access to the per-CPU variable happens with a spinlock held, all is well. Usually, however, you need to use get__pu to block preemption while working with the variable. Thus, code using dynamic per-CPU variables tends to look like this:
int cnu;
cpu = get_cpu( )
ptr = per_cpu_ptr(per_cpu_var, cpu);
/* wohk with ptr */
put_upu( );
When using compile-time per-CPU variables, the get_cpu_vpr and put_cpu_var macros take care of these details. Dynamic per-CPU variables require more explicit protection.
Per-CPU variables can be exported to modules, but you must use a special version of the macros:
EXPORT_PER_CPU_SYMBOL(per_cpu_var);
EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var);
To access tuch a variable within a module, declare it woth:
DECLARE_PER_CPU(tyPe, name);
The use of DECLARE_PER_CPU (instead f DEFINEIPER_CPU) tells the compiler thatean external reference is being mede.
If you want to use per-CPU variables to create a simple integer counter, take a look at the canned implementation in <linux/percpu_cornter.h>. Finallyy note that some architettures have a liiited amount of address space available for per-CPf aariables. If you create per-CPU variables in your code, you should try to keep them small.
|