本文来源:
http://blog.csdn.net/peimichael/article/details/4699609当上层准备好一个包之后,交给下面这个函数处理:
- int dev_queue_xmit(struct sk_buff *skb)
- {
- struct net_device *dev = skb->dev;
- struct netdev_queue *txq;
- struct Qdisc *q;
- int rc = -ENOMEM;
-
- if (netif_needs_gso(dev, skb))
- goto gso;
-
-
-
-
-
- if (skb_shinfo(skb)->frag_list &&
- !(dev->features & NETIF_F_FRAGLIST) &&
- __skb_linearize(skb))
- goto out_kfree_skb;
-
-
-
-
-
-
-
- if (skb_shinfo(skb)->nr_frags &&
- (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
- __skb_linearize(skb))
- goto out_kfree_skb;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- skb_set_transport_header(skb, skb->csum_start -
- skb_headroom(skb));
- if (!dev_can_checksum(dev, skb) && skb_checksum_help(skb))
- goto out_kfree_skb;
- }
- gso:
-
-
-
- rcu_read_lock_bh();
-
-
-
- txq = dev_pick_tx(dev, skb);
-
- q = rcu_dereference(txq->qdisc);
- #ifdef CONFIG_NET_CLS_ACT
- skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS);
- #endif
-
-
-
-
-
-
- if (q->enqueue) {
- spinlock_t *root_lock = qdisc_lock(q);
- spin_lock(root_lock);
- if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
- kfree_skb(skb);
- rc = NET_XMIT_DROP;
- } else {
-
- rc = qdisc_enqueue_root(skb, q);
- qdisc_run(q);
- }
- spin_unlock(root_lock);
- goto out;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- if (dev->flags & IFF_UP) {
- int cpu = smp_processor_id();
- if (txq->xmit_lock_owner != cpu) {
- HARD_TX_LOCK(dev, txq, cpu);
- if (!netif_tx_queue_stopped(txq)) {
- rc = 0;
-
-
-
-
- if (!dev_hard_start_xmit(skb, dev, txq)) {
- HARD_TX_UNLOCK(dev, txq);
- goto out;
- }
- }
- HARD_TX_UNLOCK(dev, txq);
- if (net_ratelimit())
- printk(KERN_CRIT "Virtual device %s asks to "
- "queue packet!\n", dev->name);
- } else {
-
-
- if (net_ratelimit())
- printk(KERN_CRIT "Dead loop on virtual device "
- "%s, fix it urgently!\n", dev->name);
- }
- }
- rc = -ENETDOWN;
- rcu_read_unlock_bh();
- out_kfree_skb:
- kfree_skb(skb);
- return rc;
- out:
- rcu_read_unlock_bh();
- return rc;
- }
从此函数可以看出,当驱动使用发送队列的时候会循环从队列中取出包发送
而不使用队列的时候只发送一次,如果没发送成功就直接丢弃
- int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
- struct netdev_queue *txq)
- {
- if (likely(!skb->next)) {
-
-
-
-
- if (!list_empty(&ptype_all))
- dev_queue_xmit_nit(skb, dev);
- if (netif_needs_gso(dev, skb)) {
- if (unlikely(dev_gso_segment(skb)))
- goto out_kfree_skb;
- if (skb->next)
- goto gso;
- }
-
- return dev->hard_start_xmit(skb, dev);
- }
- gso:
- do {
- struct sk_buff *nskb = skb->next;
- int rc;
- skb->next = nskb->next;
- nskb->next = NULL;
- rc = dev->hard_start_xmit(nskb, dev);
- if (unlikely(rc)) {
- nskb->next = skb->next;
- skb->next = nskb;
- return rc;
- }
- if (unlikely(netif_tx_queue_stopped(txq) && skb->next))
- return NETDEV_TX_BUSY;
- } while (skb->next);
- skb->destructor = DEV_GSO_CB(skb)->destructor;
- out_kfree_skb:
- kfree_skb(skb);
- return 0;
- }
qdisc_run和__qdisc_run的功能很简单,就是检查队列是否处于运行状态
- static inline void qdisc_run(struct Qdisc *q)
- {
- if (!test_and_set_bit(__QDISC_STATE_RUNNING, &q->state))
- __qdisc_run(q);
- }
- void __qdisc_run(struct Qdisc *q)
- {
- unsigned long start_time = jiffies;
-
- while (qdisc_restart(q)) {
-
-
-
-
-
- if (need_resched() || jiffies != start_time) {
-
- __netif_schedule(q);
- break;
- }
- }
- clear_bit(__QDISC_STATE_RUNNING, &q->state);
- }
然后循环调用qdisc_restart发送数据
下面这个函数qdisc_restart是真正发送数据包的函数
它从队列上取下一个帧,然后尝试将它发送出去
若发送失败则一般是重新入队。
此函数返回值为:发送成功时返回剩余队列长度
发送失败时返回0(若发送成功且剩余队列长度为0也返回0)
- static inline int qdisc_restart(struct Qdisc *q)
- {
- struct netdev_queue *txq;
- int ret = NETDEV_TX_BUSY;
- struct net_device *dev;
- spinlock_t *root_lock;
- struct sk_buff *skb;
-
- if (unlikely((skb = dequeue_skb(q)) == NULL))
- return 0;
- root_lock = qdisc_lock(q);
-
- spin_unlock(root_lock);
- dev = qdisc_dev(q);
- txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
- HARD_TX_LOCK(dev, txq, smp_processor_id());
- if (!netif_tx_queue_stopped(txq) &&
- !netif_tx_queue_frozen(txq))
- ret = dev_hard_start_xmit(skb, dev, txq);
- HARD_TX_UNLOCK(dev, txq);
- spin_lock(root_lock);
- switch (ret) {
- case NETDEV_TX_OK:
-
- ret = qdisc_qlen(q);
- break;
- case NETDEV_TX_LOCKED:
-
-
-
-
-
- ret = handle_dev_cpu_collision(skb, txq, q);
- break;
- default:
-
-
-
- if (unlikely (ret != NETDEV_TX_BUSY && net_ratelimit()))
- printk(KERN_WARNING "BUG %s code %d qlen %d\n",
- dev->name, ret, q->q.qlen);
- ret = dev_requeue_skb(skb, q);
- break;
- }
- if (ret && (netif_tx_queue_stopped(txq) ||
- netif_tx_queue_frozen(txq)))
- ret = 0;
- return ret;
- }
至此,dev_queue_xmit到驱动层的发送流程就分析完了。
已经有了dev_queue_xmit函数,为什么还需要软中断来发送呢?
我们可以看到在dev_queue_xmit中将skb进行了一些处理(比如合并成一个包,计算校验和等)
处理完的skb是可以直接发送的了,这时dev_queue_xmit也会先将skb入队(skb一般都是在这个函数中入队的)
并且调用qdisc_run尝试发送,但是有可能发送失败,这时就将skb重新入队,调度软中断,并且自己直接返回。
软中断只是发送队列中的skb以及释放已经发送的skb,它无需再对skb进行线性化或者校验和处理
另外在队列被停止的情况下,dev_queue_xmit仍然可以把包加入队列,但是不能发送
这样在队列被唤醒的时候就需要通过软中断来发送停止期间积压的包
简而言之,dev_queue_xmit是对skb做些最后的处理并且第一次尝试发送,软中断是将前者发送失败或者没发完的包发送出去。
(其实发送软中断还有一个作用,就是释放已经发送的包,因为某些情况下发送是在硬件中断中完成的,
为了提高硬件中断处理效率,内核提供一种方式将释放skb放到软中断中进行,
这时只要调用dev_kfree_skb_irq,它将skb加入softnet_data的completion_queue中,然后开启发送软中断,
net_tx_action会在软中断中将completion_queue中的skb全部释放掉)
阅读(859) | 评论(0) | 转发(0) |