免费注册 查看新帖 |

Chinaunix

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

[内核同步] 关于per-cpu的疑问。。。 [复制链接]

论坛徽章:
4
酉鸡
日期:2014-03-21 23:19:50狮子座
日期:2014-08-01 22:11:40酉鸡
日期:2015-01-10 21:31:442015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2014-03-22 16:05 |显示全部楼层
本帖最后由 chishanmingshen 于 2014-03-26 12:58 编辑

静态和动态的per-cpu变量存储在一段(貌似同一段?!)连续的虚拟空间?

如果上面猜测是对的,就需要在vmalloc空间预留一段连续空间啊,code在哪里?

希望结合pcpu_get_vm_areas这个函数 (3.12以上)指点下啊,谢谢!


3.12

  1. /**
  2. * pcpu_get_vm_areas - allocate vmalloc areas for percpu allocator
  3. * @offsets: array containing offset of each area
  4. * @sizes: array containing size of each area
  5. * @nr_vms: the number of areas to allocate
  6. * @align: alignment, all entries in @offsets and @sizes must be aligned to this
  7. *
  8. * Returns: kmalloc'd vm_struct pointer array pointing to allocated
  9. *            vm_structs on success, %NULL on failure
  10. *
  11. * Percpu allocator wants to use congruent vm areas so that it can
  12. * maintain the offsets among percpu areas.  This function allocates
  13. * congruent vmalloc areas for it with GFP_KERNEL.  These areas tend to
  14. * be scattered pretty far, distance between two areas easily going up
  15. * to gigabytes.  To avoid interacting with regular vmallocs, these
  16. * areas are allocated from top.
  17. *
  18. * Despite its complicated look, this allocator is rather simple.  It
  19. * does everything top-down and scans areas from the end looking for
  20. * matching slot.  While scanning, if any of the areas overlaps with
  21. * existing vmap_area, the base address is pulled down to fit the
  22. * area.  Scanning is repeated till all the areas fit and then all
  23. * necessary data structres are inserted and the result is returned.
  24. */
  25. struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
  26.                                      const size_t *sizes, int nr_vms,

  27. /**
  28. * pcpu_get_vm_areas - allocate vmalloc areas for percpu allocator
  29. * @offsets: array containing offset of each area
  30. * @sizes: array containing size of each area
  31. * @nr_vms: the number of areas to allocate
  32. * @align: alignment, all entries in @offsets and @sizes must be aligned to this
  33. *
  34. * Returns: kmalloc'd vm_struct pointer array pointing to allocated
  35. *            vm_structs on success, %NULL on failure
  36. *
  37. * Percpu allocator wants to use congruent vm areas so that it can
  38. * maintain the offsets among percpu areas.  This function allocates
  39. * congruent vmalloc areas for it with GFP_KERNEL.  These areas tend to
  40. * be scattered pretty far, distance between two areas easily going up
  41. * to gigabytes.  To avoid interacting with regular vmallocs, these
  42. * areas are allocated from top.
  43. *
  44. * Despite its complicated look, this allocator is rather simple.  It
  45. * does everything top-down and scans areas from the end looking for
  46. * matching slot.  While scanning, if any of the areas overlaps with
  47. * existing vmap_area, the base address is pulled down to fit the
  48. * area.  Scanning is repeated till all the areas fit and then all
  49. * necessary data structres are inserted and the result is returned.
  50. */
  51. struct vm_struct **pcpu_get_vm_areas(const unsigned long *offsets,
  52.                                      const size_t *sizes, int nr_vms,
  53.                                      size_t align)
  54. {
  55.         const unsigned long vmalloc_start = ALIGN(VMALLOC_START, align);
  56.         const unsigned long vmalloc_end = VMALLOC_END & ~(align - 1);
  57.         struct vmap_area **vas, *prev, *next;
  58.         struct vm_struct **vms;
  59.         int area, area2, last_area, term_area;
  60.         unsigned long base, start, end, last_end;
  61.         bool purged = false;

  62.         /* verify parameters and allocate data structures */
  63.         BUG_ON(align & ~PAGE_MASK || !is_power_of_2(align));
  64.         for (last_area = 0, area = 0; area < nr_vms; area++) {
  65.                 start = offsets[area];
  66.                 end = start + sizes[area];

  67.                 /* is everything aligned properly? */
  68.                 BUG_ON(!IS_ALIGNED(offsets[area], align));
  69.                 BUG_ON(!IS_ALIGNED(sizes[area], align));

  70.                 /* detect the area with the highest address */
  71.                 if (start > offsets[last_area])
  72.                         last_area = area;

  73.                 for (area2 = 0; area2 < nr_vms; area2++) {
  74.                         unsigned long start2 = offsets[area2];
  75.                         unsigned long end2 = start2 + sizes[area2];

  76.                         if (area2 == area)
  77.                                 continue;

  78.                         BUG_ON(start2 >= start && start2 < end);
  79.                         BUG_ON(end2 <= end && end2 > start);
  80.                 }
  81.         }
  82.         last_end = offsets[last_area] + sizes[last_area];

  83.         if (vmalloc_end - vmalloc_start < last_end) {
  84.                 WARN_ON(true);
  85.                 return NULL;
  86.         }

  87.         vms = kcalloc(nr_vms, sizeof(vms[0]), GFP_KERNEL);
  88.         vas = kcalloc(nr_vms, sizeof(vas[0]), GFP_KERNEL);
  89.         if (!vas || !vms)
  90.                 goto err_free2;

  91.         for (area = 0; area < nr_vms; area++) {
  92.                 vas[area] = kzalloc(sizeof(struct vmap_area), GFP_KERNEL);
  93.                 vms[area] = kzalloc(sizeof(struct vm_struct), GFP_KERNEL);
  94.                 if (!vas[area] || !vms[area])
  95.                         goto err_free;
  96.         }
  97. retry:
  98.         spin_lock(&vmap_area_lock);

  99.         /* start scanning - we scan from the top, begin with the last area */
  100.         area = term_area = last_area;
  101.         start = offsets[area];
  102.         end = start + sizes[area];

  103.         if (!pvm_find_next_prev(vmap_area_pcpu_hole, &next, &prev)) {
  104.                 base = vmalloc_end - last_end;
  105.                 goto found;
  106.         }
  107.         base = pvm_determine_end(&next, &prev, align) - end;

  108.         while (true) {
  109.                 BUG_ON(next && next->va_end <= base + end);
  110.                 BUG_ON(prev && prev->va_end > base + end);

  111.                 /*
  112.                  * base might have underflowed, add last_end before
  113.                  * comparing.
  114.                  */
  115.                 if (base + last_end < vmalloc_start + last_end) {
  116.                         spin_unlock(&vmap_area_lock);
  117.                         if (!purged) {
  118.                                 purge_vmap_area_lazy();
  119.                                 purged = true;
  120.                                 goto retry;
  121.                         }
  122.                         goto err_free;
  123.                 }

  124.                 /*
  125.                  * If next overlaps, move base downwards so that it's
  126.                  * right below next and then recheck.
  127.                  */
  128.                 if (next && next->va_start < base + end) {
  129.                         base = pvm_determine_end(&next, &prev, align) - end;
  130.                         term_area = area;
  131.                         continue;
  132.                 }

  133.                 /*
  134.                  * If prev overlaps, shift down next and prev and move
  135.                  * base so that it's right below new next and then
  136.                  * recheck.
  137.                  */
  138.                 if (prev && prev->va_end > base + start)  {
  139.                         next = prev;
  140.                         prev = node_to_va(rb_prev(&next->rb_node));
  141.                         base = pvm_determine_end(&next, &prev, align) - end;
  142.                         term_area = area;
  143.                         continue;
  144.                 }

  145.                 /*
  146.                  * This area fits, move on to the previous one.  If
  147.                  * the previous one is the terminal one, we're done.
  148.                  */
  149.                 area = (area + nr_vms - 1) % nr_vms;
  150.                 if (area == term_area)
  151.                         break;
  152.                 start = offsets[area];
  153.                 end = start + sizes[area];
  154.                 pvm_find_next_prev(base + end, &next, &prev);
  155.         }
  156. found:
  157.         /* we've found a fitting base, insert all va's */
  158.         for (area = 0; area < nr_vms; area++) {
  159.                 struct vmap_area *va = vas[area];

  160.                 va->va_start = base + offsets[area];
  161.                 va->va_end = va->va_start + sizes[area];
  162.                 __insert_vmap_area(va);
  163.         }

  164.         vmap_area_pcpu_hole = base + offsets[last_area];

  165.         spin_unlock(&vmap_area_lock);

  166.         /* insert all vm's */
  167.         for (area = 0; area < nr_vms; area++)
  168.                 setup_vmalloc_vm(vms[area], vas[area], VM_ALLOC,
  169.                                  pcpu_get_vm_areas);

  170.         kfree(vas);
  171.         return vms;

  172. err_free:
  173.         for (area = 0; area < nr_vms; area++) {
  174.                 kfree(vas[area]);
  175.                 kfree(vms[area]);
  176.         }
  177. err_free2:
  178.         kfree(vas);
  179.         kfree(vms);
  180.         return NULL;
  181. }
复制代码

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:58:11
发表于 2014-03-22 18:46 |显示全部楼层
本帖最后由 arm-linux-gcc 于 2014-03-23 18:15 编辑

动态的是一段连续的空间
alloc_percpu的底层实现是用alloc_page
同一个per-CPU变量的各个副本是不连续的

静态的,在编译时只有一份,即在vmlinux中就只有一份。
在内核启动时,会做拷贝,但是对于同一个静态per-CPU的各个副本是不连续的
内核初始化过程中,会将__per_cpu_start ~ __per_cpu_end(所有静态定义的per-CPU变量的编译阶段生产的副本都在这个区域)拷贝到另外一处动态分配到的地方(使用的是bootmem分配器得到的),pcpu_unit_offsets[]中会记录副本之间的地址间隔。
内核最后会释放__per_cpu_start ~ __per_cpu_end这些内存,因为他们是位于__init_begin ~ __init_end的



论坛徽章:
4
酉鸡
日期:2014-03-21 23:19:50狮子座
日期:2014-08-01 22:11:40酉鸡
日期:2015-01-10 21:31:442015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2014-03-22 19:01 |显示全部楼层
回复 2# arm-linux-gcc

谢谢。
静态比较容易理解,我不理解的是动态的。。

论坛徽章:
0
发表于 2014-03-22 22:38 |显示全部楼层
動態的話 2F就回答很清楚啦

每個cpu各自擁有phy mem一個連續的空間

至於怎麼要得到一個連續空間就是利用kmalloc

论坛徽章:
1
拜羊年徽章
日期:2015-03-03 16:15:43
发表于 2014-03-23 00:48 来自手机 |显示全部楼层
应该是动态好理解,静态不好理解。静态变量应该只是为每个核规定一些配额,告诉每个核该为多少变量预留空间。初始化时,每个核会根据配额动态分配per-cpu变量内存空间。分配完后,静态变量就被回收了。

论坛徽章:
4
酉鸡
日期:2014-03-21 23:19:50狮子座
日期:2014-08-01 22:11:40酉鸡
日期:2015-01-10 21:31:442015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2014-03-23 13:08 |显示全部楼层
本帖最后由 chishanmingshen 于 2014-03-23 13:12 编辑

希望结合pcpu_get_vm_areas这个函数 分析。。。

论坛徽章:
4
酉鸡
日期:2014-03-21 23:19:50狮子座
日期:2014-08-01 22:11:40酉鸡
日期:2015-01-10 21:31:442015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2014-03-23 13:11 |显示全部楼层
回复 4# wth0722
不是kmalloc!kmalloc的仅仅是管理相关结构体

   

论坛徽章:
0
发表于 2014-03-23 22:52 |显示全部楼层
回复 7# chishanmingshen

那能請問你覺的是用那個方式要記憶體?


   

论坛徽章:
4
酉鸡
日期:2014-03-21 23:19:50狮子座
日期:2014-08-01 22:11:40酉鸡
日期:2015-01-10 21:31:442015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2014-03-24 08:04 |显示全部楼层
回复 8# wth0722

1楼中我列出了代码了,只是没看明白

   

论坛徽章:
9
辰龙
日期:2014-08-18 20:38:42未羊
日期:2014-09-04 08:50:45丑牛
日期:2014-09-06 00:12:55寅虎
日期:2014-12-22 20:50:56摩羯座
日期:2015-01-14 22:28:15巳蛇
日期:2015-01-23 20:39:272015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之青岛
日期:2016-03-13 23:37:1915-16赛季CBA联赛之深圳
日期:2016-03-29 18:52:38
发表于 2014-03-24 22:48 |显示全部楼层
回复 1# chishanmingshen

你从哪看出静态和动态PERCPU变量是同一段内存这个结论的?
我看了一个2.6.34.10内核 X86体系的代码。

静态PERCPU是在setup_per_cpu_areas->pcpu_embed_first_chunk 函数里分配的。
面这个函数里面采用alloc_bootmem_nopanic进行内存的分配。这时候正式的内存管理系统还不有起来。

而动态的PERCPU是在函数alloc_pcpu_chunk中vmalloc出来的。

1. 两处都是动态分配的,都不是静态预留的。
2. 一个是bootmem分配器分出来的,属低端内存。一个是vmalloc出来的,属高端内存。


另外,对于你贴出来的函数,别人注释也写得很清楚:
* Returns: kmalloc'd vm_struct pointer array pointing to allocated
*            vm_structs on success, %NULL on failure
*
* Percpu allocator wants to use congruent vm areas so that it can
* maintain the offsets among percpu areas.  This function allocates
* congruent vmalloc areas for it.  These areas tend to be scattered
* pretty far, distance between two areas easily going up to
* gigabytes.  To avoid interacting with regular vmallocs, these areas
* are allocated from top.


晚上看到你这个问题才翻的代码,以前也就直接用alloc_percpu,没有看实现。有什么理解错的,请多多包涵。


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP