Kernel之通知链

背景

最近在调触摸屏,看代码时偶然看到了通知链相关的代码,加上之前刚好了解了一点通知链相关的东东,所以特此记录下。
在触摸屏驱动中的主要功能就是在开关屏的时候通知TP进行resume和suspend操作,大致的伪代码如下,以我看的elan的TP驱动为例:

...

/* 通知链的事件回调函数,收到通知TP进行resume和suspend操作 */
static int fb_notifier_callback(struct notifier_block *self,
				unsigned long event, void *data)
{
	struct fb_event *evdata = data;
	int *blank;
	struct elan_ts_data *ts =
		container_of(self, struct elan_ts_data, fb_notif);

	if (evdata && evdata->data && event == FB_EVENT_BLANK && ts &&
	    ts->client) {
		blank = evdata->data;
		if (*blank == FB_BLANK_UNBLANK)
			elan_ts_resume(&ts->client->dev);
		else if (*blank == FB_BLANK_POWERDOWN)
			elan_ts_suspend(&ts->client->dev);
	}

	return 0;
}

/* 
	在probe函数中注册fb client,而 fb_register_client()函数实现的其实就是通知链注册:
	int fb_register_client(struct notifier_block *nb)
	{
		return blocking_notifier_chain_register(&fb_notifier_list, nb);
	}

	在开关屏的时候会调用 fb_notifier_call_chain(FB_EVENT_BLANK, &event) 来触发通知:
	int fb_notifier_call_chain(unsigned long val, void *v)
	{
		return blocking_notifier_call_chain(&fb_notifier_list, val, v);
	}

	//drivers/video/fbdev/core/fb_notify.c
*/
static int elan_ts_probe(...)
{
	...
	ts->fb_notif.notifier_call = fb_notifier_callback;
	err = fb_register_client(&ts->fb_notif);
	if (err)
		dev_err(&client->dev, "[FB]Unable to register fb_notifier: %d",
			err);
	...
}

/* 在remove函数中注销fb client */
static int elan_ts_remove(struct i2c_client *client)
{
	...
	fb_unregister_client(&ts->fb_notif);
	...
}
...

所以从上面的例子就可以很好的看出什么是通知链,为什么会有通知链?

Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain)。
通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。主要代码位于kernel/notifier.c中,对应的头文件为include/linux/notifier.h
事件通知链表是一个事件处理函数的列表,每个通知链都与某个或某些事件有关,当特定的事件发生时,就调用相应的事件通知链中的回调函数,进行相应的处理。

类型

针对不同的使用场景,有以下4种通知链:

  • 原始通知链
  • 原子通知链
  • 可阻塞通知链
  • SRCU通知链

原始通知链

对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护

对应API:

/* 定义notifier head并静态初始化 */
#define RAW_NOTIFIER_HEAD(name)					\
	struct raw_notifier_head name =				\
		RAW_NOTIFIER_INIT(name)
/* 只动态初始化 */
#define RAW_INIT_NOTIFIER_HEAD(name) do {	\
		(name)->head = NULL;		\
	} while (0)

/* 注册 */
int raw_notifier_chain_register(struct raw_notifier_head *nh,
		struct notifier_block *nb);
/* 注销 */
int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
		struct notifier_block *nb);
/* 通知 */
int raw_notifier_call_chain(struct raw_notifier_head *nh,
		unsigned long val, void *v);

原子通知链

通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞

对应API:

/* 定义notifier head并静态初始化 */
#define ATOMIC_NOTIFIER_HEAD(name)				\
	struct atomic_notifier_head name =			\
		ATOMIC_NOTIFIER_INIT(name)
/* 只动态初始化 */
#define ATOMIC_INIT_NOTIFIER_HEAD(name) do {	\
		spin_lock_init(&(name)->lock);	\
		(name)->head = NULL;		\
	} while (0)

/* 注册 */
int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
		struct notifier_block *nb);
/* 注销 */
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
		struct notifier_block *nb);
/* 通知 */
int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
		unsigned long val, void *v);

可阻塞通知链

通知链元素的回调函数在进程上下文中运行,允许阻塞

对应API:

/* 定义notifier head并静态初始化 */
#define BLOCKING_NOTIFIER_HEAD(name)				\
	struct blocking_notifier_head name =			\
		BLOCKING_NOTIFIER_INIT(name)
/* 只动态初始化 */
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do {	\
		init_rwsem(&(name)->rwsem);	\
		(name)->head = NULL;		\
	} while (0)

/* 注册 */
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
		struct notifier_block *nb);
/* 注销 */
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
		struct notifier_block *nb);
/* 通知 */
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
		unsigned long val, void *v);

SRCU通知链

可阻塞通知链的一种变体,且初始化与其他的有些差异

对应API:

/* 定义notifier head并静态初始化 */
#define SRCU_NOTIFIER_HEAD(name)				\
	_SRCU_NOTIFIER_HEAD(name, /* not static */)
#define SRCU_NOTIFIER_HEAD_STATIC(name)				\
	_SRCU_NOTIFIER_HEAD(name, static)
/* 动态初始化函数 */
/* srcu_notifier_heads must be 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);

/* 注册 */
int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
		struct notifier_block *nb);
/* 注销 */
int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
		struct notifier_block *nb);
/* 通知 */
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
		unsigned long val, void *v);

实例

下面是个例子,使用的是原始通知链, 有部分是直接copy参考博文里面的代码,大致功能就是通过proc接口触发不同的通知链事件:

#include <linux/init.h>  
#include <linux/kernel.h>  
#include <linux/module.h>  

#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/notifier.h>

#define VERSION "0.1"

#define PROC_FILE_NAME "notifier-test"

#define EVENT_A 0x01    
#define EVENT_B 0x02

static char notifier_call;
static RAW_NOTIFIER_HEAD(raw_chain_list);   //define notifer chain list

static ssize_t notifier_test_write(struct file *filp, const char *userbuf, size_t count,
			      loff_t *ppos)
{
       char buf[80];
       int len = 0;

       if(*ppos > 0 /*|| count > 80*/)
              return -EFAULT;

       count = min_t(size_t, count, (sizeof(buf)-1));
       if (copy_from_user(buf, userbuf, count))
		return -EFAULT;

       if(sscanf(buf, "%hhd", &notifier_call) != 1)
              return -EFAULT;

       len = strlen(buf);
       *ppos = len;       

       printk("notifier_test_write :%d...  \n", notifier_call);
       switch (notifier_call) {
       case EVENT_A:
              printk("raw_notifier_call_chain: EVENT_A!\n");
              raw_notifier_call_chain(&raw_chain_list, EVENT_A, NULL);
              break;
       case EVENT_B:
              printk("raw_notifier_call_chain: EVENT_B!\n");
              raw_notifier_call_chain(&raw_chain_list, EVENT_B, NULL);
              break;
       default:
              printk("notifier_test_write Error!\n");
              break;
       }

       return len;
}

ssize_t notifier_test_read(struct file *filp, char *userbuf, size_t count, loff_t *ppos)
{
       int len = 0;
       char buf[64];

       if(*ppos > 0 /*|| count < BUFSIZE*/)
              return 0;

       printk("notifier_test_read :%lld... \n", *ppos);

       len += sprintf(buf, "%d\n", notifier_call);
       if(copy_to_user(userbuf, &buf, len))
              return -EFAULT;

       *ppos = len;
       return len;
}

// define callback function
int raw_notifer_callback(struct notifier_block *nb, unsigned long event, void *v)
{
	switch (event) {
       case EVENT_A:
              printk("raw_notifer_callback running EVENT_A!\n");
              break;
       case EVENT_B:
              printk("raw_notifer_callback running EVENT_B!\n");
              break;
       default:
              break;
	}

	return NOTIFY_DONE;
}

// define notifier block
static struct notifier_block raw_notif = {
	.notifier_call = raw_notifer_callback,  //appoint notifier callback function
};

static int __init notifier_test_init(void)   /*模块加载函数,通过insmod命令加载模块时,被自动执行*/
{  
       static struct proc_dir_entry *p;

       printk("init notifier_test-module...  \n");  

       p = proc_create(PROC_FILE_NAME, 0664, NULL,
                     /*(const struct proc_ops *)*/&notifier_test_fops);
	if (p == NULL)
              printk("[notifier error] proc_create failed!!\n");

       notifier_call = 0;
       raw_notifier_chain_register(&raw_chain_list, &raw_notif);

       return 0;  
}  

static void __exit notifier_test_exit(void)    /*模块卸载函数,当通过rmmod命令卸载时,会被自动执行*/
{  
       printk("exit notifier_test-modules... \n"); 
       raw_notifier_chain_unregister(&raw_chain_list, &raw_notif); 
       remove_proc_entry(PROC_FILE_NAME, NULL);
}  

module_init(notifier_test_init);   
module_exit(notifier_test_exit); 

MODULE_LICENSE("GPL");  

参考

https://blog.csdn.net/u014134180/article/details/86563754