If your code needs to detect whether the current CPU is hotplug, you can refer to the following sample code
#include <linux/cpu.h>
static int foobar_cpu_callback (struct notifier_block *NFB,
unsigned long action, void *HCPU)
{
unsigned int cpu = (unsigned long) hcpu;
Switch (action) {
Case Cpu_online:
Case Cpu_online_frozen:
Foobar_online_action (CPU);
Break
Case Cpu_dead:
Case Cpu_dead_frozen:
Foobar_dead_action (CPU);
Break
}
return NOTIFY_OK;
}
static struct Notifier_block Foobar_cpu_notifier =
{
. Notifier_call = Foobar_cpu_callback,
};
Be sure to include <linux/cpu.h> or you will get an error
Second, call Register_cpu_notifier (&foobar_cpu_notifier) or register_hotcpu_notifier (&foobar_cpu_notifier); To register a notify so that when the CPU is online or dead it will call Foobar_cpu_callback
There is no difference between register_cpu_notifier and Register_hotcpu_notifier. The definition of Register_hotcpu_notifier is as follows:
#define Register_hotcpu_notifier (NB) register_cpu_notifier (NB)
Then foobar_online_action and foobar_dead_action can handle CPU online and cpu_dead.
int register_cpu_notifier (struct notifier_block *nb)
{
int ret;
Cpu_maps_update_begin ();
ret = Raw_notifier_chain_register (&cpu_chain, NB);
Cpu_maps_update_done ();
return ret;
}
Register_cpu_notifier is to add the previously defined notifier_call to the Cpu_chain list.
int Raw_notifier_chain_register (struct raw_notifier_head *nh,
struct Notifier_block *n)
{
Return Notifier_chain_register (&nh->head, N);
}
In Raw_notifier_chain_register, you get the head of the Cpu_chain list, and then you add N.
static int notifier_chain_register (struct notifier_block **nl,
struct Notifier_block *n)
{
while ((*NL)!= NULL) {
if (N->priority > (*NL)->priority)
Break
NL = & ((*NL)->next);
}
N->next = *NL;
Rcu_assign_pointer (*NL, N);
return 0;
}
Notifier_chain_register is prioritized when added to the Cpu_chain->head list, with priority from high to low.
When CPU hotplug occurs, Cpu_notify is invoked to notify events on the Cpu_chain list.
static int cpu_notify (unsigned long val, unsigned int cpu)
{
Return __cpu_notify (val, CPU,-1, NULL);
}
static int __cpu_notify (unsigned long val, unsigned int cpu, int nr_to_call,
int *nr_calls)
{
unsigned long mod = Cpuhp_tasks_frozen? cpu_tasks_frozen:0;
void *hcpu = (void *) (long) CPU;
int ret;
ret = __raw_notifier_call_chain (&cpu_chain, Val | mod, HCPU, Nr_to_call,
Nr_calls);
Return Notifier_to_errno (ret);
}
__cpu_notify gets the current HotPlug CPU number HCPU, and then calls __raw_notifier_call_chain
int __raw_notifier_call_chain (struct raw_notifier_head *nh,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
Return Notifier_call_chain (&nh->head, Val, V, Nr_to_call, nr_calls);
}
And registration is the same as getting head, which is the head of the list
static int Notifier_call_chain (struct notifier_block **nl,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
int ret = Notify_done;
struct Notifier_block *nb, *next_nb;
NB = Rcu_dereference_raw (*NL);
while (NB && Nr_to_call) {
NEXT_NB = Rcu_dereference_raw (Nb->next);
#ifdef config_debug_notifiers
if (Unlikely (!func_ptr_is_kernel_text (Nb->notifier_call)) {
WARN (1, "Invalid notifier called!");
NB = NEXT_NB;
Continue
}
#endif
ret = Nb->notifier_call (nb, Val, v);
if (nr_calls)
(*nr_calls) + +;
if (ret & notify_stop_mask) = = Notify_stop_mask)
Break
NB = NEXT_NB;
nr_to_call--;
}
return ret;
}
Will traverse the entire cpu_chain->head, respectively, call Notifier_call, this example we registered Notifier_call = = Foobar_cpu_callback, then NB = NEXT_NB to continue the iteration.
The registration notify must have been done in kernel space, which is callback's function address, either in kernel Buildin or in module, so notifier_call_chain can make this decision, The prerequisite is to open the config_debug_notifiers.
And Func_ptr_is_kernel_text is to determine whether callback's function address is in kernel Buildin or module.
int func_ptr_is_kernel_text (void *ptr)
{
unsigned long addr;
Addr = (unsigned long) dereference_function_descriptor (PTR);
if (Core_kernel_text (addr))
return 1;
return is_module_text_address (addr);
}
Since it is the debug option, the individual thinks it is best to print the symbol for the function address by%PF better, that is, change to the following
int func_ptr_is_kernel_text (void *ptr)
{
unsigned long addr;
Addr = (unsigned long) dereference_function_descriptor (PTR);
WARN (1, "addr =%pf", addr);
if (Core_kernel_text (addr))
return 1;
return is_module_text_address (addr);
}
If you want to run your callback on the CPU, you can refer to the following code, the above article to understand, the following code understanding is very simple
Cpu_notifier_register_begin ();
For_each_online_cpu (i) {
Foobar_cpu_callback (&foobar_cpu_notifier,
cpu_up_prepare, i);
Foobar_cpu_callback (&foobar_cpu_notifier,
cpu_online, i);
}
__register_cpu_notifier (&foobar_cpu_notifier);
Cpu_notifier_register_done ();