网络数据包发送之dev_hard_start_xmit
2014-11-24 10:21
1071 查看
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; unsigned int skb_len;
/* 检查上层协议是否已经完成了数据包分片的工作? */ if (likely(!skb->next)) { netdev_features_t features; /* * If device doesn't 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); features = netif_skb_features(skb);
/* 上层协议要求驱动进行VLAN插入加速,但是当前网络设备不支持该功能时,则需要手动完成数据包的VLAN字段插入 */ if (vlan_tx_tag_present(skb) && !vlan_hw_offload_capable(features, skb->vlan_proto)) { skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb)); if (unlikely(!skb)) goto out; /* 由于驱动根据该成员决定是否需要硬件插入VLAN,所以这里需要清除vlan_tci标志 */
skb->vlan_tci = 0; } /* If encapsulation offload request, verify we are testing * hardware encapsulation features instead of standard * features for the netdev */ if (skb->encapsulation) features &= dev->hw_enc_features;
/* 如果上层协议需要底层驱动执行数据包硬件分片操作,但是底层硬件不支持该功能时,则需要手动完成分片操作 */ if (netif_needs_gso(skb, features)) { /* ok,这里开始手动分片操作 */
if (unlikely(dev_gso_segment(skb, features))) goto out_kfree_skb; if (skb->next) goto gso; } else { /* 如果硬件不支持分离集合DMA操作而SKB带有非线性碎片数据的话,则需要对数据进行线性化拼接操作 */
if (skb_needs_linearize(skb, features) && __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) { if (skb->encapsulation) skb_set_inner_transport_header(skb, skb_checksum_start_offset(skb)); else skb_set_transport_header(skb, skb_checksum_start_offset(skb)); if (!(features & NETIF_F_ALL_CSUM) && skb_checksum_help(skb)) goto out_kfree_skb; } }
/* 这里把将要发给驱动的数据包提供给上层嗅探程序进行分析监控 */ if (!list_empty(&ptype_all)) dev_queue_xmit_nit(skb, dev); skb_len = skb->len; trace_net_dev_start_xmit(skb, dev); /* 到这里终于把数据包提交给底层驱动程序了,漫长的网络子系统漫游结束了,是该离开的时候了 */
rc = ops->ndo_start_xmit(skb, dev); trace_net_dev_xmit(skb, rc, dev, skb_len); /* 发送成功的话,需要更新该传输队列的统计计数 */
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 (!list_empty(&ptype_all)) dev_queue_xmit_nit(nskb, dev); skb_len = nskb->len; trace_net_dev_start_xmit(nskb, dev); rc = ops->ndo_start_xmit(nskb, dev); trace_net_dev_xmit(nskb, rc, dev, skb_len); 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_xmit_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; consume_skb(skb); return rc; } out_kfree_skb: kfree_skb(skb); out: return rc; }
相关文章推荐
- Linux网络之设备接口层:发送数据包流程dev_queue_xmit(二)
- Linux网络之设备接口层:发送数据包流程dev_queue_xmit
- Linux内核构造数据包并发送(二)(dev_queue_xmit方式)
- Linux内核构造数据包并发送(二)(dev_queue_xmit方式)
- Linux网络之设备接口层:发送数据包流程dev_queue_xmit
- 网络数据包发送接收全过程
- 网络数据包捕获与发送的多重实现(转载)
- 网络数据包发送接收全过程
- ps1脚本发送magic数据包,唤醒网络中电脑
- 网络数据包发送和接受原理
- 网卡驱动:数据包发送的流控机制-netif_start_queue/netif_wake_queue/netif_stop_queue
- linux下网络监听与发送数据包的方法(即libpcap、libnet两种类库的使用方法)
- 网络数据包发送接收全过程简介
- 网络数据包捕获与发送的多重实现
- Linux 内核网络协议栈 ------ tcp重传数据包 tcp_xmit_retransmit_skb
- 网络编程——设计一个发送TCP数据包的程序
- C# 自己写的一个类,用来将结构体或类中的数据打成"数据包",进行网络发送(转载)
- 使用dev_queue_xmit发送定制报文
- 读取linux下的网络设备的mac地址与发送原始数据包 (2011-11-23 20:11)
- 网卡驱动:数据包发送的流控机制-netif_start_queue/netif_wake_queue/netif_stop_queue