内核通知链

linux内核提供通知链机制,用来提供一种异步通知机制。实现原理是将一些异步事件的回调函数链接到一个链表头上,在某种特定情况下,通过遍历该链表以调用所有注册到该链表上的事件回调函数。

通知链类型

linux内核定义了以下四种通知链:

Atomic通知链:在中断/原子操作上下文中调用通知回调。通知块回调函数不可以阻塞。

1
2
3
4
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block __rcu *head;
};

blocking通知链:在进程上下文中的通知,允许通知块阻塞。

1
2
3
4
struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block __rcu *head;
};

raw通知:通知块回调不做任何限制,所有的锁和保护由调用者提供。

1
2
3
struct raw_notifier_head {
struct notifier_block __rcu *head;
};

SRCU(Sleepable Read-Copy Update)通知:是一种Blocking通知链的变体,其限制和blocking通知链相同。用Sleepable Read-Copy Update代替了阻塞通知链中的读写信号量。具有开销小的优势,常用于通知块需要频繁调用,并且通知块基本不会删除的场景。

struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block __rcu *head;
};

通知链上的实例

Linux内核以一个notifier_block结构定义一个通知块:

1
2
3
4
5
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block __rcu *next;
int priority;
};

其中:

1
2
3
notifier_call是该通知实例的回调函数。
next指向该通知块所属通知链的下一个通知块。
priority表示该通知实例的优先级。

通知链定义和初始化接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 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)

通知链注册和取消注册接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
struct notifier_block *nb);
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
struct notifier_block *nb);

extern int blocking_notifier_chain_cond_register(
struct blocking_notifier_head *nh,
struct notifier_block *nb);

extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
struct notifier_block *nb);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
struct notifier_block *nb);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
struct notifier_block *nb);

通知链执行实例接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v);
extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v);
extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v);
extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v);
extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
unsigned long val, void *v, int nr_to_call, int *nr_calls);

通知链的应用

以usb总线为例说明,usb子系统初始化的时候注册了usb总线,在总线注册的时候初始化了总线的通知链。

1
2
3
4
5
6
7
8
9
10
int __bus_register(struct bus_type *bus, struct lock_class_key *key)
{
/*
* ...
*/
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
/*
* ...
*/
}

接着向usb总线通知链上注册了通知实例usb_bus_nb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static struct notifier_block usb_bus_nb = {
.notifier_call = usb_bus_notify,
};
...
/*usb_init*/
static int __init usb_init(void)
{
/*
* ...
*/
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
if (retval)
goto bus_notifier_failed;
/*
* ...
*/
}

由其回调函数实现可知该通知实例的目的是在usb设备热插拔的时候在sysfs下创建和删除设备或接口文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* Notifications of device and interface registration
*/
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;

switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
if (dev->type == &usb_device_type)
(void) usb_create_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
usb_create_sysfs_intf_files(to_usb_interface(dev));
break;

case BUS_NOTIFY_DEL_DEVICE:
if (dev->type == &usb_device_type)
usb_remove_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
usb_remove_sysfs_intf_files(to_usb_interface(dev));
break;
}
return 0;
}

当插入或拔出usb设备时,会通过usb_notify_add_device或者usb_notify_remove_device调用usb总线通知链上的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static int generic_probe(struct usb_device *udev)
{
int err, c;

/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
if (usb_device_is_owned(udev))
; /* Don't configure if the device is owned */
else if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
/* This need not be fatal. The user can try to
* set other configurations. */
}
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);

return 0;
}

上面代码中usb_notify_add_device函数实现如下:

1
2
3
4
void usb_notify_add_device(struct usb_device *udev)
{
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
}