Linux 内核协议栈的 TSO、GSO

Linux 内核的 TSO 会在真正将skb发送到网卡前做GSO的检查:

  • 对于真实硬件设备,是在sch_direct_xmit中调用validate_xmit_skb_list检查
  • 对于虚拟设备,则是在__dev_queue_xmit中调用validate_xmit_skb检查
static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
{
	struct net_device *dev = skb->dev;
	struct netdev_queue *txq;
	struct Qdisc *q;
	int rc = -ENOMEM;

        ......
	txq = netdev_pick_tx(dev, skb, accel_priv);
	q = rcu_dereference_bh(txq->qdisc);

	......
        //该设备有对应的网卡队列,说明是真实网卡设备,在该队列上进行Qdisc调度,并发送skb
	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) {
                        .......
                        //对于虚拟设备,同样检查GSO分段
			skb = validate_xmit_skb(skb, dev);
			if (!skb)
				goto drop;

			HARD_TX_LOCK(dev, txq, cpu);

			if (!netif_xmit_stopped(txq)) {
				//实际发送到网卡设备
				skb = dev_hard_start_xmit(skb, dev, txq, &rc);
				__this_cpu_dec(xmit_recursion);
				if (dev_xmit_complete(rc)) {
					HARD_TX_UNLOCK(dev, txq);
					goto out;
				}
			}
			HARD_TX_UNLOCK(dev, txq);
                        ......
		} else {
			......
		}
	}

	......
	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, bool validate)
{
    int ret = NETDEV_TX_BUSY;
    
    ......
    /* Note that we validate skb (GSO, checksum, ...) outside of locks */
    if (validate)
        skb = validate_xmit_skb_list(skb, dev);  //检查硬件TSO分段特性,如果不存在则软件分段

    if (likely(skb)) {
        HARD_TX_LOCK(dev, txq, smp_processor_id());
        if (!netif_xmit_frozen_or_stopped(txq))
            skb = dev_hard_start_xmit(skb, dev, txq, &ret);   //发送到网卡进行最后发送

        HARD_TX_UNLOCK(dev, txq);
    } else {
        spin_lock(root_lock);
        return qdisc_qlen(q);
    }
    ......

    return ret;
}

dev_hard_start_xmit 里判断 netif_needs_gso 判断网卡是否支持gso,如果不支持则调用 dev_gso_segment 里面又调用 skb_gso_segment 把报文分片,对于ipv4而言,实际调用了 tcp_tso_segment,最后返回多个sk_buff 组成的链表,头指针存在 skb->next 里;如果网卡本身支持的话,直接把大块的skb交给网卡:调用netdev_ops->ndo_start_xmit 发送出去

可以看到,在判断netif_need_gso时,是要检查网卡的netdev->features值的,我们可以在include/linux/netdevice.h中看到这些值:

#define NETIF_F_SG      1   /* Scatter/gather IO. */
#define NETIF_F_IP_CSUM     2   /* Can checksum TCP/UDP over IPv4. */
#define NETIF_F_NO_CSUM     4   /* Does not require checksum. F.e. loopack. */
#define NETIF_F_HW_CSUM     8   /* Can checksum all the packets. */

#define NETIF_F_FRAGLIST    64  /* Scatter/gather IO. */

#define NETIF_F_GSO     2048    /* Enable software GSO. */

#define NETIF_F_GSO_SHIFT   16
#define NETIF_F_GSO_MASK    0x00ff0000
#define NETIF_F_TSO     (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
#define NETIF_F_UFO     (SKB_GSO_UDP << NETIF_F_GSO_SHIFT)

对于要支持TSO的网卡而言,需要有 NETIF_F_SG | NETIF_F_TSO | NETIF_F_IP_CSUM,相应如果要支持UFO,应该就需要 NETIF_F_SG | NETIF_F_UFO | NETIF_F_IP_CSUM

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值