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