Linux内核通知链 使用和简析
1. 使用简述
通知链表是一个函数链表,链表上的每一个节点都注册了一个处理函数。当该chain对应的事件发生时(call chain),链表上所有节点对应的函数就会被执行。
定义
1、定义notifier head
通知链是一个链表,需要有一个链表头,后续的元素就可以陆续添加到这个链表中去
static RAW_NOTIFIER_HEAD(hello_chain);1
2、定义该通知链的注册方法
所谓注册就是根据优先级将被通知的元素添加到链表中去
static int register_hello_notifier(struct notifier_block *nb)
{
int err;
err = raw_notifier_chain_register(&hello_chain, nb);
if(err)
goto out;
out:
return err;
}
EXPORT_SYMBOL(register_test_notifier);
3 定义该通知链的事件通知方法
static int call_hello_notifiers(unsigned long val, void *v)
{
return raw_notifier_call_chain(&hello_chain, val, v);
}
EXPORT_SYMBOL(call_hello_notifiers);
通知事件注册
4 定义处理函数
int hello_notifier_event_handler(struct notifier_block *nb, unsigned long event, void *v)
{
printk("event %lu\n", event);
return NOTIFY_DONE;
}
/* define a notifier_block */
static struct notifier_block hello_init_notifier = {
.notifier_call = hello_notifier_event_handler,
}
5 注册
extern int register_hello_notifier(struct notifier_block *nb);
register_test_notifier(&hello_init_notifier);
发出通知事件
6 发出通知事件
extern int call_test_notifiers(unsigned long val, void *v);
#define HEELO_EVENT 0x1612
call_test_notifiers(HEELO_EVENT, "no_use");
2. 详述
Linux 内核通知链有四种类型,根据回调函数可运行的上线文限制做出了下面的区分
原子通知链 Atomic notifier chains: Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block.
可阻塞通知链 Blocking notifier chains: Chain callbacks run in process context. Callouts are allowed to block.
原始通知链 Raw notifier chains: There are no restrictions on callbacks, registration, or unregistration. All locking and protection must be provided by the caller.
SRCU通知链 SRCU notifier chains: A variant of blocking notifier chains, with the same restrictions.
~/include/linux/notifier.h
typedef int (*notifier_fn_t)(struct notifier_block *nb,
unsigned long action, void *data);
struct notifier_block {
notifier_fn_t notifier_call;
struct notifier_block __rcu *next;
int priority;
};
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block __rcu *head;
};
struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block __rcu *head;
};
struct raw_notifier_head {
struct notifier_block __rcu *head;
};
struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block __rcu *head;
};
为了初始化链表头方便,提供了宏定义
需要注意的是SRCU的链表头需要动态初始化
#define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \
spin_lock_init(&(name)->lock); \
(name)->head = NULL; \
} while (0)
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \
init_rwsem(&(name)->rwsem); \
(name)->head = NULL; \
} while (0)
#define RAW_INIT_NOTIFIER_HEAD(name) do { \
(name)->head = NULL; \
} while (0)
/* srcu_notifier_heads must be initialized and cleaned up dynamically */
extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
#define srcu_cleanup_notifier_head(name) \
cleanup_srcu_struct(&(name)->srcu);
#define ATOMIC_NOTIFIER_INIT(name) { \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
.head = NULL }
#define BLOCKING_NOTIFIER_INIT(name) { \
.rwsem = __RWSEM_INITIALIZER((name).rwsem), \
.head = NULL }
#define RAW_NOTIFIER_INIT(name) { \
.head = NULL }
/* srcu_notifier_heads cannot be initialized statically */
#define ATOMIC_NOTIFIER_HEAD(name) \
struct atomic_notifier_head name = \
ATOMIC_NOTIFIER_INIT(name)
#define BLOCKING_NOTIFIER_HEAD(name) \
struct blocking_notifier_head name = \
BLOCKING_NOTIFIER_INIT(name)
#define RAW_NOTIFIER_HEAD(name) \
struct raw_notifier_head name = \
RAW_NOTIFIER_INIT(name)
notifier_chain_register
无论是 ATOMIC BLOCKING RAW SRCU 的注册,都是对notifier_chain_register的封装, 以实现其特性
注册的过程就是讲新的链表元素按优先级添加到链表中去,大部分时候都是用的默认优先级
~/kernel/notifier.c
/*
* Notifier chain core routines. The exported routines below
* are layered on top of these, with appropriate locking added.
*/
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;
}12345678910111213141516171819
notifier_call_chain
所谓call chain 就是遍历链表,执行所有元素的notifier_call,并传递相关参数
/**
* notifier_call_chain - Informs the registered notifiers about an event.
* @nl: Pointer to head of the blocking notifier chain
* @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function
* @nr_to_call: Number of notifier functions to be called. Don't care
* value of this parameter is -1.
* @nr_calls: Records the number of notifications sent. Don't care
* value of this field is NULL.
* @returns: notifier_call_chain returns the value returned by the
* last notifier function called.
*/
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;
}