当前位置:   article > 正文

【C语言】linux内核ipoib模块 - ipoib_send

【C语言】linux内核ipoib模块 - ipoib_send

一、中文注释

  1. int ipoib_send(struct net_device *dev, struct sk_buff *skb,
  2. struct ib_ah *address, u32 dqpn)
  3. {
  4. struct ipoib_dev_priv *priv = ipoib_priv(dev); // 获取IPoIB设备的私有数据
  5. struct ipoib_tx_buf *tx_req; // 发送请求结构体
  6. int hlen, rc; // 分别为头部长度和返回码
  7. void *phead; // 要发送数据包的头部指针
  8. unsigned int usable_sge = priv->max_send_sge - !!skb_headlen(skb); // 计算可用的Scatter/Gather Element的数量
  9. // 检查是否为GSO分段传输的skb
  10. if (skb_is_gso(skb)) {
  11. hlen = skb_transport_offset(skb) + tcp_hdrlen(skb); // 计算GSO的头部长度
  12. phead = skb->data; // 获取skb数据的头部指针
  13. // 如果skb_pull返回错误,则skb数据区太小
  14. if (unlikely(!skb_pull(skb, hlen))) {
  15. ipoib_warn(priv, "linear data too small\n");
  16. ++dev->stats.tx_dropped; // 统计丢弃的包
  17. ++dev->stats.tx_errors; // 统计错误数
  18. dev_kfree_skb_any(skb); // 释放skb
  19. return -1;
  20. }
  21. // 非GSO传输的处理
  22. } else {
  23. // 如果skb的长度过长,则丢弃
  24. if (unlikely(skb->len > priv->mcast_mtu + IPOIB_ENCAP_LEN)) {
  25. ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",
  26. skb->len, priv->mcast_mtu + IPOIB_ENCAP_LEN);
  27. ++dev->stats.tx_dropped; // 统计丢弃的包
  28. ++dev->stats.tx_errors; // 统计错误数
  29. ipoib_cm_skb_too_long(dev, skb, priv->mcast_mtu); // 处理过长的skb
  30. return -1;
  31. }
  32. phead = NULL; // 无需处理头部
  33. hlen = 0; // 头部长度为0
  34. }
  35. // 检查是否存在太多的skb碎片
  36. if (skb_shinfo(skb)->nr_frags > usable_sge) {
  37. // 尝试线性化skb,如果有错误则丢弃
  38. if (skb_linearize(skb) < 0) {
  39. ipoib_warn(priv, "skb could not be linearized\n");
  40. ++dev->stats.tx_dropped; // 统计丢弃的包
  41. ++dev->stats.tx_errors; // 统计错误数
  42. dev_kfree_skb_any(skb); // 释放skb
  43. return -1;
  44. }
  45. // 即使线性化后,碎片数仍然过多则丢弃
  46. if (skb_shinfo(skb)->nr_frags > usable_sge) {
  47. ipoib_warn(priv, "too many frags after skb linearize\n");
  48. ++dev->stats.tx_dropped; // 统计丢弃的包
  49. ++dev->stats.tx_errors; // 统计错误数
  50. dev_kfree_skb_any(skb); // 释放skb
  51. return -1;
  52. }
  53. }
  54. ipoib_dbg_data(priv,
  55. "sending packet, length=%d address=%p dqpn=0x%06x\n", // 调试信息:将要发送的数据包信息
  56. skb->len, address, dqpn);
  57. // 使用tx_ring队列缓存skb,在确保所有记录和状态一致后调用post_send()
  58. tx_req = &priv->tx_ring[priv->tx_head & (priv->sendq_size - 1)];
  59. tx_req->skb = skb;
  60. // 如果skb长度小于内联阈值并且没有分段,则进行内联发送
  61. if (skb->len < ipoib_inline_thold &&
  62. !skb_shinfo(skb)->nr_frags) {
  63. tx_req->is_inline = 1;
  64. priv->tx_wr.wr.send_flags |= IB_SEND_INLINE;
  65. } else {
  66. // 处理DMA映射
  67. if (unlikely(ipoib_dma_map_tx(priv->ca, tx_req))) {
  68. ++dev->stats.tx_errors; // 统计错误数
  69. dev_kfree_skb_any(skb); // 释放skb
  70. return -1;
  71. }
  72. tx_req->is_inline = 0;
  73. priv->tx_wr.wr.send_flags &= ~IB_SEND_INLINE;
  74. }
  75. // 设置校验和
  76. if (skb->ip_summed == CHECKSUM_PARTIAL)
  77. priv->tx_wr.wr.send_flags |= IB_SEND_IP_CSUM;
  78. else
  79. priv->tx_wr.wr.send_flags &= ~IB_SEND_IP_CSUM;
  80. // 如果发送队列满了,则暂停网络队列
  81. if (atomic_read(&priv->tx_outstanding) == priv->sendq_size - 1) {
  82. ipoib_dbg(priv, "TX ring full, stopping kernel net queue\n");
  83. netif_stop_queue(dev); // 停止内核网络队列
  84. }
  85. skb_orphan(skb); // 取消与所有者的关联
  86. skb_dst_drop(skb); // 丢弃路由缓存项
  87. // 如果网络队列停止,则请求在发送完CQ后通知,以便我们可以唤醒它
  88. if (netif_queue_stopped(dev))
  89. if (ib_req_notify_cq(priv->send_cq, IB_CQ_NEXT_COMP |
  90. IB_CQ_REPORT_MISSED_EVENTS) < 0)
  91. ipoib_warn(priv, "request notify on send CQ failed\n");
  92. rc = post_send(priv, priv->tx_head & (priv->sendq_size - 1), // 调用post_send函数发送数据包
  93. address, dqpn, tx_req, phead, hlen);
  94. if (unlikely(rc)) {
  95. ipoib_warn(priv, "post_send failed, error %d\n", rc); // 如果post_send出现错误,记录错误信息
  96. ++dev->stats.tx_errors; // 统计错误数
  97. if (!tx_req->is_inline)
  98. ipoib_dma_unmap_tx(priv, tx_req); // 如果不是内联则释放DMA映射
  99. dev_kfree_skb_any(skb); // 释放skb
  100. if (netif_queue_stopped(dev)) // 如果网络队列停止,则唤醒
  101. netif_wake_queue(dev);
  102. rc = 0; // 将返回码设置为0
  103. } else {
  104. netif_trans_update(dev); // 更新最后一个传输的时间戳
  105. rc = priv->tx_head; // 设置返回码为当前的tx_head
  106. ++priv->tx_head; // tx_head自增,为下一次发送做准备
  107. atomic_inc(&priv->tx_outstanding); // 增加未决的发送操作计数
  108. }
  109. return rc; // 返回操作结果,成功发送则返回tx_head,失败返回0
  110. }

这个函数是一个Linux内核的网络设备发送函数,用于IP over InfiniBand (IPoIB) 模块。它负责准备并发送用户数据包(skb),并操作网络设备统计信息和发送队列。当不能正确发送或者处理数据包时,该函数将适当地释放资源,并通过相应的返回码和内部统计来标识错误的发生。

二、中文讲解

这段代码是一个InfiniBand网络设备的发送函数,用于在IP over InfiniBand(IPoIB)网络中发送数据。以下是对该函数功能的中文讲解:

int ipoib_send(struct net_device *dev, struct sk_buff *skb, struct ib_ah *address, u32 dqpn)

函数`ipoib_send`有四个参数:
- dev表示要发送数据的网络设备。
- skb是一个指向socket缓冲区(数据包)的指针。
- address是远程端点的InfiniBand地址句柄。
- dqpn是目的端口号。
函数内部的变量说明:
- priv是设备的私有数据结构。
- tx_req表示用于发送的缓冲区。
- hlen是头部的长度,表示在何处开始线性化处理(如果是GSO(大尺寸传输优化)数据包)。
- rc是函数返回码。
- phead是指向数据头部的指针。
- usable_sge是扣除头部数据后剩余可用的分散/聚集元素(Scatter/Gather Elements)数量。
代码流程梳理:
1. 首先判断是不是GSO数据包,如果是,则记录下头部信息并处理skb缓冲区。如果skb太小无法处理,则丢弃并返回。
2. 如果不是GSO数据包,但skb长度超出最大传输单元(MTU),也会丢弃数据包并返回。
3. 检查数据包中的片段(frags)数量,如果超过可用的SGE数量,则尝试线性化skb。线性化失败或线性化后frags仍然太多,则丢弃数据包。
4. 设置传输请求结构`tx_req`并决定是否以内联方式发送数据。如果数据长度小于阈值且无frags,则设为内联。
5. 如果传输队列(TX ring)快满了,停止继续入队新的数据包。
6. 为数据包的发送作最后准备,包括释放相关资源,比如删除路由相关信息,并对发送队列和完成队列进行相关的操作。
7. 调用`post_send`来实际发送数据包。
8. 根据发送结果进行处理。如果失败,释放资源,并根据队列状态恢复传输队列或者记录错误。
这段代码的关键点在于管理数据包缓冲区,并且确保数据可以被有效的发送到InfiniBand网络。涉及到内存管理,异步通信,以及在发送之前后处理网络设备状态。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/154391
推荐阅读
相关标签
  

闽ICP备14008679号