如果網絡設備發送隊列沒有配置發送策略,內核就會使用默認的隊列策略來進行報文的發送。
內核中定義實現了兩種默認的隊列策略,一種是給隊列長度爲零的隊列使用的。一種是給隊列長度不爲0,但沒配置隊列策略的隊列使用的。
初始化時使用的默認隊列策略 noop_qdisc:
/*入隊操作直接丟棄報文*/ static int noop_enqueue(struct sk_buff *skb, struct Qdisc * qdisc) { kfree_skb(skb); return NET_XMIT_CN; } /*出隊操作直接返回NULL*/ static struct sk_buff *noop_dequeue(struct Qdisc * qdisc) { return NULL; } struct Qdisc_ops noop_qdisc_ops __read_mostly = { .id = "noop", .priv_size = 0, .enqueue = noop_enqueue, .dequeue = noop_dequeue, .peek = noop_dequeue, .owner = THIS_MODULE, }; static struct netdev_queue noop_netdev_queue = { .qdisc = &noop_qdisc, .qdisc_sleeping = &noop_qdisc, }; struct Qdisc noop_qdisc = { .enqueue = noop_enqueue, .dequeue = noop_dequeue, .flags = TCQ_F_BUILTIN, .ops = &noop_qdisc_ops, .list = LIST_HEAD_INIT(noop_qdisc.list), .dev_queue = &noop_netdev_queue, }; EXPORT_SYMBOL(noop_qdisc);
零長度隊列的默認隊列策略 noqueue_qdisc:
static struct Qdisc_ops noqueue_qdisc_ops __read_mostly = { .id = "noqueue", .priv_size = 0, .enqueue = noop_enqueue, .dequeue = noop_dequeue, .peek = noop_dequeue, .owner = THIS_MODULE, }; static struct netdev_queue noqueue_netdev_queue = { .qdisc = &noqueue_qdisc, .qdisc_sleeping = &noqueue_qdisc, }; static struct Qdisc noqueue_qdisc = { /*隊列長度爲零,入隊函數置爲空, *發送函數根據該字段是否爲空來判斷是否需要緩存報文 */ .enqueue = NULL, .dequeue = noop_dequeue, .flags = TCQ_F_BUILTIN, .ops = &noqueue_qdisc_ops, .list = LIST_HEAD_INIT(noqueue_qdisc.list), .q.lock = __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock), .dev_queue = &noqueue_netdev_queue, };
單隊列時使用的默認隊列策略 pfifo_fast:
該默認隊列策略使用三個優先級隊列來管理報文的發送。根據skb->priority字段設置的優先級來決定報文的發送優先級。
/*默認報文優先級隊列個數爲 3 個*/ #define PFIFO_FAST_BANDS 3 /*隊列策略的私有數據結構 * q:三個不同優先級的報文隊列,數組下標越小優先級越高 * bitmap:記錄三個優先級隊列中哪些有報文需要發送 */ struct pfifo_fast_priv { u32 bitmap; struct sk_buff_head q[PFIFO_FAST_BANDS]; }; /*根據priv->bitmap的值取出有報文要發送的隊列號, *如果有多個隊列都有報文要發送,返回優先級最高的隊列號 */ static const int bitmap2band[] = {-1, 0, 1, 0, 2, 0, 1, 0}; struct sk_buff_head *band2list(struct pfifo_fast_priv *priv, int band) { return priv->q + band; } /*根據skb->priority字段往隊列策略優先級隊列中的映射表*/ static const u8 prio2band[TC_PRIO_MAX+1] = { 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };
入隊和出隊的操作:
static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc) { /*如果隊列沒滿,就緩存報文*/ if (skb_queue_len(&qdisc->q) < qdisc_dev(qdisc)->tx_queue_len) { /*根據skb的優先級找到隊列策略對應的優先級隊列*/ int band = prio2band[skb->priority & TC_PRIO_MAX]; struct pfifo_fast_priv *priv = qdisc_priv(qdisc); struct sk_buff_head *list = band2list(priv, band); /*置位優先級隊列對應的bitmap位*/ priv->bitmap |= (1 << band); qdisc->q.qlen++; /*把報文加入隊列*/ return __qdisc_enqueue_tail(skb, qdisc, list); } /*如果隊列已經滿了,丟棄報文*/ return qdisc_drop(skb, qdisc); }
static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc) { struct pfifo_fast_priv *priv = qdisc_priv(qdisc); /*找到有報文要發送的優先級最高的隊列*/ int band = bitmap2band[priv->bitmap]; if (likely(band >= 0)) { struct sk_buff_head *list = band2list(priv, band); /*從隊列中取一個報文*/ struct sk_buff *skb = __qdisc_dequeue_head(qdisc, list); qdisc->q.qlen--; /*如果隊列爲空,清除bitmap位*/ if (skb_queue_empty(list)) { priv->bitmap &= ~(1 << band); } return skb; } /*沒有隊列有報文要發送,返回NULL*/ return NULL; }
struct Qdisc_ops pfifo_fast_ops __read_mostly = { .id = "pfifo_fast", .priv_size = sizeof(struct pfifo_fast_priv), .enqueue = pfifo_fast_enqueue, .dequeue = pfifo_fast_dequeue, .owner = THIS_MODULE, };
隊列策略的初始化:
在設備創建時會使用noop_qdisc來初始化發送隊列的隊列策略:
int register_netdevice(struct net_device *dev) { 。。。。。。 dev_init_scheduler(dev); 。。。。。。 } void dev_init_scheduler(struct net_device *dev) { dev->qdisc = &noop_qdisc; netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); }
當打開設備時,如果沒創建隊列策略,就會給創建一個默認的隊列策略。
dev_open() { 。。。。。。 dev_activate(dev); 。。。。。。 } void dev_activate(struct net_device *dev) { /*如果dev使用的默認的qisc noop_qdisc, *創建一個新的qdisc*/ if (dev->qdisc == &noop_qdisc) { attach_default_qdiscs(dev); } }
static void attach_one_default_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_unused) { struct Qdisc *qdisc; /*如果隊列長度不爲0,就創建一個發送隊列策略pfifo_fast*/ if (dev->tx_queue_len) { qdisc = qdisc_create_dflt(dev, dev_queue, &pfifo_fast_ops, TC_H_ROOT); if (!qdisc) { printk(KERN_INFO "%s: activation failed\n", dev->name); return; } /* Can by-pass the queue discipline for default qdisc */ qdisc->flags |= TCQ_F_CAN_BYPASS; } /*發送隊列長度爲0,就使用noqueue_qdisc*/ else { qdisc = &noqueue_qdisc; } dev_queue->qdisc_sleeping = qdisc; } static void attach_default_qdiscs(struct net_device *dev) { struct netdev_queue *txq; struct Qdisc *qdisc; /*取得設備的第一個發送隊列*/ txq = netdev_get_tx_queue(dev, 0); /*如果設備只有一個發送隊列或者發送隊列長度爲0, *調用attach_one_default_qdisc創建一個默認隊列策略 */ if (!netif_is_multiqueue(dev) || dev->tx_queue_len == 0) { netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); dev->qdisc = txq->qdisc_sleeping; atomic_inc(&dev->qdisc->refcnt); } else { qdisc = qdisc_create_dflt(dev, txq, &mq_qdisc_ops, TC_H_ROOT); if (qdisc) { qdisc->ops->attach(qdisc); dev->qdisc = qdisc; } } }