作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
前文已经学习到了sch_direct_xmit函数,下面进入下一级函数。
- 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)) {
/* skb->next为null,即只发送一个skb */
-
if (!list_empty(&ptype_all))
-
dev_queue_xmit_nit(skb, dev); //ptype_all上的协议处理,如tcpdump
-
-
/*
-
* If device doesnt need skb->dst, release it right now while
-
* its hot in this cpu cache
-
*/
- /* device 不需要dst */
-
if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
-
skb_dst_drop(skb);
/* 使该skb不属于任何一个socket */
-
skb_orphan_try(skb);
-
-
if (netif_needs_gso(dev, skb)) {
- /* 需要scatter gather功能 */
/*
从注释上看,是进行perform protocol segmentation on skb。
但是实际工作,目前我不清楚
*/
-
if (unlikely(dev_gso_segment(skb)))
-
goto out_kfree_skb;
-
if (skb->next)
-
goto gso;
-
} else {
- //不需要scatter gather
/*
如需要连续内存,则将skb内存线性化,即连续内存
*/
-
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) {
- /* 计算checksum */
-
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;
-
}
-
}
/* 发送skb数据 */
-
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->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))
-
return NETDEV_TX_BUSY;
-
} while (skb->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;
- }
继续下一个函数,ops->ndo_start_xmit。又是一个回调函数,这个回调函数由驱动所决定。以e1000_main.c为例,
- static const struct net_device_ops e1000_netdev_ops = {
-
.ndo_open = e1000_open,
-
.ndo_stop = e1000_close,
-
.ndo_start_xmit = e1000_xmit_frame,
-
.ndo_get_stats = e1000_get_stats,
-
.ndo_set_rx_mode = e1000_set_rx_mode,
-
.ndo_set_mac_address = e1000_set_mac,
-
.ndo_tx_timeout = e1000_tx_timeout,
-
.ndo_change_mtu = e1000_change_mtu,
-
.ndo_do_ioctl = e1000_ioctl,
-
.ndo_validate_addr = eth_validate_addr,
-
-
.ndo_vlan_rx_register = e1000_vlan_rx_register,
-
.ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
-
.ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
-
#ifdef CONFIG_NET_POLL_CONTROLLER
-
.ndo_poll_controller = e1000_netpoll,
-
#endif
- };
从这里可以看出,对于e1000_main来说,该回调函数为e1000_xmit_frame。这个涉及到驱动层,一般都与硬件相关,在此就不继续探讨了。
那么现在,一个基本的IP包发送流程,基本上浏览完毕。小小的总结一下,当linux发送IP数据包时,数据包有可能是立刻发送出去,也有可能由下一次的软中断发送。