内核中与网络设备的删除相关的函数有两个:unregister_netdev和free_netdev。前者用在从内核中删除一个已经注册的网络设备;而后者用于释放一个已经分配的网络设备(struct net_device),其在alloc_netdev函数执行成功之后,注册函数register_netdev出错时调用;或者在unregister_netdev中调用,或者在其后调用。
不同与内核中的其它结构,在引用计数为0时自动释放。网络设备net_device不会在引用计数为0时自动释放。其释放最终通过kobject系统的device_release函数完成。也就是说net_device是借助其kobject对象的引用为0时,释放自身的。
注销网络设备
内核的注销函数(unregister_netdev)分两个执行部分:unregister_netdevice和netdev_run_todo,其中后者net_dev_run_todo不持有rtnl_lock锁,所以其可使用netdev_wait_allrefs函数等待对此网络设备的引用全部释放。
void unregister_netdev(struct net_device *dev)
{
rtnl_lock();
unregister_netdevice(dev);
rtnl_unlock();
}
void rtnl_unlock(void)
{
netdev_run_todo();
}
注销函数unregister_netdevice最终调用rollback_registered_many完成主要功能。包括:
1)调用dev_close_many函数关闭设备,停止Qdisc队列;
2)调用flush_all_backlogs函数清空设备的接收和处理队列(process_queue & input_pkt_queue);
3)从全局设备hash列表(name列表、index列表)中删除设备。
4)调用NETDEV_UNREGISTER通知链,使注册此消息的模块,有机会释放对此设备的引用;
5)清空设备的单播地址和多播地址列表(dev_uc_flush和dev_mc_flush);
执行完成之后设备的reg_state状态变为了NETREG_UNREGISTERING。后续等待设备引用计数清零的操作有函数netdev_wait_allrefs完成,其每一秒钟调用一次NETDEV_UNREGISTER和NETDEV_UNREGISTER_FINAL通知链,使其它模块有机会通过通知链函数释放设备的引用。另外,其每10秒钟打印一次警告信息,显示当前的设备引用计数的数值。
static void net_set_todo(struct net_device *dev)
{
while (!list_empty(&list)) {
dev->reg_state = NETREG_UNREGISTERED;
netdev_wait_allrefs(dev);
if (dev->priv_destructor)
dev->priv_destructor(dev);
if (dev->needs_free_netdev)
free_netdev(dev);
/* Free network device */
kobject_put(&dev->dev.kobj);
}
}
释放网络设备
内核版本4.15的net_device结构体,将其成员destructor改为了priv_destructor,增加了needs_free_netdev标志。这样就把net_device的通用释放函数与设备的私有数据的释放分离开了。之前的内核只有一个destructor成员,两项功能混在一起。比如旧的bond_destructor函数中包含有free_netdev函数。现在bond模块中bond_destructor仅完成自身的清理工作即可,将needs_free_netdev设置为true,有net_set_todo去执行free_netdev的操作。
void bond_setup(struct net_device *bond_dev)
{
bond_dev->needs_free_netdev = true;
bond_dev->priv_destructor = bond_destructor;
}
int bond_create(struct net *net, const char *name)
{
bond_dev = alloc_netdev_mq(..., bond_setup, tx_queues);
}
另外,如果不设置needs_free_netdev标志,也可在调用unregister_netdev之后,显示调用free_netdev函数,释放设备。
最终由kobject系统的device_release函数释放net_device结构体占用内存。
static struct kobj_type device_ktype = {
.release = device_release,
};
static void device_release(struct kobject *kobj)
{
kfree(p);
}
void free_netdev(struct net_device *dev)
{
dev->reg_state = NETREG_RELEASED;
/* will free via device release */
put_device(&dev->dev);
}
内核版本
Linux-4.15