【Linux5.4】【TUN】代码学习记录(2)–tun_init()
TUN模块入口从module_init(tun_init)开始:
module_init(tun_init);
在tun_init中主要执行三个函数
static int __init tun_init(void)
{
...
ret = rtnl_link_register(&tun_link_ops);
ret = misc_register(&tun_miscdev);
ret = register_netdevice_notifier(&tun_notifier_block);
...
}
rtnl_link_register(&tun_link_ops)做了什么?
rtnl_link_register(&tun_link_ops)封装了__rtnl_link_register函数,其作用是在link_ops链表中添加tun_link_ops->list。
注册逻辑:在link_ops链表中对tun_link_ops内的kind值进行查找,确认没有才可进行添加操作。
这两个函数和link_ops都定义在<net/core/rtnetlink.c>中
static LIST_HEAD(link_ops);
/**
* rtnl_link_register - Register rtnl_link_ops with rtnetlink.
* @ops: struct rtnl_link_ops * to register
*
* Returns 0 on success or a negative error code.
*/
int rtnl_link_register(struct rtnl_link_ops *ops)
{
int err;
/* Sanity-check max sizes to avoid stack buffer overflow. */
if (WARN_ON(ops->maxtype > RTNL_MAX_TYPE ||
ops->slave_maxtype > RTNL_SLAVE_MAX_TYPE))
return -EINVAL;
rtnl_lock();
err = __rtnl_link_register(ops);
rtnl_unlock();
return err;
}
EXPORT_SYMBOL_GPL(rtnl_link_register);
/**
* __rtnl_link_register - Register rtnl_link_ops with rtnetlink.
* @ops: struct rtnl_link_ops * to register
*
* The caller must hold the rtnl_mutex. This function should be used
* by drivers that create devices during module initialization. It
* must be called before registering the devices.
*
* Returns 0 on success or a negative error code.
*/
int __rtnl_link_register(struct rtnl_link_ops *ops)
{
//查找是否已存在ops->kind的值
if (rtnl_link_ops_get(ops->kind))
return -EEXIST;
/* The check for setup is here because if ops
* does not have that filled up, it is not possible
* to use the ops for creating device. So do not
* fill up dellink as well. That disables rtnl_dellink.
*/
if (ops->setup && !ops->dellink)
ops->dellink = unregister_netdevice_queue;
//将ops->list插入link_ops链表内,完成注册
list_add_tail(&ops->list, &link_ops);
return 0;
}
EXPORT_SYMBOL_GPL(__rtnl_link_register);
misc_register(&tun_miscdev)做了什么?
misc_register(&tun_miscdev)主要作用是在misc_list链表中加入misc->list,完成混杂设备注册。
注册逻辑:首先判断是否动态获取混杂设备的子设备号,也就是minor的值,若是动态获取则查找一个未使用编号交给misc->minor,若是非动态则先判断输入的子设备号在misc->list中是否已存在,不存在便可使用。
接着MKDEV()使用主设备号(10)+子设备号组合获取一个dev_t类型的设备编号。
device_create_with_groups内使用获得的设备编号创建device对象。
最后将misc->list加入链表完成注册。
misc_register函数和misc_list链表定义在<drivers/char/misc.c>
static LIST_HEAD(misc_list);
/**
* misc_register - register a miscellaneous device
* @misc: device structure
*
* Register a miscellaneous device with the kernel. If the minor
* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
* and placed in the minor field of the structure. For other cases
* the minor number requested is used.
*
* The structure passed is linked into the kernel and may not be
* destroyed until it has been unregistered. By default, an open()
* syscall to the device sets file->private_data to point to the
* structure. Drivers don't need open in fops for this.
*
* A zero is returned on success and a negative errno code for
* failure.
*/
int misc_register(struct miscdevice *misc)
{
dev_t dev;
int err = 0;
/* 判断是否为动态获取子设备号 */
bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
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;
}
}
}
/* 用主设备号+子设备号创建设备dev */
dev = MKDEV(MISC_MAJOR, misc->minor);
/* 创建device对象,由misc->this_device指针获取 */
misc->this_device =
device_create_with_groups(misc_class, misc->parent, dev,
misc, misc->groups, "%s", misc->name);
/* 判断指针是否合法 */
if (IS_ERR(misc->this_device)) {
if (is_dynamic) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
misc->minor = MISC_DYNAMIC_MINOR;
}
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}
EXPORT_SYMBOL(misc_register);
register_netdevice_notifier(&tun_notifier_block)做了什么?
register_netdevice_notifier(&tun_notifier_block)主要作用是在netdev_chain链表中添加tun_notifier_block,成功后发布设备注册事件通知,根据设备状态发布设备启动事件通知。
该函数与netdev_chain定义在<net/core/dev.c>
static RAW_NOTIFIER_HEAD(netdev_chain);
/**
* register_netdevice_notifier - register a network notifier block
* @nb: notifier
*
* Register a notifier to be called when network device events occur.
* The notifier passed is linked into the kernel structures and must
* not be reused until it has been unregistered. A negative errno code
* is returned on a failure.
*
* When registered all registration and up events are replayed
* to the new notifier to allow device to have a race free
* view of the network device list.
*/
int register_netdevice_notifier(struct notifier_block *nb)
{
struct net_device *dev;
struct net_device *last;
struct net *net;
int err;
/* Close race with setup_net() and cleanup_net() */
down_write(&pernet_ops_rwsem);
rtnl_lock();
/* 在网络通知链netdev_chain中添加nb(tun_notifier_block) */
err = raw_notifier_chain_register(&netdev_chain, nb);
if (err)
goto unlock;
if (dev_boot_phase)
goto unlock;
for_each_net(net) {
for_each_netdev(net, dev) {
/* 发布设备注册事件 */
err = call_netdevice_notifier(nb, NETDEV_REGISTER, dev);
err = notifier_to_errno(err);
if (err)
goto rollback;
if (!(dev->flags & IFF_UP))
continue;
/* 发布设备启动UP事件 */
call_netdevice_notifier(nb, NETDEV_UP, dev);
}
}
unlock:
rtnl_unlock();
up_write(&pernet_ops_rwsem);
return err;
rollback:
last = dev;
for_each_net(net) {
for_each_netdev(net, dev) {
if (dev == last)
goto outroll;
if (dev->flags & IFF_UP) {
call_netdevice_notifier(nb, NETDEV_GOING_DOWN,
dev);
call_netdevice_notifier(nb, NETDEV_DOWN, dev);
}
call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev);
}
}
outroll:
raw_notifier_chain_unregister(&netdev_chain, nb);
goto unlock;
}
EXPORT_SYMBOL(register_netdevice_notifier);
tun_init小结
tun_init函数并不复杂:
在该函数内,无论是注册rtnl,注册混杂设备,还是注册原始通知链,都是在一个链表中加入某一表项,由该表项完成tun各个功能的实现。