文章目录
如 net_device的分配与释放中的介绍,net_device对象只有通过register_netdev()注册到系统后才能被外部感知到。类似的,在释放之前需要通过unregister_netdev()将net_device对象先从系统中注销。这篇笔记分析了这两个过程的实现。
RTNL锁
在分析注册和注销过程之前,先来看个RTNL锁,因为这两个过程都由这把锁保护,代码如下:
static DEFINE_MUTEX(rtnl_mutex);
void rtnl_lock(void)
{
mutex_lock(&rtnl_mutex);
}
void __rtnl_unlock(void)
{
mutex_unlock(&rtnl_mutex);
}
void rtnl_unlock(void)
{
mutex_unlock(&rtnl_mutex);
// 释放锁后执行了一些其它工作,见下面的分析
netdev_run_todo();
}
这是一个互斥锁,这意味着net_device的中注册和注销不能在原子上下文中使用。
注册net_device
分配好net_device对象并进行初始化后,驱动程序就可以通过register_netdev()向系统注册该net_device对象了。
/**
* register_netdev - register a network device
* @dev: device to register
*
* Take a completed network device structure and add it to the kernel
* interfaces. A %NETDEV_REGISTER message is sent to the netdev notifier
* chain. 0 is returned on success. A negative errno code is returned
* on a failure to set up the device, or if the name is a duplicate.
*
* This is a wrapper around register_netdevice that takes the rtnl semaphore
* and expands the device name if you passed a format string to
* alloc_netdev.
*/
int register_netdev(struct net_device *dev)
{
int err;
rtnl_lock(); // 持锁情况下执行注册过程
// 如果驱动程序指定的网络设备名称中有%字符,则内核认为传入的是一个格式化字符串,
// 会尝试为其分配一个唯一的ID,以此组成最终的网络设备名称。比如传入
// "eth%d", 最终的结果是"eth0"、"eth1"等等
if (strchr(dev->name, '%')) {
err = dev_alloc_name(dev, dev->name);
if (err < 0)
goto out;
}
// 执行真正的注册流程
err = register_netdevice(dev);
out:
rtnl_unlock();
return err;
}
register_netdevice()
- 继续初始化net_device的一些字段;
- 如果有指定那么执行ndo_init()回调函数,如果返回失败那么也会导致注册过程失败;
- 确保net_device的名称在namespace内是唯一的。在namespace内分配一个唯一的ifindex;
- 将net_device对象添加到全局数据结构中;
- 发送设备注册通知事件;
int register_netdevice(struct net_device *dev)
{
struct hlist_head *head;
struct hlist_node *p;
int ret;
struct net *net = dev_net(dev);
// 设备接口层必须已经初始化完成,即net_dev_init()已经执行完毕
BUG_ON(dev_boot_phase);
ASSERT_RTNL(); // 确保在持有RTNL锁的情况下调用
might_sleep();
// 网络设备的注册状态必须是UNINITIALIZED,刚分配的net_device就是这个状态
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
BUG_ON(!net);
// 初始化地址列表、队列锁
spin_lock_init(&dev->addr_list_lock);
netdev_set_addr_lockdep_class(dev);
netdev_init_queue_locks(dev);
dev->iflink = -1;
// 执行驱动程序提供的ndo_init()回调函数
if (dev->netdev_ops->ndo_init) {
ret = dev->netdev_ops->ndo_init(dev);
if (ret) {
if (ret > 0)
ret = -EIO;
goto out;
}
}
// 校验net_device对象的名称
if (!dev_valid_name(dev->name)) {
ret = -EINVAL;
goto err_uninit;
}
// 分配网络设备索引
dev->ifindex = dev_new_index(net);
if (dev->iflink == -