BiscuitOS/Linux 上构建 GFP_ZONE_TABLE【转】

转自:http://blog.chinaunix.net/uid-30647659-id-5690543.html

本贴主要讲述如何构建 BiscuitOS 或 Linux 上的 GFP_ZONE_TABLE 表.
当在内核中分配内存的时候,必须指定分配的标志,如:
kmalloc(size,gfp)
kmem_cache_alloc(cache,gfp)
内核通过 gfp 标志来判断从哪个区间分配内存,内核将物理内存按区间分配,在 Linux 中存在 ZONE_DMA,ZONE_NORMAL,ZONE_DMA32 和 ZONE_HIGHMEM 几个 zone.
每个 zone 管理各自内存,调用者只要使用 gfp 标志就可以从不同的 gfp 区间获得内存.
内核使用 gfp_zone(gfp) 函数来将 gfp 标志转换为对应的 zone.其实现如下:

点击(此处)折叠或打开

  1. static inline enum zone_type gfp_zone(gfp_t flags)
  2. {
  3.     enum zone_type z;
  4.     int bit = (int)(flags & GFP_ZONEMASK);
  5.     z = (GFP_ZONE_TABLE >> (bit * ZONES_SHIFT)) &
  6.             ((1 << ZONES_SHIFT) - 1);
  7.     if(__builtin_constant_p(bit))
  8.         BUILD_BUG_ON((GFP_ZONE_BAD >> bit) & 1);
  9.     else {
  10. #ifdef CONFIG_DEBUG_VM
  11.         BUG_ON((GFP_ZONE_BAD >> bit) & 1);
  12. #endif
  13.     }
  14.     return z;
  15. }
从上面代码实现过程中可以看出这个转换依赖 GFP_ZONE_TABLE 表.其定义如下:
#define GFP_ZONE_TABLE ( \
        (ZONE_NORMAL << 0 * ZONES_SHIFT)   \
        | (OPT_ZONE_DMA << ___GFP_DMA * ZONES_SHIFT) \
        | (OPT_ZONE_HIGHMEM << ___GFP_HIGHMEM * ZONES_SHIFT) \
        | (OPT_ZONE_DMA32 << ___GFP_DMA32 * ZONES_SHIFT)  \
        | (ZONE_NORMAL << ___GFP_MOVABLE * ZONES_SHIFT)   \
        | (OPT_ZONE_DMA << (___GFP_MOVABLE | ___GFP_DMA) * ZONES_SHIFT)  \
        | (ZONE_MOVABLE << (___GFP_MOVABLE | ___GFP_HIGHMEM) * ZONES_SHIFT) \
        | (OPT_ZONE_DMA32 << (___GFP_MOVABLE | ___GFP_DMA32) * ZONES_SHIFT) \
        )
以及另外一个表 GFP_ZONE_BAD.
#define GFP_ZONE_BAD (   \
          1 << (___GFP_DMA | ___GFP_HIGHMEM)                  \
        | 1 << (___GFP_DMA | ___GFP_DMA32)               \
        | 1 << (___GFP_DMA32 | ___GFP_HIGHMEM)                   \
        | 1 << (___GFP_DMA | ___GFP_DMA32 | ___GFP_HIGHMEM)       \
        | 1 << (___GFP_MOVABLE | ___GFP_HIGHMEM | ___GFP_DMA)   \
        | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA)    \
        | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_HIGHMEM)    \
        | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA | ___GFP_HIGHMEM) \
)
内核是如何构建这两张表?本贴重点讲述内核如何构建这两张表.
首先内核将一个节点分作不同的管理区,每个管理区使用 struct zone 进行管理,内核在 zone 基本分为:
ZONE_DMA,ZONE_NORMAL,ZONE_DMA32 和 ZONE_HIGHMEM,每个管理区使用 gfp 标志表示为:
#define ___GFP_DMA               0x01u
#define ___GFP_HIGHMEM     0x02u
#define ___GFP_DMA32          0x04u
#define ___GFP_MOVABLE     0x08u
在 gfp 标志中低 3 位来表示内存从哪个 zone 获得,其掩码为:
#define GFP_ZONEMASK  (__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32 | __GFP_MOVABLE)
内核规定 ___GFP_DMA,___GFP_HIGHMEM 和 ___GFP_DMA32 其两个或全部不能同时存在于 gfp 标志中.
四个标志排列组合后可以获得下表:
序号 ___GFP_DMA ___GFP_HIGHMEM ___GFP_DMA32 ___GFP_MOVABLE 组合结果
0 0 0 0 0 从 ZONE_NORMAL 中分配
1 1 0 0 0 从 ZONE_NORMAL 或
ZONE_DMA 中分配
2 0 1 0 0 从 ZONE_NORMAL 或
ZONE_HIGHMEM 中分配
3 1 1 0 0 不能同时满足,错误
4 0 0 1 0 从 ZONE_NORMAL 或
ZONE_DMA32 中分配
5 1 0 1 0 不能同时满足,错误
6 0 1 1 0 不能同时满足,错误
7 1 1 1 0 不能同时满足,错误
8 0 0 0 1 从 ZONE_NORMAL 或
ZONE_MOVABLE 获得
9 1 0 0 1 从 ZONE_NORMAL 或
(ZONE_DMA + ZONE_MOVALE) 获得
a 0 1 0 1 从 ZONE_MOVABLE 获得
b 1 1 0 1 不能同时满足,错误
c 0 0 1 1 从 ZONE_DMA
d 1 0 1 1 不能同时满足,错误
e 0 1 1 1 不能同时满足,错误
f 1 1 1 1 不能同时满足,错误
从上面的表很容易构建 GFP_ZONE_BAD,将所有错误情况或起来就行.
1. (___GFP_DMA      | ___GFP_HIGHMEM)
2. (___GFP_DMA      | ___GFP_DMA32)
3. (___GFP_DMA32 | ___GFP_HIGHMEM )
4. (___GFP_DMA32 | ___GFP_HIGHMEM | ___GFP_DMA)
5. (___GFP_DMA     | ___GFP_HIGHMEM | ___GFP_MOVABLE )
6. (___GFP_DMA     | ___GFP_DMA32       | ___GFP_MOVABLE)
7. (___GFP_DMA32 | ___GFP_HIGHMEM | ___GFP_MOVABLE)
8. (___GFP_DMA32 | ___GFP_DMA32       | ___GFP_HIGHMEM | ___GFP_MOVABLE)
将上面 8 种情况合成 BAD TABLE 如下:
#define GFP_ZONE_BAD (   \
          1 << (___GFP_DMA | ___GFP_HIGHMEM)                  \
        | 1 << (___GFP_DMA | ___GFP_DMA32)               \
        | 1 << (___GFP_DMA32 | ___GFP_HIGHMEM)                   \
        | 1 << (___GFP_DMA | ___GFP_DMA32 | ___GFP_HIGHMEM)       \
        | 1 << (___GFP_MOVABLE | ___GFP_HIGHMEM | ___GFP_DMA)   \
        | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA)    \
        | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_HIGHMEM)    \
        | 1 << (___GFP_MOVABLE | ___GFP_DMA32 | ___GFP_DMA | ___GFP_HIGHMEM) \
)
剩下的 8 种情况就是可以分配.分别为:
1.  0
2. (___GFP_DMA)
3. (___GFP_HIGHMEM)
4. (___GFP_DMA32)
5. (___GFP_MOVABLE)
6. (___GFP_DMA           | ___GFP_MOVABLE)
7. (___GFP_HIGHMEM | ___GFP_MOVABLE)
8. (___GFP_DMA32      | ___GFP_MOVABLE)
构建初期表为
\)
#define TABLE (                                                                  \
        (1 << 0)                                                                        \
      |  (1<<  ___GFP_DMA)                                                    \
      |  (1 <<  ___GFP_HIGHMEM)                                          \
      |  (1 <<  ___GFP_DMA32)                                                \
      |  (1 <<  ___GFP_MOVABLE)                                            \
      |  (1 << ___GFP_DMA           | ___GFP_MOVABLE)        \
      |  (1 << ___GFP_HIGHMEM | ___GFP_MOVABLE)        \
      |  (1 << ___GFP_DMA32      | ___GFP_MOVABLE)        \
)将对应的 zone 填充进去,其中
OPT_ZONE_DMA 代表 ___GFP_NORMAL 或者 ___GFP_NORMAL
OPT_ZONE_HGIHMEM 代表 ___GFP_NORMAL 或者 ___GFP_HIGHMEM
OPT_ZONE_DMA32 代表 ___GFP_NORMAL 或者 ___GFP_DMA32
根据表的分析,可获得下面结论:
#define TABLE (                                                                  \
        (ZONE_NORMAL << 0)                                                                        \
      |  (OPT_ZONE_DMA <<  ___GFP_DMA)                                                    \
      |  (OPT_ZONE_HIGHMEM <<  ___GFP_HIGHMEM)                                          \
      |  (OPT_ZONE_DMA32 <<  ___GFP_DMA32)                                                \
      |  (ZONE_NORMAL <<  ___GFP_MOVABLE)                                            \
      |  (OPT_ZONE_DMA << ___GFP_DMA           | ___GFP_MOVABLE)        \
      |  (ZONE_MOVABLE << ___GFP_HIGHMEM | ___GFP_MOVABLE)        \
      |  (OPT_ZONE_DMA32 << ___GFP_DMA32      | ___GFP_MOVABLE)        \
)
由于不同的平台会使用不同数量的 zone 管理区,常见的 zone 分配为 ZONE_NORMAL 和 ZONE_HIGHMEM 搭配.
于是内核将 TABLE 的位宽使用 ZONES_SHIFT 表示,如一个具有 ZONE_DMA,ZONE_DMA32,ZONE_NORMAL 和 ZONE_HIGHMEM 的平台
ZONES_SHIFT 为 2,表示 GFP_ZONE_TABLE 每个选项的位宽为 2.于是最终的表为:
#define GFP_ZONE_TABLE ( \
        (ZONE_NORMAL << 0 * ZONES_SHIFT)   \
        | (OPT_ZONE_DMA << ___GFP_DMA * ZONES_SHIFT) \
        | (OPT_ZONE_HIGHMEM << ___GFP_HIGHMEM * ZONES_SHIFT) \
        | (OPT_ZONE_DMA32 << ___GFP_DMA32 * ZONES_SHIFT)  \
        | (ZONE_NORMAL << ___GFP_MOVABLE * ZONES_SHIFT)   \
        | (OPT_ZONE_DMA << (___GFP_MOVABLE | ___GFP_DMA) * ZONES_SHIFT)  \
        | (ZONE_MOVABLE << (___GFP_MOVABLE | ___GFP_HIGHMEM) * ZONES_SHIFT) \
        | (OPT_ZONE_DMA32 << (___GFP_MOVABLE | ___GFP_DMA32) * ZONES_SHIFT) \
        )
 
posted @ 2020-08-16 23:17  Sky&Zhang  阅读(513)  评论(0编辑  收藏  举报