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