setup_per_cpu_pageset()
mm/page_alloc.c
/*
* Allocate per cpu pagesets and initialize them.
* Before this call only boot pagesets were available.
*/
void __init setup_per_cpu_pageset(void)
{
struct pglist_data *pgdat;
struct zone *zone;
for_each_populated_zone(zone)
setup_zone_pageset(zone);
for_each_online_pgdat(pgdat)
pgdat->per_cpu_nodestats =
alloc_percpu(struct per_cpu_nodestat);
}
Per-CPU Page Frame Cache를 각 zone별로 할당한다. 그리고 노드별로 통계를 per-cpu로 할당한다.
setup_pageset()
mm/page_alloc.c
/*
* Boot pageset table. One per cpu which is going to be used for all
* zones and all nodes. The parameters will be set in such a way
* that an item put on a list will immediately be handed over to
* the buddy list. This is safe since pageset manipulation is done
* with interrupts disabled.
*
* The boot_pagesets must be kept even after bootup is complete for
* unused processors and/or zones. They do play a role for bootstrapping
* hotplugged processors.
*
* zoneinfo_show() and maybe other functions do
* not check if the processor is online before following the pageset pointer.
* Other parts of the kernel may not check if the zone is available.
*/
static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch)
{
pageset_init(p);
pageset_set_batch(p, batch);
}
부트 페이지셋 테이블을 할당 받고 초기화한다.
pageset_init()
mm/page_alloc.c
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]);
}
- memset(p, 0, sizeof(*p));
- 인수로 지정된 per_cpu_pageset 구조체 p를 0으로 clear 한다.
- pcp->count = 0;
- 페이지 셋의 페이지들 카운트를 0으로 초기화한다.
- for (migratetype = 0; migratetype < MIGRATE_PCPTYPES; migratetype++) INIT_LIST_HEAD(&pcp->lists[migratetype]);
- p->pcp->lists[] 배열 중 MIGRATE_PCPTYPES 까지 만큼만 리스트를 초기화한다.
pageset_set_batch()
mm/page_alloc.c
/* a companion to pageset_set_high() */
static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch)
{
pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch));
}
pcp의 high 값으로 batch의 6배를 설정한다. 요청한 batch 값은 최소 1 이상 값으로 설정한다.
pageset_update()
mm/page_alloc.c
/*
* pcp->high and pcp->batch values are related and dependent on one another:
* ->batch must never be higher then ->high.
* The following function updates them in a safe manner without read side
* locking.
*
* Any new users of pcp->batch and pcp->high should ensure they can cope with
* those fields changing asynchronously (acording the the above rule).
*
* mutex_is_locked(&pcp_batch_high_lock) required when calling this function
* outside of boot time (or some other assurance that no concurrent updaters
* exist).
*/
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;
}
- 지정된 순서 대로 batch를 1로 한 후 high와 batch를 대입한다.
setup_zone_pageset()
mm/page_alloc.c
static void __meminit setup_zone_pageset(struct zone *zone)
{
int cpu;
zone->pageset = alloc_percpu(struct per_cpu_pageset);
for_each_possible_cpu(cpu)
zone_pageset_init(zone, cpu);
}
- zone->pageset = alloc_percpu(struct per_cpu_pageset);
- 지정된 zone의 pageset에 percpu 형태로 메모리를 할당한다.
- for_each_possible_cpu(cpu) zone_pageset_init(zone, cpu);
- 모든 possible cpu에 대해 루프를 돌며 zone에 대한 페이지셋을 초기화한다.
zone_pageset_init()
mm/page_alloc.c
static void __meminit zone_pageset_init(struct zone *zone, int cpu)
{
struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu);
pageset_init(pcp);
pageset_set_high_and_batch(zone, pcp);
}
- struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu);
- zone에 대한 pageset per-cpu 데이터를 가져온다.
- pageset_init(pcp);
- pageset을 초기화한다.
pageset_set_high_and_batch()
mm/page_alloc.c
static void pageset_set_high_and_batch(struct zone *zone,
struct per_cpu_pageset *pcp)
{
if (percpu_pagelist_fraction)
pageset_set_high(pcp,
(zone->managed_pages /
percpu_pagelist_fraction));
else
pageset_set_batch(pcp, zone_batchsize(zone));
}
pageset_set_high()
mm/page_alloc.c
/*
* pageset_set_high() sets the high water mark for hot per_cpu_pagelist
* to the value high for the pageset p.
*/
static void pageset_set_high(struct per_cpu_pageset *p,
unsigned long high)
{
unsigned long batch = max(1UL, high / 4);
if ((high / 4) > (PAGE_SHIFT * 8))
batch = PAGE_SHIFT * 8;
pageset_update(&p->pcp, high, batch);
}
페이지셋에 high값과 batch 값을 갱신하는데 batch 값은 high /4 값으로 하되 1~96 범위로 한정한다.
- unsigned long batch = max(1UL, high / 4);
- batch에 high / 4 값을 대입하되 최소 1보다 커야 한다.
- if ((high / 4) > (PAGE_SHIFT * 8)) batch = PAGE_SHIFT * 8;
- high / 4 값이 PAGE_SHIFT(12) * 8 보다 큰 경우 batch 값을 PAGE_SHIFT(12) * 8로 제한한다.
- pageset_update(&p->pcp, high, batch);
- 페이지셋의 high, batch 값을 갱신한다.
참고
- setup_per_cpu_pageset() | 문c – 현재 글
- Per-CPU Page Frame Cache (zone->pageset) | 문c