Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1919600
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: LINUX

2012-10-31 10:58:03

int dev_queue_xmit(struct sk_buff *skb)
{
    struct net_device *dev = skb->dev;
    struct netdev_queue *txq;
    struct Qdisc *q;
    int rc = -ENOMEM;

    /* GSO will handle the following emulations directly. */
    if (netif_needs_gso(dev, skb))
        goto gso;

    /* Convert a paged skb to linear, if required */
    if (skb_needs_linearize(skb, dev) && __skb_linearize(skb))
        goto out_kfree_skb;

    /* If packet is not checksummed and device does not support
     * checksumming for this protocol, complete checksumming here.
     */
    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:
    /* Disable soft irqs for various locks below. Also
     * stops preemption for RCU.
     */
    rcu_read_lock_bh();

    txq = dev_pick_tx(dev, skb);//选择设备队列
    q = rcu_dereference_bh(txq->qdisc);

#ifdef CONFIG_NET_CLS_ACT
    skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
    if (q->enqueue) {
        rc = __dev_xmit_skb(skb, q, dev, txq);//发送
        goto out;
    }

    /* The device has no queue. Common case for software devices:
       loopback, all the sorts of tunnels...

       Really, it is unlikely that netif_tx_lock protection is necessary
       here.  (f.e. loopback and IP tunnels are clean ignoring statistics
       counters.)
       However, it is possible, that they rely on protection
       made by us here.

       Check this and shot the lock. It is not prone from deadlocks.
       Either shot noqueue qdisc, it is even simpler 8)
     */
    if (dev->flags & IFF_UP) {
        int cpu = smp_processor_id(); /* ok because BHs are off */

        if (txq->xmit_lock_owner != cpu) {

            HARD_TX_LOCK(dev, txq, cpu);

            if (!netif_tx_queue_stopped(txq)) {
                rc = dev_hard_start_xmit(skb, dev, txq);
                if (dev_xmit_complete(rc)) {
                    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 {
            /* Recursion is detected! It is possible,
             * unfortunately */
            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;
}

static struct netdev_queue *dev_pick_tx(struct net_device *dev,
                    struct sk_buff *skb)
{
    int queue_index;
    struct sock *sk = skb->sk;

    queue_index = sk_tx_queue_get(sk);//skb如果有sk,则使用skb的队列,否则返回-1
    if (queue_index < 0) {//如果小于0,则要选择一个队列
        const struct net_device_ops *ops = dev->netdev_ops;

        if (ops->ndo_select_queue) {//如果设备驱动提供了队列选择函数,则由设备队列选择函数确定队列
            queue_index = ops->ndo_select_queue(dev, skb);
            queue_index = dev_cap_txqueue(dev, queue_index);
        } else {
            queue_index = 0;
            if (dev->real_num_tx_queues > 1)//如果设备提供多于1个队列
                queue_index = skb_tx_hash(dev, skb);//则调用skb_tx_hash选择

            if (sk) {
                struct dst_entry *dst = rcu_dereference_check(sk->sk_dst_cache, 1);

                if (dst && skb_dst(skb) == dst)
                    sk_tx_queue_set(sk, queue_index);
            }
        }
    }

    skb_set_queue_mapping(skb, queue_index);//记录队列索引
    return netdev_get_tx_queue(dev, queue_index);返回队列索引对于的队列
}


static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
                 struct net_device *dev,
                 struct netdev_queue *txq)
{
    spinlock_t *root_lock = qdisc_lock(q);
    int rc;

    spin_lock(root_lock);
    if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
        kfree_skb(skb);
        rc = NET_XMIT_DROP;
    } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
           !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) {
        /*
         * This is a work-conserving queue; there are no old skbs
         * waiting to be sent out; and the qdisc is not running -
         * xmit the skb directly.
         */
        if (!(dev->priv_flags & IFF_XMIT_DST_RELEASE))
            skb_dst_force(skb);
        __qdisc_update_bstats(q, skb->len);
        if (sch_direct_xmit(skb, q, dev, txq, root_lock))
            __qdisc_run(q);
        else
            clear_bit(__QDISC_STATE_RUNNING, &q->state);

        rc = NET_XMIT_SUCCESS;
    } else {
        skb_dst_force(skb);
        rc = qdisc_enqueue_root(skb, q);
        qdisc_run(q);
    }
    spin_unlock(root_lock);

    return rc;
}


int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
            struct net_device *dev, struct netdev_queue *txq,
            spinlock_t *root_lock)
{
    int ret = NETDEV_TX_BUSY;

    /* And release qdisc */
    spin_unlock(root_lock);//释放root_lock

    HARD_TX_LOCK(dev, txq, smp_processor_id());//处理器smp_processor_id获得txq队列锁
    if (!netif_tx_queue_stopped(txq) && !netif_tx_queue_frozen(txq))//如果队列没有stop且没有frozen
        ret = dev_hard_start_xmit(skb, dev, txq);//发送出去

    HARD_TX_UNLOCK(dev, txq);

    spin_lock(root_lock);

    if (dev_xmit_complete(ret)) {
        /* Driver sent out skb successfully or skb was consumed */
        ret = qdisc_qlen(q);
    } else if (ret == NETDEV_TX_LOCKED) {
        /* Driver try lock failed */
        ret = handle_dev_cpu_collision(skb, txq, q);
    } else {
        /* Driver returned NETDEV_TX_BUSY - requeue skb */
        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);
    }

    if (ret && (netif_tx_queue_stopped(txq) ||
            netif_tx_queue_frozen(txq)))
        ret = 0;

    return ret;
}

int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
            struct netdev_queue *txq)
{
    const struct net_device_ops *ops = dev->netdev_ops;
    int rc = NETDEV_TX_OK;

    if (likely(!skb->next)) {//不为空,则表示需要做GSO
        if (!list_empty(&ptype_all))
            dev_queue_xmit_nit(skb, dev);

        /*
         * If device doesnt need skb->dst, release it right now while
         * its hot in this cpu cache
         */
        if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
            skb_dst_drop(skb);

        skb_orphan_try(skb);

        if (netif_needs_gso(dev, skb)) {
            if (unlikely(dev_gso_segment(skb)))
                goto out_kfree_skb;
            if (skb->next)
                goto gso;
        }

        rc = ops->ndo_start_xmit(skb, dev);//调用网卡驱动的发送函数
        if (rc == NETDEV_TX_OK)
            txq_trans_update(txq);//更新队列传输时间
        return rc;
    }

gso:
    do {
        struct sk_buff *nskb = skb->next;//把需要传输的SKB赋值给nskb,实际上第一个skb应该是不需要传输的

        skb->next = nskb->next;
        nskb->next = NULL;

        /*
         * If device doesnt need nskb->dst, release it right now while
         * its hot in this cpu cache
         */
        if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
            skb_dst_drop(nskb);

        rc = ops->ndo_start_xmit(nskb, dev);//调用网卡驱动的发送函数
        if (unlikely(rc != NETDEV_TX_OK)) {
            if (rc & ~NETDEV_TX_MASK)
                goto out_kfree_gso_skb;
            nskb->next = skb->next;
            skb->next = nskb;
            return rc;
        }
        txq_trans_update(txq);
        if (unlikely(netif_tx_queue_stopped(txq) && skb->next))//如果队列stop且next非空则返回BUSY以等待下次传输
            return NETDEV_TX_BUSY;
    } while (skb->next);//直到next为空

out_kfree_gso_skb:
    if (likely(skb->next == NULL))
        skb->destructor = DEV_GSO_CB(skb)->destructor;
out_kfree_skb:
    kfree_skb(skb);
    return rc;
}
阅读(3371) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~