Chinaunix首页 | 论坛 | 博客
  • 博客访问: 476868
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 508
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-10 13:18
个人简介

Keep looking Donot settle

文章分类

全部博文(144)

文章存档

2019年(1)

2016年(31)

2015年(51)

2014年(61)

分类: 嵌入式

2014-10-28 15:29:48

我们知道对应每一个设备都有一个或几个发送的队列,它们是在驱动加载的时候就初始化的。

RTL8139的发送队列只有一个,在alloc_netdev_mq中初始化:

tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL);

         if (!tx) {

                   printk(KERN_ERR "alloc_netdev: Unable to allocate "

                          "tx qdiscs./n");

                   kfree(p);

                   return NULL;

         }

。。。。。。。。。。。

dev->_tx = tx;   /*结合分析Ethernet wathchdog时的netif_stop_queue*/

………………………………………………………

而对应于队列有2个状态位:

enum netdev_queue_state_t

{

         __QUEUE_STATE_XOFF,                这是个和流控相关的状态,netif_stop_queuenetif_start_queuenetif_wake_queue都是和它有关

         __QUEUE_STATE_FROZEN,

};

而对应于每个队列都有个Qdisc结构,它是真正描述队列的数据结构,像排队发送的数据包都存放这里,数据包的出队,入队等策略都是它描述的。

这个是在调用注册函数register_netdevice的时候初始化的dev_init_scheduler netdev_for_each_tx_queue

同时Qdisc也有几个状态位:

enum qdisc_state_t

{

         __QDISC_STATE_RUNNING,              每次调用qdisc_run的时候就置位,调用完成后就清位

         __QDISC_STATE_SCHED,                    表面所在的队列加到了CPUsoftnet_dataoutput_queue中,以在软中断中得以处理

         __QDISC_STATE_DEACTIVATED,

};

 

 

发送软中断触发的条件是:

1 __netif_reschedule或者其封装函数netif_wake_queue等;

2 释放已发送的skb

当然后者比较容易看懂,前者则比较复杂

在前面的发送流程中,就可以看到几种情况:

1 qdisc_restart中发送失败,数据包重新入队,且调用__netif_reschedule来等待下次软中断的处理

2 发送没有完成(没有全部发出),但是CPU时间已到或者时间限制,调用__netif_reschedule来等待下次软中断的处理

__netif_reschedule调用的时候会把当前队列加入到CPUsoftnet_dataoutput_queue中,以在软中断中得以处理;

注意的是:只有当前队列的__QDISC_STATE_SCHED没有置位的时候,才能加入

因为__QDISC_STATE_SCHED置位的话,则表明已经在output_queue中了。

 

下面看看net_tx_action软中断处理函数

static void net_tx_action(struct softirq_action *h)

{

         struct softnet_data *sd = &__get_cpu_var(softnet_data);

         // 完成对skb的回收

         if (sd->completion_queue) {

                   struct sk_buff *clist;

 

                   local_irq_disable();

                   clist = sd->completion_queue;

                   sd->completion_queue = NULL;

                   local_irq_enable();

 

                   while (clist) {

                            struct sk_buff *skb = clist;

                            clist = clist->next;

 

                            WARN_ON(atomic_read(&skb->users));

                            __kfree_skb(skb);

                   }

         }

         // 发送数据包

         if (sd->output_queue) {

                   struct Qdisc *head;

 

                   local_irq_disable();

                   // 取出CPU发送队列的所有网卡设备的发送队列,并置其为空

                   head = sd->output_queue;

                   sd->output_queue = NULL;

                   local_irq_enable();

 

                   while (head) {

                            struct Qdisc *q = head;

                            spinlock_t *root_lock;

 

                            head = head->next_sched;

 

                            root_lock = qdisc_lock(q);

                            if (spin_trylock(root_lock)) {

                                     smp_mb__before_clear_bit();

                                     // 因为该队列已经从CPUoutput_queue中取出,所以清除__QDISC_STATE_SCHED,以期下次可以再被调度

                                     clear_bit(__QDISC_STATE_SCHED,

                                                 &q->state);

                                     // 处理该队列,在该函数中,如果没有处理完或者发送出现问题,该队列调用__netif_reschedule再次加入output_queue

                                     qdisc_run(q);

                                     spin_unlock(root_lock);

                            } else {

                                     // 如果获取锁失败,并且队列没有被__QDISC_STATE_DEACTIVATED,就放回output_queue

                                     if (!test_bit(__QDISC_STATE_DEACTIVATED,

                                                     &q->state)) {

                                               __netif_reschedule(q);

                                     } else {

                                               // __QDISC_STATE_DEACTIVATED置位了,那么清除__QDISC_STATE_SCHED,以期下次__QDISC_STATE_DEACTIVATED

                                               // 清位后,可以调用__netif_reschedule再次加入output_queue

                                               smp_mb__before_clear_bit();

                                               clear_bit(__QDISC_STATE_SCHED,&q->state);

                                     }

                            }

                   }

         }

}

剩下一种情况就是在网卡驱动中对队列的操作:停止发送队列,以及唤醒发送队列。

当网卡驱动没有剩余的空间可以接纳数据时,就调用netif_stop_queue来阻止数据包的发送,(在qdisc_run的调用中碰到这种情况时,不会把该网卡的队列调用__netif_reschedule加入output_queue),加入output_queue是在netif_wake_queue完成的,该函数在发送数据包完成后产生的中断函数中被调用,如果说在调用的时候队列是stopped,那么就调用__netif_schedule把该队列加入到output_queue中(我觉得这个时候__QDISC_STATE_SCHED肯定是处于复位的状态,所以总能加入output_queue)。

from: http://blog.csdn.net/chensichensi/article/details/3974254

阅读(2994) | 评论(0) | 转发(0) |
0

上一篇:cacheline_aligned

下一篇:extern C的作用详解

给主人留下些什么吧!~~