网络子系统5_设备队列规则

简介:
//	1.设备描述符与队列规则相关的字段:
//		1.1 dev->tx_queue_len 指示驱动程序规则队列的队列长度,在注册设备时使用,通知核心是否为设备提供队列规则机制.
//			1.1.1 不适用队列规则=0
//			1.1.2 使用队列规则>0
//		1.2 dev->qdisc,执行设备传输时,qdisc_run,dev_queue_xmit始终通过该字段获取设备当前使用的队列规则。
//		1.3 dev->qdisc_sleep, 保存设备具备传输能力时,使用的设备队列。

//	2.设备队列规则设置的时机:
//		2.1 注册设备(register_netdevice)时,设置为noop_qdisc
//		2.2 开启设备(dev_open)时,创建新的队列规则。
//		2.3 关闭设备(dev_close)时,设置dev->qdisc为noop_qdisc,表示在设备关闭的过程中,任何的传输都会被丢弃


//	初始化设备的队列规则
//	调用路径:register_netdevice->dev_init_scheduler
1.1 void dev_init_scheduler(struct net_device *dev)
{
	//获取qdisc_tree_lock,dev->queue_lock,并关软中断
	qdisc_lock_tree(dev);
	//设置dev的队列规则
	dev->qdisc = &noop_qdisc;
	dev->qdisc_sleeping = &noop_qdisc;
	INIT_LIST_HEAD(&dev->qdisc_list);
	//开锁
	qdisc_unlock_tree(dev);
	//队列看门狗
	dev_watchdog_init(dev);
}

//初始化看门狗
1.2 static void dev_watchdog_init(struct net_device *dev)
{
	//初始化定时器
	init_timer(&dev->watchdog_timer);
	dev->watchdog_timer.data = (unsigned long)dev;
	//定时器函数
	dev->watchdog_timer.function = dev_watchdog;
}

//	队列规则使用的看门狗定时器
//	看门狗函数执行的条件:
//		1.设备在系统中
//		2.设备处于IFF_UP状态
//		3.设备有载波
//		4.传输没有被关闭
//		5,上一次传输距离现在已经超过了到期时间
1.3 static void dev_watchdog(unsigned long arg)
{
	struct net_device *dev = (struct net_device *)arg;

	//持有hard_start_xmit的保护锁
	spin_lock(&dev->xmit_lock);
	//判断如果设备的队列规则不是noop_qdisc
	if (dev->qdisc != &noop_qdisc) {
		//检查设备是否存在,_PRESENT标志
		if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) {
			if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) > dev->watchdog_timeo) {
				printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name);
				//执行注册设备时提供的time_out函数
				dev->tx_timeout(dev);
			}
			//修改定时器的下一次到期时间
			if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo))
				//对非活跃的定时器修改到期时间,则同时增加对dev的引用计数
				dev_hold(dev);
		}
	}
	spin_unlock(&dev->xmit_lock);
	//释放对dev的引用计数
	dev_put(dev);
}

//	noop队列规则
//	所有操作,均将skb释放掉
2.1 struct Qdisc noop_qdisc = {
	.enqueue	=	noop_enqueue,//skb入队操作
	.dequeue	=	noop_dequeue,//skb出队操作
	.flags		=	TCQ_F_BUILTIN,//表示内建的队列规则
	.ops		=	&noop_qdisc_ops,//规则操作
	.list		=	LIST_HEAD_INIT(noop_qdisc.list),//链表头
};



//	分配队列规则:
//		当设备第一次被开启式时,为设备创建队列规则
//	参数:
//		ops为pfifo_fast_ops

//	调用路径:dev_open->dev_activate->qdisc_create_dflt
3.1 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
{
	void *p;
	struct Qdisc *sch;
	int size;
	//队列规则对齐到32字节
	size = ((sizeof(*sch) + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST);
	size += ops->priv_size + QDISC_ALIGN_CONST;
	//分配内存
	p = kmalloc(size, GFP_KERNEL);
	if (!p)
		return NULL;
	memset(p, 0, size);
	//对齐到32字节
	sch = (struct Qdisc *)(((unsigned long)p + QDISC_ALIGN_CONST) 
			       & ~QDISC_ALIGN_CONST);
	//保存为对齐而浪费的字节
	sch->padded = (char *)sch - (char *)p;

	//初始化规则队列链表头,用于链接到dev->qdisc_list
	INIT_LIST_HEAD(&sch->list);
	//初始化设备的传输队列
	skb_queue_head_init(&sch->q);
	//队列操作
	sch->ops = ops;
	sch->enqueue = ops->enqueue;
	sch->dequeue = ops->dequeue;
	//此队列关联的设备
	sch->dev = dev;
	//增加设备引用计数
	dev_hold(dev);
	//队列规则的统计变量锁为设备的传输锁
	//说明队列的统计数据更新依赖于传输队列
	sch->stats_lock = &dev->queue_lock;
	//队列引用规则为1
	atomic_set(&sch->refcnt, 1);
	if (!ops->init || ops->init(sch, NULL) == 0)
		return sch;
	//
	dev_put(dev);
	kfree(p);
	return NULL;
}



//	关闭队列规则
//	dev_close->dev_deactivate
3.2 void dev_deactivate(struct net_device *dev)
{
	struct Qdisc *qdisc;
	//关中断,获取队列锁
	spin_lock_bh(&dev->queue_lock);
	qdisc = dev->qdisc;
	dev->qdisc = &noop_qdisc;//将队列规则修改为noop_qdisc,丢弃所有的传输
	//调用qdisc->reset操作,将队列规则中所有未传输的skb丢弃掉
	qdisc_reset(qdisc);

	spin_unlock_bh(&dev->queue_lock);
	//关闭看门狗
	dev_watchdog_down(dev);
	//检查设备是否被调度
	while (test_bit(__LINK_STATE_SCHED, &dev->state))
		yield();//将当前进程放到就绪队列,切换到其他进程执行

	//等待dev->xmit_lock没有被任何cpu占用,从而保证在dev_deactivate返回后,没有任何cpu在dev上执行传输
	spin_unlock_wait(&dev->xmit_lock);
}



//	fifo队列规则
4.1 static struct Qdisc_ops pfifo_fast_ops = {
	.next		=	NULL,
	.cl_ops		=	NULL,
	.id		=	"pfifo_fast",//规则id
	.priv_size	=	3 * sizeof(struct sk_buff_head),//三个skb链表头
	.enqueue	=	pfifo_fast_enqueue,//入队
	.dequeue	=	pfifo_fast_dequeue,//出队
	.requeue	=	pfifo_fast_requeue,//重新入队
	.init		=	pfifo_fast_init,//初始化
	.reset		=	pfifo_fast_reset,//重置
	.dump		=	pfifo_fast_dump,//dump
	.owner		=	THIS_MODULE,
};

//	fifo的入队操作:
4.2 static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
{
	//规则队列中的队列头
	struct sk_buff_head *list = qdisc_priv(qdisc);
	//根据skb的优先级,计算skb应该进入的list头
	list += prio2band[skb->priority&TC_PRIO_MAX];
	//如果当前列表头中链接的skb个数<设备传输队列的长度
	if (list->qlen < qdisc->dev->tx_queue_len) {
		//将skb挂在到此list最后
		__skb_queue_tail(list, skb);
		//增加队列长度、队列中数据byte数、输出封包个数
		qdisc->q.qlen++;
		qdisc->bstats.bytes += skb->len;
		qdisc->bstats.packets++;
		return 0;
	}
	//该skb应该进入的队列已满,丢掉skb
	qdisc->qstats.drops++;
	kfree_skb(skb);
	return NET_XMIT_DROP;
}

// fifo出队操作:
4.3 static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
{
	int prio;
	struct sk_buff_head *list = qdisc_priv(qdisc);
	struct sk_buff *skb;

	//3个sk_buff_head,0-3优先级递减
	for (prio = 0; prio < 3; prio++, list++) {
		//从最高优先级的sk_buff_head出队一个skb
		skb = __skb_dequeue(list);
		if (skb) {
			//递减规则队列的包个数
			qdisc->q.qlen--;
			return skb;
		}
	}
	return NULL;
}

//	fifo重新入队操作:
//	调用时机:
//		出队skb后,发现hard_start_xmit的锁被获取,则重新入队skb
4.4 static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
{
	struct sk_buff_head *list = qdisc_priv(qdisc);
	//计算skb优先级对应的head
	list += prio2band[skb->priority&TC_PRIO_MAX];
	//入队
	__skb_queue_head(list, skb);
	//更新长度与统计变量
	qdisc->q.qlen++;
	qdisc->qstats.requeues++;
	return 0;
}

//	fifo 复位队列
//	清空队列中的所有skb
4.5 static void pfifo_fast_reset(struct Qdisc* qdisc)
{
	int prio;
	struct sk_buff_head *list = qdisc_priv(qdisc);
	//0-3优先级依次清空队列头
	for (prio=0; prio < 3; prio++)
		skb_queue_purge(list+prio);
	qdisc->q.qlen = 0;
}

//	清空链表
//	pfifo_fast_reset->skb_queue_purge
4.6 void skb_queue_purge(struct sk_buff_head *list)
{
	struct sk_buff *skb;
	//出链表后释放
	while ((skb = skb_dequeue(list)) != NULL)
		kfree_skb(skb);
}


//规则队列初始化
4.7 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
{
	int i;
	struct sk_buff_head *list = qdisc_priv(qdisc);
	//初始化3个sk_buff_head
	for (i=0; i<3; i++)
		skb_queue_head_init(list+i);

	return 0;
}

//skb->priority -> sk_buff_head的映射
4.8 static const u8 prio2band[TC_PRIO_MAX+1] =
	{ 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };

//删除队列规则
//调用路径unregister_netdevice->dev_shutdown->qdisc_destroy
void qdisc_destroy(struct Qdisc *qdisc)
{
	struct list_head cql = LIST_HEAD_INIT(cql);
	struct Qdisc *cq, *q, *n;

	if (qdisc->flags & TCQ_F_BUILTIN ||//noop_qdisc 设置此标识
		!atomic_dec_and_test(&qdisc->refcnt))//pfifo_fast_ops 在初始化时,设置引用计数为1
				return;

	if (!list_empty(&qdisc->list)) {//由于将qdisc链接到dev->qdisc_list,因此此判断失败
		if (qdisc->ops->cl_ops == NULL)//pfifo_fast_ops 的此字段为null
			list_del(&qdisc->list);
		else
			list_move(&qdisc->list, &cql);
	}

	list_for_each_entry(cq, &cql, list)//由于pfifo_fast_ops->cl_ops为null,所以cql为空,此循环不执行
		list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list)
			if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle)) {
				if (q->ops->cl_ops == NULL)
					list_del_init(&q->list);
				else
					list_move_tail(&q->list, &cql);
			}
	list_for_each_entry_safe(cq, n, &cql, list)//不执行
		list_del_init(&cq->list);

	call_rcu(&qdisc->q_rcu, __qdisc_destroy);//直接调用__qdisc_destroy,kfree(qdisc)
}

目录
相关文章
|
17天前
|
5G 数据安全/隐私保护
如果已经链接了5Gwifi网络设备是否还能搜索到其他5Gwifi网络
当设备已经连接到一个5G Wi-Fi网络时,它仍然有能力搜索和发现其他可用的5G Wi-Fi网络。这里所说的“5G Wi-Fi”通常指的是运行在5GHz频段的Wi-Fi网络,而不是与移动通信中的5G网络(即第五代移动通信技术)混淆。
|
1月前
|
网络协议 安全 文件存储
动态DNS(DDNS)技术在当前网络环境中日益重要,它允许使用动态IP地址的设备通过固定域名访问
动态DNS(DDNS)技术在当前网络环境中日益重要,它允许使用动态IP地址的设备通过固定域名访问,即使IP地址变化,也能通过DDNS服务保持连接。适用于家庭网络远程访问设备及企业临时或移动设备管理,提供便捷性和灵活性。示例代码展示了如何使用Python实现基本的DDNS更新。尽管存在服务可靠性和安全性挑战,DDNS仍极大提升了网络资源的利用效率。
53 6
|
29天前
|
存储 网络性能优化 网络虚拟化
局域网络设备
网卡、中继器、集线器、网桥和交换机是网络通信中的关键设备。网卡实现计算机与网络的连接,中继器用于延长网络传输距离,集线器将多台设备连接至共享网络,网桥通过MAC地址转发数据,而交换机提供高性能的数据转发和过滤服务,支持VLAN、QoS等功能,适用于不同规模的网络环境。
39 3
|
1月前
|
网络协议 安全 网络安全
|
1月前
|
网络虚拟化 数据安全/隐私保护 数据中心
对比了思科和华为网络设备的基本配置、接口配置、VLAN配置、路由配置、访问控制列表配置及其他重要命令
本文对比了思科和华为网络设备的基本配置、接口配置、VLAN配置、路由配置、访问控制列表配置及其他重要命令,帮助网络工程师更好地理解和使用这两个品牌的产品。通过详细对比,展示了两者的相似之处和差异,强调了持续学习的重要性。
56 2
|
1月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
829 2
|
10天前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
47 17
|
20天前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
21天前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
43 10
|
23天前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
在数字化时代,网络安全和信息安全已成为我们生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的内容,并提供一些实用的代码示例。通过阅读本文,您将了解到如何保护自己的网络安全,以及如何提高自己的信息安全意识。
47 10

热门文章

最新文章