3.4.2 特定于体系结构的设置 (五):冷热缓存的初始化

这里讨论冷热缓存相关的数据结构的初始化,以及用于控制缓存填充行为的“水印”的计算。Zone_pcp_init负责初始化该缓存,该函数由free_area_init_nodes调用。

start_kernel

->setup_arch

  ->machine_specific_memory_setup

  ->setup_memory

  ->paging_init

    -> free_area_init

      ->free_area_init_node

                ->free_area_init_core

                  ->zone_pcp_init

static __meminit void zone_pcp_init(struct zone *zone)
{
        /*
         * per cpu subsystem is not up at this point. The following code
         * relies on the ability of the linker to provide the
         * offset of a (static) per cpu variable into the per cpu area.
         */
        zone->pageset = &boot_pageset;
 
        if (populated_zone(zone))
                printk(KERN_DEBUG "  %s zone: %lu pages, LIFO batch:%u\n",
                        zone->name, zone->present_pages,
                                         zone_batchsize(zone));
}

在用 zone_batchsize 算出批量大小后,代码将遍历系统中的所有 CPU ,同时调用 setup_pageset 填充每个 per_cpu_pageset 实例的常量。在调用该函数时使用了 zone_pcp 宏来选择与当前 CPU 相关的内存域 pageset 实例。

如下是zone_batchsize计算水印的过程:

static int zone_batchsize(struct zone *zone)
{
#ifdef CONFIG_MMU
        int batch;
        batch = zone->managed_pages / 1024;
        if (batch * PAGE_SIZE > 512 * 1024)
                batch = (512 * 1024) / PAGE_SIZE;
        batch /= 4;             /* We effectively *= 4 below */
        if (batch < 1)
                batch = 1;
        batch = rounddown_pow_of_two(batch + batch/2) - 1;
 
        return batch;
 
#else
             return 0;
#endif
}


上述代码计算得到batch,大约相当于内存域中页数的0.25%o。移位操作确保计算结果具有2^N-1的形式。根据经验,该值在大多数系统负载下都能最小化缓存混叠效应。Fls是一个特定于计算机的操作,用于算出一个值中置位的最低比特位。要注意,这种校正会使结果值偏离内存域中页数的0.25%o.batch = 22时偏差最大,由于22+11-1=32fls会算出比特位5是最低置位比特位,而1<<5-1=31,通常情况下偏差都比这小,实际上可以忽略。

内存域中的内存数量超过512MB时,批量大小并不增长。对于页面大小为4096KB的系统,如果页数超过13172,则会达到512MB的限制。在setup_pageset中考虑缓存极限的计算时,batch值是有意义的。

setup_pageset

  ->pageset_init

  ->pageset_set_batch

    ->pageset_update

static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch)
{
        pageset_init(p);
        pageset_set_batch(p, batch);
}
 
static void pageset_init(struct per_cpu_pageset *p)
{
        struct per_cpu_pages *pcp;
        int migratetype;
 
        memset(p, 0, sizeof(*p));
 
        pcp = &p->pcp;
        pcp->count = 0;
        for (migratetype = 0; migratetype < MIGRATE_PCPTYPES; migratetype++)
                INIT_LIST_HEAD(&pcp->lists[migratetype]);
}
 
static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch)
{
        pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch));
}
 
static void pageset_update(struct per_cpu_pages *pcp, unsigned long high,
                unsigned long batch)
{
       /* start with a fail safe value for batch */
        pcp->batch = 1;
        smp_wmb();
 
       /* Update high, then batch, in order */
        pcp->high = high;
        smp_wmb();
 
        pcp->batch = batch;
}


对热页来说,下限为0,上限为6*batch,缓存中页的平均数量大约是4*batch,因为内核不会让缓存水平降到太低,batch*4相当于内存域中页数的千分之一(这也是zone_batchsize试图将批量大小优化到总页数0.25%o的原因)。IA-32处理器上L2缓存数量在0.25MB~2MB之间,因此在冷热缓存中保持更多的内存是无意义的。根据经验,缓存大小是主内存的千分之一。考虑到当前系统每个CPU配备的物理内存大约在1GB~2GB,该规则是有意义的。这样,计算出批量大小使得冷热缓存中的也有可能放置到L2缓存中。

冷页列表的水印稍低一些,因为冷页并不放置到缓存中,只用于一些不太关注性能的操作,其上限是batch值的两倍。

Pcp->batch决定了在重新填充列表时,有多少页会立即使用。出于性能方面的考虑,一般会向列表添加连续的多页,而不是单页。

zone_pcp_init结束时,会输出各个内存域的页数及计算出的批量大小,从启动日志可以看到。

[    0.000000] Initmem setup node 0 [mem 0x00001000-0x6dffffff]

[    0.000000] On node 0 totalpages: 447905

[    0.000000]   DMA zone: 32 pages used for memmap

[    0.000000]   DMA zone: 0 pages reserved

[    0.000000]   DMA zone: 3995 pages, LIFO batch:0

[    0.000000]   Normal zone: 1744 pages used for memmap

[    0.000000]   Normal zone: 222974 pages, LIFO batch:31

[    0.000000]   HighMem zone: 1745 pages used for memmap

[    0.000000]   HighMem zone: 220936 pages, LIFO batch:31

 

你可能感兴趣的:(3.4.2 特定于体系结构的设置 (五):冷热缓存的初始化)