内核网络设备的删除

内核中与网络设备的删除相关的函数有两个: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

猜你喜欢

转载自blog.csdn.net/sinat_20184565/article/details/81511240