<kernel v5.0>
page_alloc_init()
mm/page_alloc.c
void __init page_alloc_init(void) { int ret; ret = cpuhp_setup_state_nocalls(CPUHP_PAGE_ALLOC_DEAD, "mm/page_alloc:dead", NULL, page_alloc_cpu_dead); WARN_ON(ret < 0); }
cpu가 다운될 때 페이지 할당자와 관련되어 사용되는 각종 per-cpu용 캐시(pagevec, pcp) 및 vm 통계용 메모리를 회수한다.
- 코드 라인 5~7에서 cpuhp_setup_state_nocalls() 함수는 cpu hot-plug 상태가 변동되어 cpu의 시작과 종료 시 호출될 함수를 지정할 수 있다.
- 기존에는 hotcpu notifier를 사용한 방법을 사용하였었는데 커널 v4.10-rc에서 수정되었다.
- 새 방법 참고: mm/page_alloc: Convert to hotplug state machine
- 기존 방법 참고: hotcpu_notifier() | 문c
- 기존에는 hotcpu notifier를 사용한 방법을 사용하였었는데 커널 v4.10-rc에서 수정되었다.
page_alloc_cpu_dead()
mm/page_alloc.c
static int page_alloc_cpu_dead(unsigned int cpu) { lru_add_drain_cpu(cpu); drain_pages(cpu); /* * Spill the event counters of the dead processor * into the current processors event counters. * This artificially elevates the count of the current * processor. */ vm_events_fold_cpu(cpu); /* * Zero the differential counters of the dead processor * so that the vm statistics are consistent. * * This is only okay since the processor is dead and cannot * race with what we are doing. */ cpu_vm_stats_fold(cpu); return 0; }
해당 cpu용으로 사용되던 페이지 할당자와 관련된 메모리(pagevec, pcp)들을 회수하고 이벤트 카운터와 vm 카운터들을 갱신한다.
- 코드 라인 3에서 다운된 @cpu가 사용하는 페이지 할당자의 회수 매커니즘 lruvec에 사용하던 per-cpu 캐시들에서 페이지를 회수하여 해당 zone(또는 memory cgroup의 zone)에 있는 lruvec으로 이전한다.
- 코드 라인 4에서 다운된 @cpu가 사용하는 버디 시스템의 0 페이지 할당 전용 캐시인 Per-Cpu Page Frame Cache 페이지를 해지한다.
- 코드 라인 12에서 다운된 @cpu에 대한 이벤트 카운터들을 현재 cpu의 이벤트 카운터에 더한 후 fold된 cpu에 대한 이벤트 카운터를 모두 clear 한다.
- 코드 라인 21에서 다운된 @cpu의 전체 pageset event를 zone 및 전역에 옮기고 clear 한다.
vm_events_fold_cpu()
mm/vmstat.c
/* * Fold the foreign cpu events into our own. * * This is adding to the events on one processor * but keeps the global counts constant. */ void vm_events_fold_cpu(int cpu) { struct vm_event_state *fold_state = &per_cpu(vm_event_states, cpu); int i; for (i = 0; i < NR_VM_EVENT_ITEMS; i++) { count_vm_events(i, fold_state->event[i]); fold_state->event[i] = 0; } }
fold될 cpu에 대한 event[] 카운터들을 현재의 cpu event[] 카운터에 더한 후 fold될 cpu에 대한 event[]는 모두 clear 한다.
cpu_vm_stats_fold()
mm/vmstat.c
/* * Fold the data for an offline cpu into the global array. * There cannot be any access by the offline cpu and therefore * synchronization is simplified. */ void cpu_vm_stats_fold(int cpu) { struct zone *zone; int i; int global_diff[NR_VM_ZONE_STAT_ITEMS] = { 0, }; for_each_populated_zone(zone) { struct per_cpu_pageset *p; p = per_cpu_ptr(zone->pageset, cpu); for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) if (p->vm_stat_diff[i]) { int v; v = p->vm_stat_diff[i]; p->vm_stat_diff[i] = 0; atomic_long_add(v, &zone->vm_stat[i]); global_diff[i] += v; } } fold_diff(global_diff); }
fold될 cpu에 대한 vm_stat_diff[] 카운터들을 zone->vm_stat[] 카운터에 더하고 전역 vm_stat[]에도 도한 후 fold될 cpu에 대한 카운터는 모두 clear 한다.
fold_diff()
mm/vmstat.c
/* * Fold a differential into the global counters. * Returns the number of counters updated. */ static int fold_diff(int *diff) { int i; int changes = 0; for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) if (diff[i]) { atomic_long_add(diff[i], &vm_stat[i]); changes++; } return changes; }
인수로 전달 받은 vm_stat 값을 전역 vm_stat[]에 더하고 변경 된 항목 수가 리턴된다.
참고
- hotcpu_notifier() | 문c
- Per-CPU Page Frame Cache (zone->pageset) | 문c
- [Linux] pageflags로 살펴본 메모리의 일생 | 문c
- From mm-summi | Fujitsu – 다운로드
- LRU Lists & pagevecs | 문c
- Buddy Memory Allocator (해지) | 문c