setup_per_cpu_pageset()

 

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 값을 갱신한다.

 

참고

 

댓글 남기기