發送隊列的默認隊列策略 (linux網絡子系統學習 第十一節 )

如果網絡設備發送隊列沒有配置發送策略,內核就會使用默認的隊列策略來進行報文的發送。

內核中定義實現了兩種默認的隊列策略,一種是給隊列長度爲零的隊列使用的。一種是給隊列長度不爲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;
        }
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章