teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ yn...

65
1 Buddy和CMA简介 以及在Android中实际使用 CMA遇到问题的改进 2014/10/19 朱辉 [email protected] CMA UNMOVABLE RECLAIMABLE MOVABLE RESERVE

Upload: others

Post on 12-Jul-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

1

Buddy和CMA简介以及在Android中实际使用

CMA遇到问题的改进

2014/10/19

朱辉 [email protected]

CMA

UNMOVABLE RECLAIMABLE

MOVABLE RESERVE

Page 2: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

2

目录

• Buddy系统结构简介

• CMA结构简介

• CMA的初始化

• CMA在普通内存结构中的分配和释放

• CMA对连续内存的分配

• CMA对连续内存的释放

• CMA在实际使用中遇到的问题和解决思路

Page 3: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

3

Buddy系统结构简介及内存分配和释放过程

• 介绍CMA从内存结构讲起因为其中有着千丝万缕的联系。

• 尤其是CMA本身就是Buddy系统的一部分。

Page 4: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

4

均匀访存模型 UMA

• 从内存节点将起,讲内存节点不能不讲这两种访存模型。

• 每个CPU通过北桥中的内存控制器访问内存。

• 北桥易成为访问内存的瓶颈。

• 每个CPU访问内存速度一样。

• Linux内核把全部内存当成一个节点NODE。

Page 5: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

5

均匀访存模型 UMA

Page 6: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

6

非均匀访存模型 NUMA

• 前一种模型的改进。

• 每个CPU自带内存控制器访问一部分内存。

• 内存相对每个CPU有本地内存和远端内存,访问速度不同。

• Linux内核将内存分成了若干节点NODE。

• NUMA的cache同步效率问题最近被扯出来狂黑。

• 内存分配的第一步基本就是对NODE的选择,系统一般会选择本地NODE。

Page 7: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

7

非均匀访存模型 NUMA

Page 8: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

8

节点(NODE)中的区域(ZONE)

• 因为不同地址的内存情况不同,所以节点中的内存被分成了不同的区域。

• 不同的系统,ZONE的类型也不同,基本有以下几种:ZONE_DMA:可用作DMA的内存区域。ZONE_NORMAL:直接被内核直接映射到自己的虚拟地址空间的地址。ZONE_HIGHMEM:不能被直接映射到内核的虚拟地址空间的地址。

• 基本上就是越接近0的内存越珍贵,所以在内存分配时是先选择起始ZONE,然后从高到低试图从其中分配内存。

Page 9: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

9

节点(NODE)中的区域(ZONE)

Page 10: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

10

早期区域(ZONE)中的结构(Buddy)• Buddy是为了减少内存碎片而设计。

• ZONE中大小为MAX_ORDER的数组,数组中每一个元素是一个列表,其依次索引了2的0次方到2的(MAX_ORDER-1)次方大小的页,这里索引的就是空闲页。

• 关于PCP,当释放order为0的页的时候,会先分配若干页order为0的页到PCP中,PCP中页超过一定量才会真的释放,。

分配order为0的页也是先从PCP从分配。

• 分配时,如果order是0,先试图从PCP中分配页,如果不是,根据分配指定的order到相应的数组order项目中的列表去取页大小为order的页返回。

如果没有就到order+1列表中取出1页并分为2页,这样每页就是order大小,1页返回,另1页加入order列表。

如果order+1列表中也没有页,则到order+2列表中去拆页。

后面依此类推。

• 释放页时,在order列表中找buddy,如果没找到加回order列表。

如果找到,则合并为一个order+1页大小的页并加回order+1列表。

后面依此类推。

• Buddy的问题是所有减少碎片的操作 ,都在释放才做,如果一个页持续不释放,一个页块中其他页都会被影响。所以在2.6.24开始对这个设计进行了扩展。

Page 11: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

11

早期区域(ZONE)中的结构(Buddy)

Page 12: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

12

现在区域(ZONE)中的结构(Buddy+Migrate)• 将ZONE中大小为MAX_ORDER的数组中的每一个列表替换为一个MIGRATE_TYPES大小的数组,而数

组中每一个元素是一个列表,其依次索引了2的0次方到2的(MAX_ORDER-1)次方大小的页。

因为实际使用中,每个Migrate块内部更加紧密,所以把每个Migrate块画在了一起,和实际实现不同。

• 下面是几种迁移块的介绍:

MIGRATE_UNMOVABLE,在内存当中有固定的位置,不能移动。内核自己多会分配这种类型的数据。

MIGRATE_RECLAIMABLE,不能直接移动,但可以删除,其内容页可以从其他地方重新生成。例如,映射自文件的数据属于这种类型,针对这种页,内核有专门的页面回收处理。

MIGRATE_MOVABLE,可以随意移动,用户空间应用程序所用到的页属于该类别。它们通过页表来映射,如果他们复制到新的位置,页表项也会相应的更新,应用程序不会注意到任何改变。

MIGRATE_RESERVE,保留页。

MIGRATE_ISOLATE,用来帮助做页的迁移,这里索引的页不能分配。

• 在物理页层次上,每个2的MAX_ORDER-1次方大小的页,也就是Buddy最大的一层的页,被规定为一个页块,页块上会记录其的迁移类型。

Page 13: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

13

现在区域(ZONE)中的结构(Buddy+Migrate)

• 在请求页时,内核已经选择了要从哪个migrate中分配。具体相关函数allocflags_to_migratetype。

• 分配时首先在相关migrate块中进行,过程和早期方法一样,层层找页。相关函数__rmqueue_smallest。

• 如果在当前migrate块中找不到页,就按照后备规则(其中MIGRATE_RESERVE并不是一项,而是标明这条规则的结束)进行横向并从下向上的找页。

也就是从MAX_ORDER-1开始,把规则中每个migrate块过一下,如果没有再进入到MAX_ORDER-2进行每个migrate块的找页。

在找到合适的页的时候,还会根据条件做页的迁徙,把分配出的页的整个页块迁移到当前要分配的migrate块上(页块上的记录也会被改变)。相关函数try_to_steal_freepages。这里的用词有点个诡异,为什么不直接用migrate?

相关函数__rmqueue_fallback。

• 如果还没找到,就到MIGRATE_RESERVE以从上到下的普通方式找页。如果还没有,就失败。

• MIGRATE_UNMOVABLE,MIGRATE_RECLAIMABLE,MIGRATE_MOVABLE是可互相迁移内存的,所以把他们画在一起,而MIGRATE_RESERVE是不能的,所以被分开。

• 从对迁移块的使用可以看到,除了反向找页和根据需求作页迁徙这种效率的设计以外,迁移块中MIGRATE_MOVABLE和MIGRATE_RECLAIMABLE可以通过换页和释放页来解决Buddy面临的碎页问题(这里我没找到实际代码,不过在思路上是可行的),而MIGRATE_UNMOVABLE可以通过到这两个迁移块中找页解决自己的无大页问题。

Page 14: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

14

现在区域(ZONE)中的结构(Buddy+Migrate)

• 页的释放和传统方式一样,增加的部分是回到哪个迁移块要由页块中的migrate类型来决定。

• 从这里我们可以看到在现在的内存结构中,页块上的迁移类型非常重要,标记了每个页“回家的路”。

Page 15: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

15

现在区域(ZONE)中的结构(Buddy+Migrate)

Page 16: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

16

后备(fallbacks)规则

static int fallbacks[MIGRATE_TYPES][4] = {

[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },

[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },

#ifdef CONFIG_CMA

[MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },

[MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */

#else

[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },

#endif

[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */

#ifdef CONFIG_MEMORY_ISOLATION

[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */

#endif

};

Page 17: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

17

现有内存结构的限制

• 无法分配超过2的(MAX_ORDER-1)次方大小的连续页。

• 即使要分配的页小于2的(MAX_ORDER-1)次方大小,也不能保证某个区域(ZONE)页有这个页。

• 以前有采用alloc_bootmem在启动建立Buddy之前分配内存,但是这些内存无法再被Buddy系统使用,造成了内存的浪费。

• 所以内核于2012年5月引入CMA,或者可称为Contiguous Memory Allocator,连续内存分配器。

Page 18: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

18

CMA结构简介

• 请找彩蛋

CMA

UNMOVABLE RECLAIMABLE

MOVABLE RESERVE

Page 19: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

19

CMA是一个特殊的Migrate类型

CMA

UNMOVABLE RECLAIMABLE

MOVABLE RESERVE

CMA

UNMOVABLE RECLAIMABLE RESERVEMOVABLE

Page 20: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

20

CMA结构简介

• MIGRATE_CMA也是一个迁移类型。

• CMA用来解决前面提到的问题。

• 后面介绍分配会详细的介绍这张图。

Page 21: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

21

Page 22: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

22

CMA的初始化过程

• 在系统初始化还没将内存交给Buddy系统之前,用dma_contiguous_reserve_area函数可以帮助驱动或者系统申请memblock中的一块区域为CMA,并添加到cma_areas数组中。

• 系统默认申请的申请的存入dma_contiguous_def_base。

• 驱动可以根据需要申请自己的CMA区域。

Page 23: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

23

dma_contiguous_reserve_area

Page 24: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

24

CMA的初始化过程

• 在其他内存都被放到Buddy系统后,因为前面已经分配出去,所以CMA的页并没有被初始化到Buddy系统中。

• 这时系统会调用函数cma_init_reserved_areas,其会对CMA结构进行最后的初始化(因为内存相关的操作都要在Buddy系统有内存后才能做,例如分配bitmap)。然后将CMA结构中的每个页都存入MIGRATE_CMA块,并将全部页块标记为MIGRATE_CMA。

• CMA初始化完成,从这个初始化就可以看到CMA内存分别被cma_areas数组和Buddy系统索引。

Page 25: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

25

cma_init_reserved_areas

Page 26: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

26

CMA在Buddy内存结构中的分配和释放

• 根据后备规则,MIGRATE_CMA是MIGRATE_MOVABLE的第一个后备块。

• 其分配方式和其他项目一样,唯一的区别是在前面介绍过的函数try_to_steal_freepages中,MIGRATE_CMA在任何情况下都不能被迁移到其他迁移块。

• 在释放时,这些页也会回到MIGRATE_CMA块中。

Page 27: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

27

Page 28: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

28

后备(fallbacks)规则

static int fallbacks[MIGRATE_TYPES][4] = {

[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },

[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },

#ifdef CONFIG_CMA

[MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },[MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */

#else

[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },

#endif

[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */

#ifdef CONFIG_MEMORY_ISOLATION

[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */

#endif

};

Page 29: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

29

小问题

后面要介绍CMA对连续内存的分配和释放,是比较枯燥的一段,所以我在这里提两个小问题增加点乐趣,能在我自己解答前回答一个问题的同学,送F码一个:

1.这个问题和刚才介绍的部分相关,不想听后面话题的同学可以思考这个,CMA在普通内存结构中的分配是有一定问题的,这也是我在这个话题最后要介绍的,这个问题是什么?

2.这个问题和后面介绍比较相关,为什么MIGRATE_CMA不能迁移给MIGRATE_MOVABLE?

Page 30: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

30

CMA对连续内存的分配

• 首先通过驱动结构dev,找到要使用的cma结构,如果找不到则使用系统分配的dma_contiguous_default_area。

• 进入cma_alloc函数,先对CMA结构加锁,通过CMA结构中的bitmap找到合适的数据块起始位置并做标记。

• 解锁CMA结构。

Page 31: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

31

CMA对连续内存的分配和释放

Page 32: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

32

alloc_contig_range

• 函数cma_alloc将加全局锁cma_mutex。

• 然后调用函数alloc_contig_range真正的对这块CMA内存进行分配。

• 函数alloc_contig_range返回后会对cma_mutex解锁。

• 这个锁的范围在CMA加入后有不少变化,解决了一些死锁的问题。实际使用的早期内核版本的时候要小心死锁的问题。

• 下面对alloc_contig_range内部过程进行介绍。

Page 33: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

33

start_isolate_page_range

• 函数alloc_contig_range首先会调用start_isolate_page_range。

• 先将全部页块标记为MIGRATE_ISOLATE,这样后面这些页被自动放入MIGRATE_ISOLATE的空闲列表。

Page 34: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

34

start_isolate_page_range

Page 35: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

35

start_isolate_page_range

• 这是已经在CMA空闲列表中的页反而有可能被分配出去,而且没有操作能自动将他们移到MIGRATE_ISOLATE空闲列表中,所以讲这些空闲页移动到MIGRATE_ISOLATE空闲列表中。

• 接着这个函数就返回了。

Page 36: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

36

start_isolate_page_range

Page 37: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

37

__alloc_contig_migrate_range

• 接alloc_contig_range函数会调用函数__alloc_contig_migrate_range,这个函数的目的就是把剩下的页都移到MIGRATE_ISOLATE空闲列表中。

• 其首先会调用函数migrate_prep,其会调用函数lru_add_drain_all。这个函数的主要作用是把lru列表中的页都删掉,方便后续处理。

• 接着其会循环调用函数isolate_migratepages_range、函数reclaim_clean_pages_from_list、函数migrate_pages,对全部非空闲的页进行处理。

• 对所有页处理完成后,这个函数就会返回。• 下面对这三个函数依次进行介绍。

Page 38: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

38

isolate_migratepages_range

• 找出没处理过的COMPACT_CLUSTER_MAX个页,放入一个列表cc->migratepages。

Page 39: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

39

isolate_migratepages_range

Page 40: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

40

reclaim_clean_pages_from_list

• 将cc->migratepages中干净可以回收的页,一般是文件cache,回收并释放掉。

• 因为页块已经被标记为MIGRATE_ISOLATE,所以其会自动被加入到MIGRATE_ISOLATE空闲页中。

Page 41: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

41

reclaim_clean_pages_from_list

Page 42: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

42

第二个小问题是否有人回答

• 后面就会揭晓答案

Page 43: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

43

migrate_pages

• 对无法通过其他方式释放掉的页,就会到这里进行最终的迁移页处理。

• 这个函数会先分配一个页,两个页进行切换,最后将页释放掉并交给MIGRATE_ISOLATE空闲页。

• 前面问题2答案也揭晓了,CMA只能作为MIGRATE_MOVABLE分配出去,因为只有MIGRATE_MOVABLE类型是随便移动的。但是如果CMA迁移成MIGRATE_MOVABLE,因为MIGRATE_UNMOVABLE,MIGRATE_RECLAIMABLE和MIGRATE_MOVABLE可能互相迁徙,所以CMA就有可能迁移给另两者,则无法再移动。则CMA就无法再当连续页分配了。

Page 44: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

44

migrate_pages

Page 45: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

45

migrate_pages

Page 46: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

46

drain_all_pages

• 从函数__alloc_contig_migrate_range返回后,alloc_contig_range函数会再次调用lru_add_drain_all(),这里为何再次调用没搞明白。

• 接着调用drain_all_pages(),将PCP中缓冲的页都释放掉,从而其中标记为MIGRATE_ISOLATE的页会被释放倒MIGRATE_ISOLATE空闲列表中。

Page 47: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

47

drain_all_pages

Page 48: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

48

页面分配前的扩张

• 现在全部页面都已经都进入了MIGRATE_ISOLATE空闲列表中了,唯一的问题是开头的结尾的部分页可能在Buddy结构中一个大页中的一部分。

函数alloc_contig_range对他们的解决办法对开头页进行检查,如果是则将分配的开头地址向这个块的开头页扩展。

• 后续操作在页面分配结束后介绍。

Page 49: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

49

test_pages_isolated

• 这里对全部页面的状态进行最后的检查。如果有状态不对的,函数会出错返回。

Page 50: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

50

test_pages_isolated

Page 51: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

51

isolate_freepages_range

• 这里将指定好的页面从Buddy系统中抽取出来标记为已经使用。

Page 52: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

52

isolate_freepages_range

Page 53: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

53

页面分配后的收缩

• 将刚才多分配的页面全部释放掉,通过这两步的操作,分配给连续内存的页面就不再是某个Buddy块中的一部分。

Page 54: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

54

undo_isolate_page_range

• 最后函数alloc_contig_range会将全部标记为MIGRATE_ISOLATE的页面标记回MIGRATE_CMA。

因为这时要分配给连续内存的页已经都从Buddy系统中抽出去了,不论任何状态都不再会被使用到了。

• 函数alloc_contig_range返回,函数cma_alloc返回,最终连续页被返回给驱动。

Page 55: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

55

undo_isolate_page_range

Page 56: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

56

CMA对连续内存的释放

• 因为分配出去的页已经被标记为MIGRATE_CMA了,所以对他们的释放就很简单。

• 先把这些页释放掉,让他们回到MIGRATE_CMA空闲列表。

• 接着清楚掉bitmap中的项目,就可以了。

Page 57: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

57

CMA对连续内存的释放

Page 58: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

58

现在要回答问题1了

• 有没有同学回答?

Page 59: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

59

CMA在实际使用中遇到的问题和解决思路

• 因为MIGRATE_CMA只是MIGRATE_MOVABLE的备份项目。

在系统初始化时,普通内存除了MIGRATE_RESERVE,其他内存都是标记为MIGRATE_MOVABLE的。在很多系统包括Android系统,MIGRATE_MOVABLE块占用的内存也最大。

• 所以使用到MIGRATE_CMA时,其之外的内存已经所剩无几。而这时系统很容易除非lowmemorykiller,如果设置lowmemorykiller的规则,则可能触发的是oomkiller。

• 最终的结果是即使系统经常杀掉进程,CMA中还有大量内存可用,CMA没有起到原来的设计目的。

Page 60: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

60

Page 61: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

61

CMA_AGGRESSIVE

• 增加了一个叫CMA_AGGRESSIVE的功能,其主要思路是在找页第一阶段,函数__rmqueue_smallest中,如果条件允许,先从CMA中找页。

• 现在引入了一个新的问题,这样大部分的CMA页都会被分出去,在分配连续页的时候会有一些问题被引入。

Page 62: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

62

CMA_AGGRESSIVE

Page 63: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

63

新引入问题的处理

• 函数migrate_pages中,因为有换页的操作,这里就有个分配页的问题。如果系统中本身内存页比较少,就很可能需要调用函数shrink_slab取得一部分内存。但是因为系统结构的限制,这个接口每次只能释放COMPACT_CLUSTER_MAX个页,系统一般定义为32。

而这里分配页也会使用到函数__rmqueue_smallest,很可能会再次去分配CMA的页。

• 所以当分配的连续内存比较多,而且系统内存本身已经不足,很可能整个分配连续内存的过程会变的缓慢并且出错率很高。

• 解决方法:

1. 在申请连续内存开始的部分根据系统内存大小调用一次shrink相关函数分配出一部分free内存,提高换页的成功率和速度。

2. 在申请连续页的开头和结尾操作原子变量作为轻量锁,在函数__rmqueue_smallest先去检查这个锁,成功才去从CMA中分配页,介绍Buddy系统分配页和CMA系统分配页的冲突。

Page 64: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

64

migrate_pages

Page 65: teawater(朱辉)的主页teawater.github.io/presentation/2014clk_cma.pdfÎÏ ¤ ¨ ¥ 34 ÐÑ YN ÒÓ:;ÔÕÖ×ØW ¨ ÙÚN Û ² µ µ ÜÝhÜÝ n]oÞßY]oàáhOâãäå ãæ ÐÛ

65

谢谢!问题?

• weibo: @teawater_z 欢迎在线吐槽