tun驱动之tun_init

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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值