网络设备发送队列相关数据结构及其创建函数 (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;
}


你可能感兴趣的:(linux,linux网络协议栈,发送队列dev_queue,队列策略qdisc)