内核通知链为内核子系统之间的事件通知机制,内核中用链表来维护通知链。应用实例:
事件通知者:
o 定义通知链表头
static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
o 定义通知链注册函数,被通知者通过这个函数注册该通知链,通过pm_chain_head 链表头跟通知者关联起来
int register_pm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&pm_chain_head, nb);
}
o 定义通知函数, 链表头pm_chain_head所在链表里的每个struct_block里面的回调notifier_call 将被执行
int pm_notifier_call_chain(unsigned long val)
{
int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL);
return notifier_to_errno(ret);
}
被通知者:
o 定义 notifier_block
static struct notifier_block exynos_cpuidle_notifier = {
.notifier_call = exynos_cpuidle_notifier_event,
};
static int exynos_cpuidle_notifier_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
switch (event) { // 根据通知者通知的不同event做出处理
case PM_SUSPEND_PREPARE:
disable_hlt();
pr_debug("PM_SUSPEND_PREPARE for CPUIDLE\n");
return NOTIFY_OK;
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
enable_hlt();
pr_debug("PM_POST_SUSPEND for CPUIDLE\n");
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
o 注册notifier_block到pm_chain_head所在链表
register_pm_notifier(&exynos_cpuidle_notifier);
通知链技术可以概括为:事件的被通知者将事件发生时应该执行的操作通过函数指针方式保存在链表(通知链)中,然后当事件发生时通知者依次执行链表中每一个元素的回调函数完成通知。(回调函数)
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *); // 通知
struct notifier_block __rcu *next;
int priority;
};
通知链实现分析:
元素基础结构
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *); // 通知
struct notifier_block __rcu *next;
int priority; // 由注册者指定,被通知者注册时指定的优先级别, 高优先级回调被将被优先执行
};
分四种通知链
0 原子通知链( Atomic notifier chains):通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block __rcu *head;
};
1 可阻塞通知链( Blocking notifier chains):通知链元素的回调函数在进程上下文中运行,允许阻塞
struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block __rcu *head;
};
2 原始通知链( Raw notifier chains):对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护
struct raw_notifier_head {
struct notifier_block __rcu *head;
};
3 SRCU 通知链( SRCU notifier chains ):可阻塞通知链的一种变体
struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block __rcu *head;
};
以阻塞式通知链为例分析内核实现
o 注册
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *n)
{
int ret;
/*
* This code gets used during boot-up, when task switching is
* not yet working and interrupts must remain disabled. At
* such times we must not call down_write().
*/
if (unlikely(system_state == SYSTEM_BOOTING))
return notifier_chain_register(&nh->head, n);
down_write(&nh->rwsem); // 利用读写信号量实现阻塞
ret = notifier_chain_register(&nh->head, n);
up_write(&nh->rwsem);
return ret;
}
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (n->priority > (*nl)->priority) // 优先级排队,定位被通知者注册的notifier_block 将放在链表的哪个位置
break;
nl = &((*nl)->next);
}
n->next = *nl; // 被通知者的notifier_block 指针加入通知链
rcu_assign_pointer(*nl, n); // 利用rcu给notifier_block 加锁, rcu(read-copy-update) 给读时加锁,写时拷贝,读后更新
return 0;
}
o 通知
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v)
{
return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
}
int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
int ret = NOTIFY_DONE;
/*
* We check the head outside the lock, but if this access is
* racy then it does not matter what the result of the test
* is, we re-check the list after having taken the lock anyway:
*/
if (rcu_dereference_raw(nh->head)) {
down_read(&nh->rwsem);
ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
nr_calls);
up_read(&nh->rwsem);
}
return ret;
}
static int __kprobes 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) { // 循环遍历每个通知链notifier_block
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); // 执行每个通知链 notifier_block的notifier_call回调
if (nr_calls)
(*nr_calls)++;
if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
break;
nb = next_nb;
nr_to_call--;
}
return ret;
}