网络子系统31_网络设备的注册与注销

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介:
//	网络设备注册
//		内核中,使用两个全局的hash表,索引net_device,分别为name hash表,index hash表,使用一个链表dev_base,链接所有net_device

//	包裹函数,获取rtnl锁,注册任务由register_netdev完成
1.1 int register_netdev(struct net_device *dev)
{
	int err;
	//获取rtnl锁
 	rtnl_lock();
 	//执行注册
	err = register_netdevice(dev);
	//开锁,并继续完成net_todo_list上挂载的dev的注册/注销
	rtnl_unlock();
    return err;
}

//	网络设备注册
//	调用路径:register_netdev->register_netdevice
//	函数主要任务:
//		1.初始化队列锁,传输锁
//		2.分配接口索引
//		3.检查设备名是否唯一
//		4.更新设备SG,TSO特性
//		5.初始化设备的队列规则
//		6.将设备添加到name,index的两个hash表中
//		7.通知netdev_chain
//		8.启动下半部

//	注:net_device->queue_lock用于保护设备的规则队列,net_device->xmit_lock用于保护驱动程序的hard_start_xmit
//	xmit_lock_owner=获取xmit_lock的cpu号
1.2 int register_netdevice(struct net_device *dev)
{
	struct hlist_head *head;
	struct hlist_node *p;
	int ret;

	//初始化设备队列锁,在net_tx_action中会被获取
	spin_lock_init(&dev->queue_lock);
	//初始化hard_xmit保护锁,在qdisc_restart中会被获取
	spin_lock_init(&dev->xmit_lock);
	//锁木有没有拥有者
	dev->xmit_lock_owner = -1;


	//表示设备目前没有分配接口索引 ifindex字段
	dev->iflink = -1;

	//如果驱动程序提供了init例程,则调用
	if (dev->init) {
		ret = dev->init(dev);
		if (ret) {
			if (ret > 0)
				ret = -EIO;
			goto out_err;
		}
	}
 	//设备名非法
	if (!dev_valid_name(dev->name)) {
		ret = -EINVAL;
		goto out_err;
	}
	//分配唯一的接口索引
	dev->ifindex = dev_new_index();
	if (dev->iflink == -1)
		//iflink等于接口索引
		dev->iflink = dev->ifindex;

	//检查设备名是否唯一
	head = dev_name_hash(dev->name);
	hlist_for_each(p, head) {
		struct net_device *d
			= hlist_entry(p, struct net_device, name_hlist);
			//检查设备名是否已经被注册
		if (!strncmp(d->name, dev->name, IFNAMSIZ)) {
			ret = -EEXIST;
 			goto out_err;
		}
 	}

	//设备支持分散聚集DMA,不支持校验和功能,则取消掉SG
	if ((dev->features & NETIF_F_SG) &&
	    !(dev->features & (NETIF_F_IP_CSUM |
			       NETIF_F_NO_CSUM |
			       NETIF_F_HW_CSUM))) {
		printk("%s: Dropping NETIF_F_SG since no checksum feature.\n",
		       dev->name);
		dev->features &= ~NETIF_F_SG;
	}

	//TCP 的TSO功能
	if ((dev->features & NETIF_F_TSO) &&
	    !(dev->features & NETIF_F_SG)) {
		printk("%s: Dropping NETIF_F_TSO since no SG feature.\n",
		       dev->name);
		dev->features &= ~NETIF_F_TSO;
	}

	//以太网默认提供rebuild_header函数,在ether_setup中设置
	if (!dev->rebuild_header)
		dev->rebuild_header = default_rebuild_header;
	//设备存在标志,多用于热插拔设备
	set_bit(__LINK_STATE_PRESENT, &dev->state);

	dev->next = NULL;
	//初始化设备的队列规则
	dev_init_scheduler(dev);
	//获取保护dev_tail,dev_name_head,dev_index_head锁
	write_lock_bh(&dev_base_lock);
	*dev_tail = dev;
	dev_tail = &dev->next;
	//将dev添加到hash表中
	hlist_add_head(&dev->name_hlist, head);
	hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
	//增加dev的引用计数
	dev_hold(dev);
	//将dev标记为NETREG_REGISTERING状态,表示正在注册中
	dev->reg_state = NETREG_REGISTERING;
	write_unlock_bh(&dev_base_lock);
	//通知netdev_chain中的监听者,有设备注册
	notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
	//由net_set_todo完成后半部的注册,主要是在sysfs中注册设备
	net_set_todo(dev);
	ret = 0;

out:
	return ret;
out_err:
	free_divert_blk(dev);
	goto out;
}



//	分配接口索引
//		确保唯一性的办法,检查新索引是否已经被使用

//	调用路径:register_netdev->register_netdevice->dev_new_index
1.3 static int dev_new_index(void)
{
	static int ifindex;
	for (;;) {
		if (++ifindex <= 0)
			ifindex = 1;
		//获取第一个没有被设备使用的索引
		if (!__dev_get_by_index(ifindex))
			return ifindex;
	}
}

//	挂载设备到后半部操作链表
//		将设备注册,注销操作,分成两部分,后一部分在释放rtnl锁的情况下进行
2.1 static inline void net_set_todo(struct net_device *dev)
{
	//后半部链表由net_todo_list_lock保护
	spin_lock(&net_todo_list_lock);
	//将设备挂在到net_todo_list上,接收之后的注册
	list_add_tail(&dev->todo_list, &net_todo_list);
	spin_unlock(&net_todo_list_lock);
}

//	注册、注销后半部入口点:
//		在释放rtnl之后,完成剩余操作

//	注:netlink为核心通知用户事件的标准内核接口
2.2 void rtnl_unlock(void)
{	
	//释放rtnl_sem信号量
	rtnl_shunlock();
	//操作net_todo_list
	netdev_run_todo();
}

//	注册与注销的切割操作
//	函数主要内容:
//		1.遍历net_todo_list上的设备
//		2.注册操作的后半部:
//			2.1 在sysfs中更新相应项
//		3.注销操作后半部:
//			3.1 删除sysfs中的相应项
//			3.2 等待net_device的引用计数变为1,
//			3.3 向netdev_chain发送注销信息,通知持有该设备的模块释放对其的引用。
//			3.4 调用驱动提供的destructor

//	注:netdev_run_todo由net_todo_run_mutex信号量保护,全局同时只有一个实例在运行。
2.3 void netdev_run_todo(void)
{
	struct list_head list = LIST_HEAD_INIT(list);
	int err;

	//保证全局只有一个netdev_run_todo在执行
	down(&net_todo_run_mutex);

	//检查net_todo_list上是否有需要执行切割操作的dev
	if (list_empty(&net_todo_list))
		goto out;

	//获取保护net_todo_list的锁
	spin_lock(&net_todo_list_lock);
	//将net_todo_list合并到本地list上,然后重新初始化net_todo_list
	list_splice_init(&net_todo_list, &list);
	spin_unlock(&net_todo_list_lock);
		
	while (!list_empty(&list)) {
		struct net_device *dev
			= list_entry(list.next, struct net_device, todo_list);
		//从list上删除dev
		list_del(&dev->todo_list);

		//判断dev当前的注册状态
		switch(dev->reg_state) {
			//在register_netdevice中被设置
		case NETREG_REGISTERING:
			//在sysfs中注册设备
			err = netdev_register_sysfs(dev);
			if (err)
				printk(KERN_ERR "%s: failed sysfs registration (%d)\n",
				       dev->name, err);
			dev->reg_state = NETREG_REGISTERED;
			break;
			//在unregister_netdevice中被设置
		case NETREG_UNREGISTERING:
			//删除sysfs中设备对应的节点
			netdev_unregister_sysfs(dev);
			dev->reg_state = NETREG_UNREGISTERED;
			//等待dev的引用计数为零,周期性打印unregister消息,并向netdev_chain通知此dev的NETDEV_UNREGISTER消息
			netdev_wait_allrefs(dev);
			//如果设备驱动提供了销毁操作,则调用
			if (dev->destructor)
				dev->destructor(dev);
			break;

		default:
			printk(KERN_ERR "network todo '%s' but state %d\n",
			       dev->name, dev->reg_state);
			break;
		}
	}

out:
	up(&net_todo_run_mutex);
}


//	使用切割操作的优势主要体现在注销设备时:
//		当驱动需要同时注销多个设备时,unregister_netdev会造成严重的锁开销,因为一次上锁解锁只能注销一个设备
//		通过使用unregister_netdevice,手工对rtnl加锁,可以通过一次加锁注销多个net_device,从而降低锁开销。

//	
//	参考 深入理解linux网络技术内幕 提供的一个例子
//1.rtnl_lock( );
// loop for each device driven by this driver {
//     ... ... ...
//     unregister_netdevice(dev);
//     ... ... ...
// }
// rtnl_unlock( );
//
//2.loop for each device driven by this driver {
//     ... ... ...
//     unregister_netdev(dev);
//     ... ... ...
// } 

//unregister_netdev()
//{
//	rtnl_lock();
//	unregister_netdevice();
//	rtnl_unlock();
//}



//	注销网络设备

//	函数主要任务:
//		1.确保设备已经被关闭
//		2.从name 哈希表,index哈希表删除
//		3.通知netdev_chain设备正在注销
//		4.释放多播列表
//		5.启动后半部操作
3.2 int unregister_netdevice(struct net_device *dev)
{
	struct net_device *d, **dp;


	if (dev->reg_state == NETREG_UNINITIALIZED) {//注销的设备必须是已经注册过的设备
		printk(KERN_DEBUG "unregister_netdevice: device %s/%p never "
				  "was registered\n", dev->name, dev);
		return -ENODEV;
	}


	if (dev->flags & IFF_UP)//设备处于开启状态,需要先关闭设备
		dev_close(dev);//修改设备状态,以及修改设备队列规则

	for (dp = &dev_base; (d = *dp) != NULL; dp = &d->next) {//遍历网卡设备列表
		if (d == dev) {
			write_lock_bh(&dev_base_lock);//操作dev_base需要获取dev_base_lock锁
			hlist_del(&dev->name_hlist);//从index,name链表删除设备
			hlist_del(&dev->index_hlist);
			if (dev_tail == &dev->next)
				dev_tail = dp;
			*dp = d->next;
			write_unlock_bh(&dev_base_lock);
			break;
		}
	}
	if (!d) {
		printk(KERN_ERR "unregister net_device: '%s' not found\n",
		       dev->name);
		return -ENODEV;
	}

	dev->reg_state = NETREG_UNREGISTERING;//设置设备为正在注销状态,稍有由后半部完成剩余的注销操作

	dev_shutdown(dev);//删除设备队列规则

	notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);//通知netdev_chain,设备要注销
	
	dev_mc_discard(dev);//删除多播列表

	if (dev->uninit)
		dev->uninit(dev);//如果设备提供了注销操作,则调用

	free_divert_blk(dev);//释放分流器

	net_set_todo(dev);//将设备链接到todo链表上

	dev_put(dev);//递减引用计数
	return 0;
}

//	删除设备多播列表
void dev_mc_discard(struct net_device *dev)
{
	spin_lock_bh(&dev->xmit_lock);
	
	while (dev->mc_list != NULL) {//链表非空
		struct dev_mc_list *tmp = dev->mc_list;
		dev->mc_list = tmp->next;//从链表取下多播控制项
		if (tmp->dmi_users > tmp->dmi_gusers)//dmi_users = number of users;dmi_guesers = number of groups
			printk("dev_mc_discard: multicast leakage! dmi_users=%d\n", tmp->dmi_users);
		kfree(tmp);//直接kfree
	}
	dev->mc_count = 0;

	spin_unlock_bh(&dev->xmit_lock);
}

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
目录
相关文章
|
30天前
|
安全 网络安全 网络虚拟化
【软件设计师备考 专题 】常用网络设备和各类通信设备(一)
【软件设计师备考 专题 】常用网络设备和各类通信设备
97 2
|
1月前
|
SQL 安全 网络协议
网络安全产品之认识漏洞扫描设备
为了保障系统的安全性,需要及时发现和修复漏洞。这可以通过漏洞扫描设备等工具进行自动化检测和修复,同时也可以加强安全意识和培训,提高人员的安全防范能力。虽然无法完全避免漏洞的存在,但通过采取有效的措施可以大大减少漏洞的数量和危害程度,保障系统的安全性和稳定性。本文让我们一起来认识漏洞扫描设备。
54 0
|
1月前
|
监控 Linux Shell
【Shell 命令集合 网络通讯 】Linux管理终端设备的登录过程 getty命令 使用指南
【Shell 命令集合 网络通讯 】Linux管理终端设备的登录过程 getty命令 使用指南
33 0
|
1月前
|
安全 Shell Linux
【Shell 命令集合 网络通讯 】Linux 打开终端设备 mingetty命令 使用指南
【Shell 命令集合 网络通讯 】Linux 打开终端设备 mingetty命令 使用指南
39 0
|
5天前
|
安全 网络安全 网络虚拟化
《计算机网络简易速速上手小册》第3章:计算机网络设备和工具(2024 最新版)
《计算机网络简易速速上手小册》第3章:计算机网络设备和工具(2024 最新版)
23 1
|
15天前
|
边缘计算 网络虚拟化 虚拟化
虚拟网络设备性能优化
在现代网络架构中,虚拟网络设备扮演着越来越重要的角色🌐,特别是在云计算☁️和容器化技术📦广泛应用的背景下。虚拟网络设备如虚拟以太网设备(veth)、虚拟交换机(vSwitch)、和虚拟路由器等,提供了灵活的网络连接和隔离方案🔗。然而,与物理网络设备相比,虚拟网络设备在处理能力💪、带宽利用率📈和延迟⏳方面可能存在性能瓶颈。因此,性能优化成为了虚拟网络设备管理中的一个重要议题🛠️。本文将探讨虚拟网络设备的性能优化手段,帮助网络管理员更有效地利用这些设备。
|
15天前
|
安全 网络安全 网络虚拟化
虚拟网络设备与网络安全:深入分析与实践应用
在数字化时代📲,网络安全🔒成为了企业和个人防御体系中不可或缺的一部分。随着网络攻击的日益复杂和频繁🔥,传统的物理网络安全措施已经无法满足快速发展的需求。虚拟网络设备🖧,作为网络架构中的重要组成部分,通过提供灵活的配置和强大的隔离能力🛡️,为网络安全提供了新的保障。本文将从多个维度深入分析虚拟网络设备是如何保障网络安全的,以及它们的实际意义和应用场景。
|
15天前
|
安全 网络安全 SDN
虚拟网络设备的真正使命:实现有控制的通信
虚拟网络设备确实提供了强大的网络隔离能力🛡️,但这种隔离本身并不是最终目的。实际上,更重要的是通过这种隔离能力实现有控制的通信🎛️,以满足特定的业务需求、安全要求和性能标准。换句话说,网络隔离是手段,而有控制的通信才是目的🎯。
虚拟网络设备的真正使命:实现有控制的通信
|
15天前
|
网络协议 Linux SDN
虚拟网络设备与Linux网络协议栈
在现代计算环境中,虚拟网络设备在实现灵活的网络配置和隔离方面发挥了至关重要的作用🔧,特别是在容器化和虚拟化技术广泛应用的今天🌐。而Linux网络协议栈则是操作系统处理网络通信的核心💻,它支持广泛的协议和网络服务🌍,确保数据正确地在网络中传输。本文将深入分析虚拟网络设备与Linux网络协议栈的关联,揭示它们如何共同工作以支持复杂的网络需求。
|
30天前
|
安全 网络安全 网络虚拟化
【软件设计师备考 专题 】常用网络设备和各类通信设备(三)
【软件设计师备考 专题 】常用网络设备和各类通信设备
36 0

热门文章

最新文章