一、初始化
当网络接口使用ifup命令启动后,就会触发dev_open。
//设备开启
dev_open
......
//设备激活
dev_activate(dev);
//当前还没有设置出口排队规则
if (dev->qdisc_sleeping == &noop_qdisc)
//当前设备标记需要发送队列
if (dev->tx_queue_len)
//创建FIFO_FAST类型出口排队规则(该种排队规则没有分类,并且内
部含有3个频道,根据报文的TOS值进行不同频道映射,在进行报文出
队时,也会优先处理频道0,但频道0没有报文时才处理频道1,依此类推)
qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops,TC_H_ROOT);
sch = qdisc_alloc(dev, ops);
//父类为TC_H_ROOT,表明该排队规则是根,没有父类
sch->parent = parentid;
//该排队规则初始化函数为pfifo_fast_init
ops->init(sch, NULL)
pfifo_fast_init
//该排队规则的私有数据块为三个SKB列表
struct sk_buff_head *list = qdisc_priv(qdisc);
//初始化三个优先级频道SKB列表
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
skb_queue_head_init(list + prio);
//将当前排队规则加入到dev->qdisc_list链表中
write_lock(&qdisc_tree_lock);
list_add_tail(&qdisc->list, &dev->qdisc_list);
write_unlock(&qdisc_tree_lock);
//如果当前发送队列长度为0,表明设备不需要通过出口队列发送,设备想直
//接发送报文
esle
//这里noqueue_qdisc队列规则没有enqueue入列回调,所以在发包报文时
//会检测如果没有enqueue则按直接发送报文方式处理。
qdisc = &noqueue_qdisc;
//初始化当前选择的排队规则
dev->qdisc_sleeping = qdisc;
//当前设备没有载波则返回
if (!netif_carrier_ok(dev))
return;
spin_lock_bh(&dev->queue_lock);
//初始化当前使用的排队规则
rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping);
//如果使用出口排队规则,则启动看门狗定时器
if (dev->qdisc != &noqueue_qdisc)
dev->trans_start = jiffies;
dev_watchdog_up(dev);
spin_unlock_bh(&dev->queue_lock);
二、发包
当上层发出报文,调用dev_queue_xmit,这里仅分析和出口排队规则相关的代码,其它详细
的代码分析可以参见《接口设备发包》
dev_queue_xmit
......
//如果当前使用出口排队规则
q = rcu_dereference(dev->qdisc);
if (q->enqueue)
spin_lock(&dev->queue_lock);
q = dev->qdisc;
if (q->enqueue)
//调用当前排队规则的入队回调,当前FIFO_FAST类型排队规则回调为
//pfifo_fast_enqueue
rc = q->enqueue(skb, q);
pfifo_fast_enqueue
//根据包中的优先级字段,确定该包放入哪个频道的链表中
//这里的prio2band为
//prio2band[TC_PRIO_MAX+1] =
//{ 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };
//根据SKB中的priority字段(其中TC_PRIO_MAX为15,表示仅
//取priority字段的后四位)进行映射,这里映射表如下
---------------------------
|频道|优先级后4位|
---------------------------
|1 | 0000 |
|2 | 0001 |
|2 | 0010 |
|2 | 0011 |
|1 | 0100 |
|2 | 0101 |
|0 | 0110 |
|0 | 0111 |
|1 | 1000 |
|1 | 1001 |
|1 | 1010 |
|1 | 1011 |
|1 | 1100 |
|1 | 1101 |
|1 | 1110 |
|1 | 1111 |
--------------------------
//SKB中的priority字段在数据包中对应的是IP包头中的TOS字段。
//通过在ip_forward函数中查看rt_tos2priority的TOS值到priority的
//转换,映射表如下(TOS值在IP头TOS字段的如下位000XXXX0)
-----------------------
TOS值 | 优先级
-----------------------
0000 | 0000
0001 | 0001
0010 | 0000
0011 | 0000
0100 | 0010
0101 | 0010
0110 | 0010
0111 | 0010
1000 | 0110
1001 | 0110
1010 | 0110
1011 | 0110
1100 | 0100
1101 | 0100
1110 | 0100
1111 | 0100
---------------------
sk_buff_head *list = prio2list(skb, qdisc);
list = qdisc_priv(qdisc);
return list + prio2band[skb->priority & TC_PRIO_MAX];
//当前频道的链表长度还未到达该设备发送队列的上限,则将
//包加入到当前频道的链表中。
if (skb_queue_len(list) < qdisc->dev->tx_queue_len)
qdisc->q.qlen++;
return __qdisc_enqueue_tail(skb, qdisc, list);
//如果当前频道的链表长度已经达到设备发送队列的上限,则
//进行丢弃处理。
return qdisc_drop(skb, qdisc);
//队列处理
qdisc_run(dev);
//如果队列发送没有关闭,并且队列调度还没有运行,则运行队列调度
if (!netif_queue_stopped(dev)
&&!test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state))
__qdisc_run(dev);
//如果队列为noop_qdisc类型,则表明接口还未启动,则直接退
//出队列调度
if (unlikely(dev->qdisc == &noop_qdisc))
goto out;
//当还有待处理的数据,并且发送队列没有关闭,则循环从队列
//中取出数据,使用网卡驱动进行发送数据。该函数具体实现不
//详细列出。之前在《接口设备发包》中已经分析过,可以参考
//那节的分析。这里主要关注,从队列中取出数据是使用的队列
//的dequeue回调,当前FIFO_FAST类型的排队规则回调为
//pfifo_fast_dequeue
while (qdisc_restart(dev) < 0 && !netif_queue_stopped(dev))
;
out:
//去除正在进行队列调度标记
clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state);
spin_unlock(&dev->queue_lock);
rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
goto out;
spin_unlock(&dev->queue_lock);
---------------------------------------------------------------------------------------------------------------------
//从队列中取出一个数据报文
pfifo_fast_dequeue
sk_buff_head *list = qdisc_priv(qdisc);
//当前FIFO_FAST类型出口排队规则含有三个优先级频道,每个频道都是一个SKB
//链表。优先级0号频道链表取报文,只有在0号频道链表没有报文时,才从1号
//频道链表中取报文,前两个频道都没有报文时才从2号频道链表中取。
for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
if (!skb_queue_empty(list + prio))
qdisc->q.qlen--;
return __qdisc_dequeue_head(qdisc, list + prio);
skb = __skb_dequeue(list); //取出报文
sch->qstats.backlog -= skb->len; //统计
return skb;
默认FIFO_FAST出口排队规则分析
最新推荐文章于 2023-08-25 15:01:03 发布