網絡設備發送隊列相關數據結構及其創建函數 (linux網絡子系統學習 第十節 )

網絡設備使用隊列來管理數據幀的輸出流量,每個隊列可以使用隊列策略算法來安排發送幀的優先級,使發送過程更高效。詳細的隊列策略處理屬於流量控制子系統的內容,本人還沒來的及研究,這裏先不涉及。本章討論沒配置隊列策略的情況下設備的發送隊列。


數據關聯:


dev->dev_queue->qdisc

網絡設備->發送隊列->隊列策略


每個網絡設備可以有自己的發送隊列,隊列數量可以自定義。

每個發送隊列可以有自己的隊列策略,隊列策略數量可以自定義。



網絡設備可以有自己的發送隊列,也可以沒有自己的發送隊列(這種情況下發送隊列長度爲零)。


1、一般虛擬網絡設備沒有自己的發送隊列。對於沒有發送隊列的網絡設備,其直接調用自己的發送函數進行發送,不需要由發包軟中斷來調度發送。如果發送失敗,就丟棄報文,不進行緩存。



2、有自己的發送隊列的網絡設備,如果發送失敗,會緩存到發送隊列,等待資源可用後再重新發送,這時報文緩存在隊列策略管理的優先級隊列中。其發送報文是由發包軟中斷進行調度來發送的。跟NAPI接收相似,NAPI接收時調度使用NAPI,發送時調度使用隊列策略qdisc。沒有配置隊列策略的發送隊列會使用默認的隊列策略pfifo_fast



數據結構定義:


1、在struct net_device 結構體中定義如下幾個發送對列相關的字段:


struct net_device
{
    /*指向設備的發送隊列列表*/
    struct netdev_queue *_tx;
                                                                                                                                                                                                                                                                   
    /*發送隊列的個數*/
    unsigned int    num_tx_queues;
                                                                                                                                                                                                                                                                 
    /*現在使用的發送隊列個數*/
    unsigned int    real_num_tx_queues;
                                                                                                                                                                                                                                                                 
    /*設備使用的默認隊列策略*/
    struct Qdisc    *qdisc;
                                                                                                                                                                                                                                                                 
    /*每個發送隊列的最大長度*/
    unsigned long    tx_queue_len;
                                                                                                                                                                                                                                                                 
    /*發送隊列的全局鎖*/
    spinlock_t    tx_global_lock;
}


2、發送隊列結構體

struct netdev_queue
{
    /*隊列所屬網絡設備*/
    struct net_device    *dev;
                                                                                                                                                                                                                                                
    /*發送隊列對應的隊列策略結構體鏈表*/
    struct Qdisc    *qdisc;
                                                                                                                                                                                                                                                
    /*隊列的運行狀態*/
    unsigned long    state;
    struct Qdisc    *qdisc_sleeping;
    spinlock_t  _xmit_lock ____cacheline_aligned_in_smp;
                                                                                                                                                                                                                                                 
    /*持有該隊列鎖的cpu id*/
    int    xmit_lock_owner;
                                                                                                                                                                                                                                                
    /*本次隊列最進一次開始發送的時間戳*/
    unsigned long    trans_start;
                                                                                                                                                                                                                                                
    /*隊列對發送報文的統計信息*/
    unsigned long    tx_bytes;
    unsigned long    tx_packets;
    unsigned long    tx_dropped;
} ____cacheline_aligned_in_smp;


3、發送隊列策略結構體

struct Qdisc
{
    /*報文入隊操作函數*/
    int  (*enqueue)(struct sk_buff *skb, struct Qdisc *dev);
    /*報文出隊操作*/
    struct sk_buff *  (*dequeue)(struct Qdisc *dev);
    unsigned    flags;
#define TCQ_F_BUILTIN       1
#define TCQ_F_THROTTLED     2
#define TCQ_F_INGRESS       4
#define TCQ_F_CAN_BYPASS    8
#define TCQ_F_MQROOT        16
#define TCQ_F_WARN_NONWC    (1 << 16)
    /*申請隊列策略內存中爲字節對齊所預留的長度*/
    int    padded;
    /*隊列策略自己的一些操作函數,
     *申請隊列策略時根據這些函數來初始化隊列策略的一些字段
     */
    struct Qdisc_ops    *ops;
                                                                                                                                                                                                                                 
    struct qdisc_size_table    *stab;
    struct list_head    list;
    u32     handle;
    u32     parent;
    /*引用計數*/
    atomic_t    refcnt;
    struct gnet_stats_rate_est  rate_est;
    int    (*reshape_fail)(struct sk_buff *skb,
                           struct Qdisc *q);
    void    *u32_node;
    /* This field is deprecated, but it is still used by CBQ
     * and it will live until better solution will be invented.
     */
    struct Qdisc    *__parent;
                                                                                                                                                                                                                                 
    /*所屬發送隊列*/
    struct netdev_queue *dev_queue;
                                                                                                                                                                                                                                 
    /*有報文發送時把隊列策略掛到softnet_data->out_queue上*/
    struct Qdisc    *next_sched;
                                                                                                                                                                                                                                 
    /*指向最近一次發送失敗的報文,下次發送時優先發送該報文*/
    struct sk_buff    *gso_skb;
    /*
     * For performance sake on SMP,
     *we put highly modified fields at the end
     */
    /*隊列策略的運行調度狀態*/
    unsigned long       state;
    /*隊列策略的默認緩存報文的隊列*/
    struct sk_buff_head q;
                                                                                                                                                                                                                                
    /*一些統計信息*/
    struct gnet_stats_basic_packed bstats;
    struct gnet_stats_queue qstats;
};


流量控制系統中每種隊列策略都提供了自己的函數,供數據鏈路層調用來完成隊列的操作。

enqueue:向隊列加入一個報文。

dequeue:從隊列中摘一個報文。



4、發送隊列的操作函數:

struct Qdisc_ops
{
    struct Qdisc_ops    *next;
    const struct Qdisc_class_ops    *cl_ops;
    /*隊列策略的名字*/
    char    id[IFNAMSIZ];
                                                                                                                                                                                                             
    /*隊列策略的私有數據結構體大小*/
    int     priv_size;
                                                                                                                                                                                                             
    /*入隊操作函數*/
    int     (*enqueue)(struct sk_buff *, struct Qdisc *);
                                                                                                                                                                                                             
    /*出隊操作函數*/
    struct sk_buff *    (*dequeue)(struct Qdisc *);
    /*初始化操作函數*/
    int     (*init)(struct Qdisc *, struct nlattr *arg);
                                                                                                                                                                                                             
    /*把隊列策略和發送隊列關聯的操作函數*/
    void    (*attach)(struct Qdisc *);
                                                                                                                                                                                                             
    struct module    *owner;
};


網絡設備發送隊列的創建


在申請設備時創建dev 的發送隊列,如果調用alloc_netdev()時,默認創建一個隊列。要創建多個發送隊列,調用alloc_netdev_mq().


#define alloc_netdev(sizeof_priv, name, setup) \
    alloc_netdev_mq(sizeof_priv, name, setup, 1)
struct net_device *alloc_netdev_mq(int sizeof_priv,
        const char *name,
        void (*setup)(struct net_device *),
        unsigned int queue_count)
{
    struct netdev_queue *tx;
    struct net_device *dev;
                                                                                                                                                                   
    ......
                                                                                                                                                                   
    tx = kcalloc(queue_count,
                   sizeof(struct netdev_queue),
                   GFP_KERNEL);
    dev->_tx = tx;
    dev->num_tx_queues = queue_count;
    dev->real_num_tx_queues = queue_count;
    netdev_init_queues(dev);
                                                                                                                                                                   
    ......
                                                                                                                                                                   
    return dev;
}


發送隊列策略的創建

struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
              struct Qdisc_ops *ops)
{
    void *p;
    struct Qdisc *sch;
    unsigned int size;
    int err = -ENOBUFS;
    /* ensure that the Qdisc and
     *the private data are 32-byte aligned
     */
    size = QDISC_ALIGN(sizeof(*sch));
                                                                                                         
    /*申請隊列策略時,隊列結構體下緊接着
     *放着隊列策略的私有結構體
     */
    size += ops->priv_size + (QDISC_ALIGNTO - 1);
    p = kzalloc(size, GFP_KERNEL);
    if (!p)
    {
        goto errout;
    }
    sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p);
    /*設置padded 字段*/
    sch->padded = (char *) sch - (char *) p;
    INIT_LIST_HEAD(&sch->list);
    skb_queue_head_init(&sch->q);
    sch->ops = ops;
                                                                                                         
    /*根據ops 裏定義的函數來初始化隊列策略的一些函數指針*/
    sch->enqueue = ops->enqueue;
    sch->dequeue = ops->dequeue;
    sch->dev_queue = dev_queue;
    dev_hold(qdisc_dev(sch));
    atomic_set(&sch->refcnt, 1);
    return sch;
errout:
    return ERR_PTR(err);
}


根據給出的ops ,給發送隊列來創建一個默認的隊列策略

struct Qdisc * qdisc_create_dflt(struct net_device *dev,
                 struct netdev_queue *dev_queue,
                 struct Qdisc_ops *ops,
                 unsigned int parentid)
{
    struct Qdisc *sch;
    /*申請一個隊列策略*/
    sch = qdisc_alloc(dev_queue, ops);
    if (IS_ERR(sch))
    {
        goto errout;
    }
    sch->parent = parentid;
    /*調用ops->init來初始化隊列策略*/
    if (!ops->init || ops->init(sch, NULL) == 0)
    {
        return sch;
    }
    qdisc_destroy(sch);
errout:
    return NULL;
}


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