tun驱动的初始化方法是tun_init。
static int __init tun_init(void)
{
int ret = 0;
pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
ret = rtnl_link_register(&tun_link_ops);
if (ret) {
pr_err("Can't register link_ops\n");
goto err_linkops;
}
ret = misc_register(&tun_miscdev);
if (ret) {
pr_err("Can't register misc device %d\n", TUN_MINOR);
goto err_misc;
}
ret = register_netdevice_notifier(&tun_notifier_block);
if (ret) {
pr_err("Can't register netdevice notifier\n");
goto err_notifier;
}
return 0;
err_notifier:
misc_deregister(&tun_miscdev);
err_misc:
rtnl_link_unregister(&tun_link_ops);
err_linkops:
return ret;
}
一 rtnl_link_register
rtnl_link_register的原型如下:
参数如下:
int rtnl_link_register(struct rtnl_link_ops *ops);
其中ops对应的是:
static struct rtnl_link_ops tun_link_ops __read_mostly = {
.kind = DRV_NAME, // 区分不同的驱动
.priv_size = sizeof(struct tun_struct),
.setup = tun_setup,
.validate = tun_validate,
};
rtnl_link_register的主要逻辑是将ops添加到全局列表link_ops中:
list_add_tail(&ops->list, &link_ops);
二 misc_register
misc_register的参数是:
static struct miscdevice tun_miscdev = {
.minor = TUN_MINOR,
.name = "tun",
.nodename = "net/tun",
.fops = &tun_fops,
};
字符设备和 块设备利用主设备号和次设备号来区分设备驱动。如一台机器接了一个打印机,其驱动的主设备号是X,次设备号是Y。这台机器再接入同一型号的一台打印机,则驱动主设备号是X,次设备号是Y1。X标识打印机的型号,Y标识此型号的具体一台打印机。
主设备号最大2^12,即4096,而次设备号最大2^20。为了解决主设备号短缺的问题,引入了miscdevice,杂设备。
杂设备驱动,主设备号固定是10;次设备号可以指定,也可以自动获取。
// 自动获取
if (is_dynamic) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);if (i >= DYNAMIC_MINORS) {
err = -EBUSY;
goto out;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
} else {
struct miscdevice *c;// 指定次设备号,进行判重
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
err = -EBUSY;
goto out;
}
}
}
最后将杂设备添加到全局列表misc_list中:
list_add(&misc->list, &misc_list);
在用户空间,调用open打开一个杂设备时,就能看到杂设备设计的巧妙之处,后面会再详细介绍。
三 register_netdevice_notifier
register_netdevice_notifier的参数为:
static struct notifier_block tun_notifier_block __read_mostly = {
.notifier_call = tun_device_event,
};
int register_netdevice_notifier(struct notifier_block *nb)
{
struct net_device *dev;
struct net_device *last;
struct net *net;
int err;
rtnl_lock();
err = raw_notifier_chain_register(&netdev_chain, nb);
if (err)
goto unlock;
if (dev_boot_phase)
goto unlock;
for_each_net(net) { // 遍历net_namespace_list
for_each_netdev(net, dev) { // 遍历net->dev_base_head
err = call_netdevice_notifier(nb, NETDEV_REGISTER, dev);
err = notifier_to_errno(err);
if (err)
goto rollback;
if (!(dev->flags & IFF_UP))
continue;
call_netdevice_notifier(nb, NETDEV_UP, dev);
}
}
unlock:
rtnl_unlock();
return err;
raw_notifier_chain_register将nb(即tun_notifier_block)添加到netdev_chain中的链表head上。
register_netdevice_notifier中的两层for循环,遍历网络空间中的每个设备,通知其NETDEV_REGISTER和NETDEV_UP。
call_netdevice_notifier(nb, NETDEV_REGISTER, dev);
call_netdevice_notifier(nb, NETDEV_UP, dev);
static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val,
struct net_device *dev)
{
struct netdev_notifier_info info = {
.dev = dev,
};
// 对于tun驱动,nb为 tun_notifier_block
return nb->notifier_call(nb, val, &info);
}
最终调用到:
static int tun_device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct tun_struct *tun = netdev_priv(dev);
if (dev->rtnl_link_ops != &tun_link_ops)
return NOTIFY_DONE;
switch (event) {
case NETDEV_CHANGE_TX_QUEUE_LEN:
if (tun_queue_resize(tun))
return NOTIFY_BAD;
break;
default:
break;
}
return NOTIFY_DONE;
}
两次通知,tun_device_event仅仅返回NOTIFY_DONE。