免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5852 | 回复: 19

[网络子系统] 关于sk_buff及其paged data的问题【已解决】 [复制链接]

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2014-03-12 13:48 |显示全部楼层
本帖最后由 jiufei19 于 2014-03-24 20:30 编辑

之前阅读了很久的linux网络内核源码,一直没有对sk_buff的scattered/gather I/O有过仔细研究,现在翻看的时候,觉得有必要搞清楚一个之前一直没有彻底弄明白的问题,希望大家解惑。

在IP报文分段过程中,提到了一个scattered/gather I/O的概念,即指内存页中存储的L4层的数据可以不需要人为地通过内存拷贝变为线性地址,直接放在那里即可,最后由网卡驱动去完成最终的拷贝。我现在弄不明白的是为啥在有paged data的情况下,sk_buff的data指针还会指向部分L4层的数据呢,换句话讲,为啥可能有所谓的main buffer(skb->data指向的L4层数据)和paged data同时并存的现象,我的理解是要么全部都是paged的数据,要么都是由skb->data指向的数据,怎么会两部分都存在呢。如下图所示:

图片1.png

问题:图中标记为x的数据和下方page中的数据为啥同时存在

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-03-12 14:47 |显示全部楼层
个人理解:
skb->data中存放的是数据的第一部分,其他部分都放到skb_shared_info->frags中了,所以实际的数据是“x+page中的数据”
tcp_sendmsg中,默认将用户态数据添加到发送队列最后一个skb中。如果skb剩余data空间不够,且支持SG,则会将此data填充完,再申请一个page作为frags,继续添加。

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2014-03-12 15:18 |显示全部楼层
本帖最后由 jiufei19 于 2014-03-12 15:19 编辑
humjb_1983 发表于 2014-03-12 14:47
个人理解:
skb->data中存放的是数据的第一部分,其他部分都放到skb_shared_info->frags中了,所以实际的数 ...


感谢答复,我之前阅读源码是就是这样理解的,即认为skb->data中存放的是带发送数据的第1部分,其他都放在frags中,但是现在我想知道的是为啥这样做,难道必须把在frags中的数据弄一部分出来放在这个所谓的main bufffer中吗?因为对内存机制不太了解,所以想不清楚这样做的理论基础是什么

论坛徽章:
16
CU十二周年纪念徽章
日期:2013-10-24 15:41:3415-16赛季CBA联赛之广东
日期:2015-12-23 21:21:55青铜圣斗士
日期:2015-12-05 10:35:30黄金圣斗士
日期:2015-11-26 20:42:16神斗士
日期:2015-11-19 12:47:50每日论坛发贴之星
日期:2015-11-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-18 06:20:002015亚冠之城南
日期:2015-11-10 19:10:492015亚冠之萨济拖拉机
日期:2015-10-28 18:47:282015亚冠之柏太阳神
日期:2015-08-30 17:21:492015亚冠之山东鲁能
日期:2015-07-07 18:48:39摩羯座
日期:2014-08-29 23:01:42
发表于 2014-03-12 16:59 |显示全部楼层
应该是防止内存在被碎片吧。直接获取page。应为一般的skb data已经能满足了。个人理解

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2014-03-12 20:42 |显示全部楼层
回复 4# tc1989tc


    我的疑问是这样的,比如某个网卡支持SG模式,那么应用层发出udp数据时,就让这些数据直接在page中就好了,为何还要有一部分数据在skb->data那里呢,反过来讲,难道必须有一部分数据位于skb->data那里吗?

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2014-03-12 21:22 |显示全部楼层
我刚才可能描述问题没有描述准确,我的真正疑问是这样的:

其实,待发送的udp数据,首个分段一定要携带header(例如报文IP和UDP的首部),这部分数据一定在线性区中,我的问题是除此外,是否可以把其他真正的L4层数据都放在page中?如果可以的话,那么本问题中所附的那张图中的数据x为啥要拷贝到main buffer中?

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2014-03-12 21:22 |显示全部楼层
本帖最后由 jiufei19 于 2014-03-12 21:23 编辑

请各位大侠解惑!

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-03-13 16:39 |显示全部楼层
本帖最后由 humjb_1983 于 2014-03-13 16:44 编辑

个人理解,理论上这个不一定非要这么做,只是一种通用做法而已。
理论上,支持SG的硬件设备,其实就是DMA支持多个离散区域的同时传输而已,而这些离散区域具体的分布,具体放在skb->data中,还是放在skb_shared_info->frags中,硬件并不关心,硬件需要知道的应该只是这些离散区域的起始地址(总线地址)和区域的len即可,具体通常是在网卡驱动中设置的,如果同时使用了skb->data和skb_shared_info->frags,那么驱动中会将skb->data第地址/len和skb_shared_info->frags中地址/len都告知网卡硬件,那么网卡硬件做DMA时,就知道从哪儿取数据了。
如下是igb驱动中的示例,供参考:

  1. static void igb_tx_map(struct igb_ring *tx_ring, struct sk_buff *skb,
  2.                        struct igb_tx_buffer *first, u32 tx_flags,
  3.                        const u8 hdr_len)
  4. {
  5. ...
  6.         dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
  7.         if (dma_mapping_error(tx_ring->dev, dma))
  8.                 goto dma_error;

  9.         /* record length, and DMA address */
  10.         first->length = size;
  11.         first->dma = dma;
  12.         first->shtx = skb_shinfo(skb)->tx_flags;
  13.         tx_desc->read.buffer_addr = cpu_to_le64(dma);
  14.         tx_buffer_info = &tx_ring->tx_buffer_info[i];

  15.         for (;;) {
  16.                 while (unlikely(size > IGB_MAX_DATA_PER_TXD)) {
  17.                         tx_desc->read.cmd_type_len =
  18.                                 cmd_type | cpu_to_le32(IGB_MAX_DATA_PER_TXD);

  19.                         i++;
  20.                         tx_desc++;
  21.                         if (i == tx_ring->count) {
  22.                                 tx_desc = IGB_TX_DESC(tx_ring, 0);
  23.                                 i = 0;
  24.                         }

  25.                         dma += IGB_MAX_DATA_PER_TXD;
  26.                         size -= IGB_MAX_DATA_PER_TXD;

  27.                         tx_desc->read.olinfo_status = 0;
  28.                         tx_desc->read.buffer_addr = cpu_to_le64(dma);
  29.                 }

  30.                 if (likely(!data_len))
  31.                         break;

  32.                 tx_desc->read.cmd_type_len = cmd_type | cpu_to_le32(size);

  33.                 i++;
  34.                 tx_desc++;
  35.                 if (i == tx_ring->count) {
  36.                         tx_desc = IGB_TX_DESC(tx_ring, 0);
  37.                         i = 0;
  38.                 }

  39.                 size = frag->size;
  40.                 data_len -= size;

  41.                 tx_buffer_info->mapped_as_page = true;
  42.                 dma = dma_map_page(tx_ring->dev,
  43.                                 frag->page,
  44.                                 frag->page_offset,
  45.                                 size,
  46.                                 DMA_TO_DEVICE);
  47.                 if (dma_mapping_error(tx_ring->dev, dma))
  48.                         goto dma_error;

  49.                 tx_buffer_info = &tx_ring->tx_buffer_info[i];
  50.                 tx_buffer_info->length = size
  51.                 tx_buffer_info->dma = dma;
  52.                 tx_desc->read.olinfo_status = 0;
  53.                 tx_desc->read.buffer_addr = cpu_to_le64(dma);

  54.                 frag++;
  55.         }
  56. ...
复制代码

论坛徽章:
1
IT运维版块每日发帖之星
日期:2015-11-17 06:20:00
发表于 2014-03-13 21:02 |显示全部楼层
回复 8# humjb_1983


    感谢humjb_1983的答复,虽然还是没有让我完全理解。主要是我对操作系统的内存管理机制没有理解,所以很难理解这里我提出的这个问题,我困惑的是该问题中那个拷贝到main buffer中的数据x,其长度如何界定,即到底会拷贝多少到main buffer,还是根本就不应该拷贝进去,main buffer中只存储协议的首部。

论坛徽章:
15
射手座
日期:2014-02-26 13:45:082015年迎新春徽章
日期:2015-03-04 09:54:452015年辞旧岁徽章
日期:2015-03-03 16:54:15羊年新春福章
日期:2015-02-26 08:47:552015年亚洲杯之卡塔尔
日期:2015-02-03 08:33:45射手座
日期:2014-12-31 08:36:51水瓶座
日期:2014-06-04 08:33:52天蝎座
日期:2014-05-14 14:30:41天秤座
日期:2014-04-21 08:37:08处女座
日期:2014-04-18 16:57:05戌狗
日期:2014-04-04 12:21:33技术图书徽章
日期:2014-03-25 09:00:29
发表于 2014-03-14 09:57 |显示全部楼层
jiufei19 发表于 2014-03-13 21:02
回复 8# humjb_1983

引用专家的注释代码,兄弟可以理解下,个人理解,tcp_sendmsg中填充数据时,会先填充skb->data的空间,填满后才会填充frags(可能需要分配新页)。
  1.                             /* Where to copy to? */
  2.                         if (skb_tailroom(skb) > 0) {/* skb线性存储区底部还有空间 */
  3.                                 /* We have some space in skb head. Superb! */
  4.                                 if (copy > skb_tailroom(skb))/* 本次只复制skb存储区底部剩余空间大小的数据量 */
  5.                                         copy = skb_tailroom(skb);
  6.                                 /* 从用户空间复制指定长度的数据到skb中,如果失败,则退出 */
  7.                                 if ((err = skb_add_data(skb, from, copy)) != 0)
  8.                                         goto do_fault;
  9.                         } else {/* 线性存储区底部已经没有空间了,复制到分散/聚集存储区中 */
  10.                                 int merge = 0;/* 是否在页中添加数据 */
  11.                                 int i = skb_shinfo(skb)->nr_frags;/* 分散/聚集片断数 */
  12.                                 struct page *page = TCP_PAGE(sk);/* 分片页页 */
  13.                                 int off = TCP_OFF(sk);/* 分片内的偏移 */

  14.                                 if (skb_can_coalesce(skb, i, page, off) &&
  15.                                     off != PAGE_SIZE) {/* 当前分片还能添加数据 */
  16.                                         /* We can extend the last page
  17.                                          * fragment. */
  18.                                         merge = 1;
  19.                                 } else if (i == MAX_SKB_FRAGS ||/* 目前skb中的页不能添加数据,这里判断是否能再分配页 */
  20.                                            (!i &&
  21.                                            !(sk->sk_route_caps & NETIF_F_SG))) {/* 网卡不支持S/G,不能分片 */
  22.                                         /* Need to add new fragment and cannot
  23.                                          * do this because interface is non-SG,
  24.                                          * or because all the page slots are
  25.                                          * busy. */
  26.                                         tcp_mark_push(tp, skb);/* SKB可以提交了 */
  27.                                         goto new_segment;/* 重新分配skb */
  28.                                 } else if (page) {/* 分页数量未达到上限,判断当前页是否还有空间 */
  29.                                         /* If page is cached, align
  30.                                          * offset to L1 cache boundary
  31.                                          */
  32.                                         off = (off + L1_CACHE_BYTES - 1) &
  33.                                               ~(L1_CACHE_BYTES - 1);
  34.                                         if (off == PAGE_SIZE) {/* 最后一个分页数据已经满,需要分配新页 */
  35.                                                 put_page(page);
  36.                                                 TCP_PAGE(sk) = page = NULL;
  37.                                         }
  38.                                 }

  39.                                 if (!page) {/* 需要分配新页 */
  40.                                         /* Allocate new cache page. */
  41.                                         if (!(page = sk_stream_alloc_page(sk)))/* 分配新页,如果内存不足则等待内存 */
  42.                                                 goto wait_for_memory;
  43.                                         off = 0;
  44.                                 }

  45.                                 if (copy > PAGE_SIZE - off)/* 待复制的数据不能大于页中剩余空间 */
  46.                                         copy = PAGE_SIZE - off;

  47.                                 /* Time to copy data. We are close to
  48.                                  * the end! */
  49.                                 err = skb_copy_to_page(sk, from, skb, page,
  50.                                                        off, copy);/* 从用户态复制数据到页中 */
  51.                                 if (err) {/* 复制失败了 */
  52.                                         /* If this page was new, give it to the
  53.                                          * socket so it does not get leaked.
  54.                                          */
  55.                                         if (!TCP_PAGE(sk)) {/* 如果是新分配的页,则将页记录到skb中,供今后使用 */
  56.                                                 TCP_PAGE(sk) = page;
  57.                                                 TCP_OFF(sk) = 0;
  58.                                         }
  59.                                         goto do_error;
  60.                                 }

  61.                                 /* Update the skb. */
  62.                                 /* 更新skb的分段信息 */
  63.                                 if (merge) {/* 在最后一个页中追加数据 */
  64.                                         skb_shinfo(skb)->frags[i - 1].size +=
  65.                                                                         copy;/* 更新最后一页的数据长度 */
  66.                                 } else {/* 新分配的页 */
  67.                                         /* 更新skb中分片信息 */
  68.                                         skb_fill_page_desc(skb, i, page, off, copy);
  69.                                         if (TCP_PAGE(sk)) {
  70.                                                 get_page(page);
  71.                                         } else if (off + copy < PAGE_SIZE) {
  72.                                                 get_page(page);
  73.                                                 TCP_PAGE(sk) = page;
  74.                                         }
  75.                                 }

  76.                                 /* 更新页内偏移 */
  77.                                 TCP_OFF(sk) = off + copy;
  78.                         }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP