网络子系统5_设备队列规则 .

本文详细介绍了网络子系统的设备队列规则,包括设备描述符中的关键字段,如tx_queue_len和qdisc,以及设备队列规则的设置时机。在设备注册、开启和关闭时,队列规则有不同的处理方式。初始化时设置为noop_qdisc,开启设备时创建新队列,关闭时恢复为noop_qdisc。此外,还讨论了设备队列的看门狗机制,用于监控传输超时情况。队列规则的实现包括noop_qdisc和pfifo_fast_qdisc,分别对应所有操作都释放skb和基于优先级的FIFO传输策略。
摘要由CSDN通过智能技术生成
  1. //  1.设备描述符与队列规则相关的字段:   
  2. //      1.1 dev->tx_queue_len 指示驱动程序规则队列的队列长度,在注册设备时使用,通知核心是否为设备提供队列规则机制.   
  3. //          1.1.1 不适用队列规则=0   
  4. //          1.1.2 使用队列规则>0   
  5. //      1.2 dev->qdisc,执行设备传输时,qdisc_run,dev_queue_xmit始终通过该字段获取设备当前使用的队列规则。   
  6. //      1.3 dev->qdisc_sleep, 保存设备具备传输能力时,使用的设备队列。   
  7.   
  8. //  2.设备队列规则设置的时机:   
  9. //      2.1 注册设备(register_netdevice)时,设置为noop_qdisc   
  10. //      2.2 开启设备(dev_open)时,创建新的队列规则。   
  11. //      2.3 关闭设备(dev_close)时,设置dev->qdisc为noop_qdisc,表示在设备关闭的过程中,任何的传输都会被丢弃   
  12.   
  13.   
  14. //  初始化设备的队列规则   
  15. //  调用路径:register_netdevice->dev_init_scheduler   
  16. 1.1 void dev_init_scheduler(struct net_device *dev)  
  17.  
  18.     //获取qdisc_tree_lock,dev->queue_lock,并关软中断   
  19.     qdisc_lock_tree(dev);  
  20.     //设置dev的队列规则   
  21.     dev->qdisc &noop_qdisc;  
  22.     dev->qdisc_sleeping &noop_qdisc;  
  23.     INIT_LIST_HEAD(&dev->qdisc_list);  
  24.     //开锁   
  25.     qdisc_unlock_tree(dev);  
  26.     //队列看门狗   
  27.     dev_watchdog_init(dev);  
  28.  
  29.   
  30. //初始化看门狗   
  31. 1.2 static void dev_watchdog_init(struct net_device *dev)  
  32.  
  33.     //初始化定时器   
  34.     init_timer(&dev->watchdog_timer);  
  35.     dev->watchdog_timer.data (unsigned long)dev;  
  36.     //定时器函数   
  37.     dev->watchdog_timer.function dev_watchdog;  
  38.  
  39.   
  40. //  队列规则使用的看门狗定时器   
  41. //  看门狗函数执行的条件:   
  42. //      1.设备在系统中   
  43. //      2.设备处于IFF_UP状态   
  44. //      3.设备有载波   
  45. //      4.传输没有被关闭   
  46. //      5,上一次传输距离现在已经超过了到期时间   
  47. 1.3 static void dev_watchdog(unsigned long arg)  
  48.  
  49.     struct net_device *dev (struct net_device *)arg;  
  50.   
  51.     //持有hard_start_xmit的保护锁   
  52.     spin_lock(&dev->xmit_lock);  
  53.     //判断如果设备的队列规则不是noop_qdisc   
  54.     if (dev->qdisc != &noop_qdisc)  
  55.         //检查设备是否存在,_PRESENT标志   
  56.         if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev))  
  57.             if (netif_queue_stopped(dev) && (jiffies dev->trans_start) dev->watchdog_timeo)  
  58.                 printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name);  
  59.                 //执行注册设备时提供的time_out函数   
  60.                 dev->tx_timeout(dev);  
  61.              
  62.             //修改定时器的下一次到期时间   
  63.             if (!mod_timer(&dev->watchdog_timer, jiffies dev->watchdog_timeo))  
  64.                 //对非活跃的定时器修改到期时间,则同时增加对dev的引用计数   
  65.                 dev_hold(dev);  
  66.          
  67.      
  68.     spin_unlock(&dev->xmit_lock);  
  69.     //释放对dev的引用计数   
  70.     dev_put(dev);  
  71.  
  72.   
  73. //  noop队列规则   
  74. //  所有操作,均将skb释放掉   
  75. 2.1 struct Qdisc noop_qdisc  
  76.     .enqueue      noop_enqueue,//skb入队操作   
  77.     .dequeue      noop_dequeue,//skb出队操作   
  78.     .flags        TCQ_F_BUILTIN,//表示内建的队列规则   
  79.     .ops          &noop_qdisc_ops,//规则操作   
  80.     .list         LIST_HEAD_INIT(noop_qdisc.list),//链表头   
  81. };  
  82.   
  83.   
  84.   
  85. //  分配队列规则:   
  86. //      当设备第一次被开启式时,为设备创建队列规则   
  87. //  参数:   
  88. //      ops为pfifo_fast_ops   
  89.   
  90. //  调用路径:dev_open->dev_activate->qdisc_create_dflt   
  91. 3.1 struct Qdisc qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)  
  92.  
  93.     void *p;  
  94.     struct Qdisc *sch;  
  95.     int size;  
  96.     //队列规则对齐到32字节   
  97.     size ((sizeof(*sch) QDISC_ALIGN_CONST) ~QDISC_ALIGN_CONST);  
  98.     size += ops->priv_size QDISC_ALIGN_CONST;  
  99.     //分配内存   
  100.     kmalloc(size, GFP_KERNEL);  
  101.     if (!p)  
  102.         return NULL;  
  103.     memset(p, 0, size);  
  104.     //对齐到32字节   
  105.     sch (struct Qdisc *)(((unsigned long)p QDISC_ALIGN_CONST)   
  106.                    ~QDISC_ALIGN_CONST);  
  107.     //保存为对齐而浪费的字节   
  108.     sch->padded (char *)sch (char *)p;  
  109.   
  110.     //初始化规则队列链表头,用于链接到dev->qdisc_list   
  111.     INIT_LIST_HEAD(&sch->list);  
  112.     //初始化设备的传输队列   
  113.     skb_queue_head_init(&sch->q);  
  114.     //队列操作   
  115.     sch->ops ops;  
  116.     sch->enqueue ops->enqueue;  
  117.     sch->dequeue ops->dequeue;  
  118.     //此队列关联的设备   
  119.     sch->dev dev;  
  120.     //增加设备引用计数   
  121.     dev_hold(dev);  
  122.     //队列规则的统计变量锁为设备的传输锁   
  123.     //说明队列的统计数据更新依赖于传输队列   
  124.     sch->stats_lock &dev->queue_lock;  
  125.     //队列引用规则为1   
  126.     atomic_set(&sch->refcnt, 1);  
  127.     if (!ops->init || ops->init(sch, NULL) == 0)  
  128.         return sch;  
  129.     //   
  130.     dev_put(dev);  
  131.     kfree(p);  
  132.     return NULL;  
  133.  
  134.   
  135.   
  136.   
  137. //  关闭队列规则   
  138. //  dev_close->dev_deactivate   
  139. 3.2 void dev_deactivate(struct net_device *dev)  
  140.  
  141.     struct Qdisc *qdisc;  
  142.     //关中断,获取队列锁   
  143.     spin_lock_bh(&dev->queue_lock);  
  144.     qdisc dev->qdisc;  
  145.     dev->qdisc &noop_qdisc;//将队列规则修改为noop_qdisc,丢弃所有的传输   
  146.     //调用qdisc->reset操作,将队列规则中所有未传输的skb丢弃掉   
  147.     qdisc_reset(qdisc);  
  148.   
  149.     spin_unlock_bh(&dev->queue_lock);  
  150.     //关闭看门狗   
  151.     dev_watchdog_down(dev);  
  152.     //检查设备是否被调度   
  153.     while (test_bit(__LINK_STATE_SCHED, &dev->state))  
  154.         yield();//将当前进程放到就绪队列,切换到其他进程执行   
  155.   
  156.     //等待dev->xmit_lock没有被任何cpu占用,从而保证在dev_deactivate返回后,没有任何cpu在dev上执行传输   
  157.     spin_unlock_wait(&dev->xmit_lock);  
  158.  
  159.   
  160.   
  161.   
  162. //  fifo队列规则   
  163. 4.1 static struct Qdisc_ops pfifo_fast_ops  
  164.     .next         NULL,  
  165.     .cl_ops       NULL,  
  166.     .id       "pfifo_fast",//规则id   
  167.     .priv_size    sizeof(struct sk_buff_head),//三个skb链表头   
  168.     .enqueue      pfifo_fast_enqueue,//入队   
  169.     .dequeue      pfifo_fast_dequeue,//出队   
  170.     .requeue      pfifo_fast_requeue,//重新入队   
  171.     .init         pfifo_fast_init,//初始化   
  172.     .reset        pfifo_fast_reset,//重置   
  173.     .dump         pfifo_fast_dump,//dump   
  174.     .owner        THIS_MODULE,  
  175. };  
  176.   
  177. //  fifo的入队操作:   
  178. 4.2 static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)  
  179.  
  180.     //规则队列中的队列头   
  181.     struct sk_buff_head *list qdisc_priv(qdisc);  
  182.     //根据skb的优先级,计算skb应该进入的list头   
  183.     list += prio2band[skb->priority&TC_PRIO_MAX];  
  184.     //如果当前列表头中链接的skb个数<设备传输队列的长度   
  185.     if (list->qlen qdisc->dev->tx_queue_len)  
  186.         //将skb挂在到此list最后   
  187.         __skb_queue_tail(list, skb);  
  188.         //增加队列长度、队列中数据byte数、输出封包个数   
  189.         qdisc->q.qlen++;  
  190.         qdisc->bstats.bytes += skb->len;  
  191.         qdisc->bstats.packets++;  
  192.         return 0;  
  193.      
  194.     //该skb应该进入的队列已满,丢掉skb   
  195.     qdisc->qstats.drops++;  
  196.     kfree_skb(skb);  
  197.     return NET_XMIT_DROP;  
  198.  
  199.   
  200. // fifo出队操作:   
  201. 4.3 static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)  
  202.  
  203.     int prio;  
  204.     struct sk_buff_head *list qdisc_priv(qdisc);  
  205.     struct sk_buff *skb;  
  206.   
  207.     //3个sk_buff_head,0-3优先级递减   
  208.     for (prio 0; prio 3; prio++, list++)  
  209.         //从最高优先级的sk_buff_head出队一个skb   
  210.         skb __skb_dequeue(list);  
  211.         if (skb)  
  212.             //递减规则队列的包个数   
  213.             qdisc->q.qlen--;  
  214.             return skb;  
  215.          
  216.      
  217.     return NULL;  
  218.  
  219.   
  220. //  fifo重新入队操作:   
  221. //  调用时机:   
  222. //      出队skb后,发现hard_start_xmit的锁被获取,则重新入队skb   
  223. 4.4 static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)  
  224.  
  225.     struct sk_buff_head *list qdisc_priv(qdisc);  
  226.     //计算skb优先级对应的head   
  227.     list += prio2band[skb->priority&TC_PRIO_MAX];  
  228.     //入队   
  229.     __skb_queue_head(list, skb);  
  230.     //更新长度与统计变量   
  231.     qdisc->q.qlen++;  
  232.     qdisc->qstats.requeues++;  
  233.     return 0;  
  234.  
  235.   
  236. //  fifo 复位队列   
  237. //  清空队列中的所有skb   
  238. 4.5 static void pfifo_fast_reset(struct Qdisc* qdisc)  
  239.  
  240.     int prio;  
  241.     struct sk_buff_head *list qdisc_priv(qdisc);  
  242.     //0-3优先级依次清空队列头   
  243.     for (prio=0; prio 3; prio++)  
  244.         skb_queue_purge(list+prio);  
  245.     qdisc->q.qlen 0;  
  246.  
  247.   
  248. //  清空链表   
  249. //  pfifo_fast_reset->skb_queue_purge   
  250. 4.6 void skb_queue_purge(struct sk_buff_head *list)  
  251.  
  252.     struct sk_buff *skb;  
  253.     //出链表后释放   
  254.     while ((skb skb_dequeue(list)) != NULL)  
  255.         kfree_skb(skb);  
  256.  
  257.   
  258.   
  259. //规则队列初始化   
  260. 4.7 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)  
  261.  
  262.     int i;  
  263.     struct sk_buff_head *list qdisc_priv(qdisc);  
  264.     //初始化3个sk_buff_head   
  265.     for (i=0; i<3; i++)  
  266.         skb_queue_head_init(list+i);  
  267.   
  268.     return 0;  
  269.  
  270.   
  271. //skb->priority -> sk_buff_head的映射   
  272. 4.8 static const u8 prio2band[TC_PRIO_MAX+1]  
  273.     1, 2, 2, 2, 1, 2, 0, 1, 1, 1, 1, 1, 1, 1, };  
  274.   
  275. //删除队列规则   
  276. //调用路径unregister_netdevice->dev_shutdown->qdisc_destroy   
  277. void qdisc_destroy(struct Qdisc *qdisc)  
  278.  
  279.     struct list_head cql LIST_HEAD_INIT(cql);  
  280.     struct Qdisc *cq, *q, *n;  
  281.   
  282.     if (qdisc->flags TCQ_F_BUILTIN ||//noop_qdisc 设置此标识   
  283.         !atomic_dec_and_test(&qdisc->refcnt))//pfifo_fast_ops 在初始化时,设置引用计数为1   
  284.                 return;  
  285.   
  286.     if (!list_empty(&qdisc->list)) {//由于将qdisc链接到dev->qdisc_list,因此此判断失败   
  287.         if (qdisc->ops->cl_ops == NULL)//pfifo_fast_ops 的此字段为null   
  288.             list_del(&qdisc->list);  
  289.         else  
  290.             list_move(&qdisc->list, &cql);  
  291.      
  292.   
  293.     list_for_each_entry(cq, &cql, list)//由于pfifo_fast_ops->cl_ops为null,所以cql为空,此循环不执行   
  294.         list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list)  
  295.             if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle))  
  296.                 if (q->ops->cl_ops == NULL)  
  297.                     list_del_init(&q->list);  
  298.                 else  
  299.                     list_move_tail(&q->list, &cql);  
  300.              
  301.     list_for_each_entry_safe(cq, n, &cql, list)//不执行   
  302.         list_del_init(&cq->list);  
  303.   
  304.     call_rcu(&qdisc->q_rcu, __qdisc_destroy);//直接调用__qdisc_destroy,kfree(qdisc)   
  305.  
//      1.设备描述符与队列规则相关的字段:
//              1.1 dev->tx_queue_len 指示驱动程序规则队列的队列长度,在注册设备时使用,通知核心是否为设备提供队列规则机制.
//                      1.1.1 不适用队列规则=0
//                      1.1.2 使用队列规则>0
//              1.2 dev->qdisc,执行设备传输时,qdisc_run,dev_queue_xmit始终通过该字段获取设备当前使用的队列规则。
//              1.3 dev->qdisc_sleep, 保存设备具备传输能力时,使用的设备队列。

//      2.设备队列规则设置的时机:
//              2.1 注册设备(register_netdevice)时,设置为noop_qdisc
//              2.2 开启设备(dev_open)时,创建新的队列规则。
//              2.3 关闭设备(dev_close)时,设置dev->qdisc为noop_qdisc,表示在设备关闭的过程中,任何的传输都会被丢弃


//      初始化设备的队列规则
//      调用路径:register_netdevice->dev_init_scheduler
1.1 void dev_init_scheduler(struct net_device *dev)
{
        //获取qdisc_tree_lock,dev->queue_lock,并关软中断
        qdisc_lock_tree(dev);
        //设置dev的队列规则
        dev->qdisc = &noop_qdisc;
        dev->qdisc_sleeping = &noop_qdisc;
        INIT_LIST_HEAD(&dev->qdisc_list);
        //开锁
        qdisc_unlock_tree(dev);
        //队列看门狗
        dev_watchdog_init(dev);
}

//初始化看门狗
1.2 static void dev_watchdog_init(struct net_device *dev)
{
        //初始化定时器
        init_timer(&dev->watchdog_timer);
        dev->watchdog_timer.data = (unsigned long)dev;
        //定时器函数
        dev->watchdog_timer.function = dev_watchdog;
}

//      队列规则使用的看门狗定时器
//      看门狗函数执行的条件:
//              1.设备在系统中
//              2.设备处于IFF_UP状态
//              3.设备有载波
//              4.传输没有被关闭
//              5,上一次传输距离现在已经超过了到期时间
1.3 static void dev_watchdog(unsigned long arg)
{
        struct net_device *dev = (struct net_device *)arg;

        //持有hard_start_xmit的保护锁
        spin_lock(&dev->xmit_lock);
        //判断如果设备的队列规则不是noop_qdisc
        if (dev->qdisc != &noop_qdisc) {
                //检查设备是否存在,_PRESENT标志
                if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) {
                        if (netif_queue_stopped(dev) && (jiffies - dev->trans_start) > dev->watchdog_timeo) {
                                printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name);
                                //执行注册设备时提供的time_out函数
                                dev->tx_timeout(dev);
                        }
                        //修改定时器的下一次到期时间
                        if (!mod_timer(&dev->watchdog_timer, jiffies + dev->watchdog_timeo))
                                //对非活跃的定时器修改到期时间,则同时增加对dev的引用计数
                                dev_hold(dev);
                }
        }
        spin_unlock(&dev->xmit_lock);
        //释放对dev的引用计数
        dev_put(dev);
}

//      noop队列规则
//      所有操作,均将skb释放掉
2.1 struct Qdisc noop_qdisc = {
        .enqueue        =       noop_enqueue,//skb入队操作
        .dequeue        =       noop_dequeue,//skb出队操作
        .flags          =       TCQ_F_BUILTIN,//表示内建的队列规则
        .ops            =       &noop_qdisc_ops,//规则操作
        .list           =       LIST_HEAD_INIT(noop_qdisc.list),//链表头
};



//      分配队列规则:
//              当设备第一次被开启式时,为设备创建队列规则
//      参数:
//              ops为pfifo_fast_ops

//      调用路径:dev_open->dev_activate->qdisc_create_dflt
3.1 struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops)
{
        void *p;
        struct Qdisc *sch;
        int size;
        //队列规则对齐到32字节
        size = ((sizeof(*sch) + QDISC_ALIGN_CONST) & ~QDISC_ALIGN_CONST);
        size += ops->priv_size + QDISC_ALIGN_CONST;
        //分配内存
        p = kmalloc(size, GFP_KERNEL);
        if (!p)
                return NULL;
        memset(p, 0, size);
        //对齐到32字节
        sch = (struct Qdisc *)(((unsigned long)p + QDISC_ALIGN_CONST) 
                               & ~QDISC_ALIGN_CONST);
        //保存为对齐而浪费的字节
        sch->padded = (char *)sch - (char *)p;

        //初始化规则队列链表头,用于链接到dev->qdisc_list
        INIT_LIST_HEAD(&sch->list);
        //初始化设备的传输队列
        skb_queue_head_init(&sch->q);
        //队列操作
        sch->ops = ops;
        sch->enqueue = ops->enqueue;
        sch->dequeue = ops->dequeue;
        //此队列关联的设备
        sch->dev = dev;
        //增加设备引用计数
        dev_hold(dev);
        //队列规则的统计变量锁为设备的传输锁
        //说明队列的统计数据更新依赖于传输队列
        sch->stats_lock = &dev->queue_lock;
        //队列引用规则为1
        atomic_set(&sch->refcnt, 1);
        if (!ops->init || ops->init(sch, NULL) == 0)
                return sch;
        //
        dev_put(dev);
        kfree(p);
        return NULL;
}



//      关闭队列规则
//      dev_close->dev_deactivate
3.2 void dev_deactivate(struct net_device *dev)
{
        struct Qdisc *qdisc;
        //关中断,获取队列锁
        spin_lock_bh(&dev->queue_lock);
        qdisc = dev->qdisc;
        dev->qdisc = &noop_qdisc;//将队列规则修改为noop_qdisc,丢弃所有的传输
        //调用qdisc->reset操作,将队列规则中所有未传输的skb丢弃掉
        qdisc_reset(qdisc);

        spin_unlock_bh(&dev->queue_lock);
        //关闭看门狗
        dev_watchdog_down(dev);
        //检查设备是否被调度
        while (test_bit(__LINK_STATE_SCHED, &dev->state))
                yield();//将当前进程放到就绪队列,切换到其他进程执行

        //等待dev->xmit_lock没有被任何cpu占用,从而保证在dev_deactivate返回后,没有任何cpu在dev上执行传输
        spin_unlock_wait(&dev->xmit_lock);
}



//      fifo队列规则
4.1 static struct Qdisc_ops pfifo_fast_ops = {
        .next           =       NULL,
        .cl_ops         =       NULL,
        .id             =       "pfifo_fast",//规则id
        .priv_size      =       3 * sizeof(struct sk_buff_head),//三个skb链表头
        .enqueue        =       pfifo_fast_enqueue,//入队
        .dequeue        =       pfifo_fast_dequeue,//出队
        .requeue        =       pfifo_fast_requeue,//重新入队
        .init           =       pfifo_fast_init,//初始化
        .reset          =       pfifo_fast_reset,//重置
        .dump           =       pfifo_fast_dump,//dump
        .owner          =       THIS_MODULE,
};

//      fifo的入队操作:
4.2 static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
{
        //规则队列中的队列头
        struct sk_buff_head *list = qdisc_priv(qdisc);
        //根据skb的优先级,计算skb应该进入的list头
        list += prio2band[skb->priority&TC_PRIO_MAX];
        //如果当前列表头中链接的skb个数<设备传输队列的长度
        if (list->qlen < qdisc->dev->tx_queue_len) {
                //将skb挂在到此list最后
                __skb_queue_tail(list, skb);
                //增加队列长度、队列中数据byte数、输出封包个数
                qdisc->q.qlen++;
                qdisc->bstats.bytes += skb->len;
                qdisc->bstats.packets++;
                return 0;
        }
        //该skb应该进入的队列已满,丢掉skb
        qdisc->qstats.drops++;
        kfree_skb(skb);
        return NET_XMIT_DROP;
}

// fifo出队操作:
4.3 static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
{
        int prio;
        struct sk_buff_head *list = qdisc_priv(qdisc);
        struct sk_buff *skb;

        //3个sk_buff_head,0-3优先级递减
        for (prio = 0; prio < 3; prio++, list++) {
                //从最高优先级的sk_buff_head出队一个skb
                skb = __skb_dequeue(list);
                if (skb) {
                        //递减规则队列的包个数
                        qdisc->q.qlen--;
                        return skb;
                }
        }
        return NULL;
}

//      fifo重新入队操作:
//      调用时机:
//              出队skb后,发现hard_start_xmit的锁被获取,则重新入队skb
4.4 static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
{
        struct sk_buff_head *list = qdisc_priv(qdisc);
        //计算skb优先级对应的head
        list += prio2band[skb->priority&TC_PRIO_MAX];
        //入队
        __skb_queue_head(list, skb);
        //更新长度与统计变量
        qdisc->q.qlen++;
        qdisc->qstats.requeues++;
        return 0;
}

//      fifo 复位队列
//      清空队列中的所有skb
4.5 static void pfifo_fast_reset(struct Qdisc* qdisc)
{
        int prio;
        struct sk_buff_head *list = qdisc_priv(qdisc);
        //0-3优先级依次清空队列头
        for (prio=0; prio < 3; prio++)
                skb_queue_purge(list+prio);
        qdisc->q.qlen = 0;
}

//      清空链表
//      pfifo_fast_reset->skb_queue_purge
4.6 void skb_queue_purge(struct sk_buff_head *list)
{
        struct sk_buff *skb;
        //出链表后释放
        while ((skb = skb_dequeue(list)) != NULL)
                kfree_skb(skb);
}


//规则队列初始化
4.7 static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
{
        int i;
        struct sk_buff_head *list = qdisc_priv(qdisc);
        //初始化3个sk_buff_head
        for (i=0; i<3; i++)
                skb_queue_head_init(list+i);

        return 0;
}

//skb->priority -> sk_buff_head的映射
4.8 static const u8 prio2band[TC_PRIO_MAX+1] =
        { 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };

//删除队列规则
//调用路径unregister_netdevice->dev_shutdown->qdisc_destroy
void qdisc_destroy(struct Qdisc *qdisc)
{
        struct list_head cql = LIST_HEAD_INIT(cql);
        struct Qdisc *cq, *q, *n;

        if (qdisc->flags & TCQ_F_BUILTIN ||//noop_qdisc 设置此标识
                !atomic_dec_and_test(&qdisc->refcnt))//pfifo_fast_ops 在初始化时,设置引用计数为1
                                return;

        if (!list_empty(&qdisc->list)) {//由于将qdisc链接到dev->qdisc_list,因此此判断失败
                if (qdisc->ops->cl_ops == NULL)//pfifo_fast_ops 的此字段为null
                        list_del(&qdisc->list);
                else
                        list_move(&qdisc->list, &cql);
        }

        list_for_each_entry(cq, &cql, list)//由于pfifo_fast_ops->cl_ops为null,所以cql为空,此循环不执行
                list_for_each_entry_safe(q, n, &qdisc->dev->qdisc_list, list)
                        if (TC_H_MAJ(q->parent) == TC_H_MAJ(cq->handle)) {
                                if (q->ops->cl_ops == NULL)
                                        list_del_init(&q->list);
                                else
                                        list_move_tail(&q->list, &cql);
                        }
        list_for_each_entry_safe(cq, n, &cql, list)//不执行
                list_del_init(&cq->list);

        call_rcu(&qdisc->q_rcu, __qdisc_destroy);//直接调用__qdisc_destroy,kfree(qdisc)
}

更多 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值