linux 多队列 分析,LINUX2.35网卡驱动多队列的一些分析

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;

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值