Slub Memory Allocator -9- (캐시 Shrink)

<kernel v5.0>

kmem_cache_shrink()

mm/slab_common.c

1/**
2 * kmem_cache_shrink - Shrink a cache.
3 * @cachep: The cache to shrink.
4 *
5 * Releases as many slabs as possible for a cache.
6 * To help debugging, a zero exit status indicates all slabs were released.
7 */
01int kmem_cache_shrink(struct kmem_cache *cachep)
02{
03        int ret;
04 
05        get_online_cpus();
06        get_online_mems();
07        ret = __kmem_cache_shrink(cachep);
08        put_online_mems();
09        put_online_cpus();
10        return ret;
11}
12EXPORT_SYMBOL(kmem_cache_shrink);

요청한 슬랩 캐시에서 해지가능한 슬랩 페이지들을 찾아 모두 해지시킨다.

 

__kmem_cache_shrink()

mm/slub.c

1/*
2 * kmem_cache_shrink discards empty slabs and promotes the slabs filled
3 * up most to the head of the partial lists. New allocations will then
4 * fill those up and thus they can be removed from the partial lists.
5 *
6 * The slabs with the least items are placed last. This results in them
7 * being allocated from last increasing the chance that the last objects
8 * are freed in them.
9 */
01int __kmem_cache_shrink(struct kmem_cache *s)
02{
03        int node;
04        int i;
05        struct kmem_cache_node *n;
06        struct page *page;
07        struct page *t;
08        struct list_head discard;
09        struct list_head promote[SHRINK_PROMOTE_MAX];
10        unsigned long flags;
11        int ret = 0;
12 
13        flush_all(s);
14        for_each_kmem_cache_node(s, node, n) {
15                INIT_LIST_HEAD(&discard);
16                for (i = 0; i < SHRINK_PROMOTE_MAX; i++)
17                        INIT_LIST_HEAD(promote + i);
18 
19                spin_lock_irqsave(&n->list_lock, flags);
20 
21                /*
22                 * Build lists of slabs to discard or promote.
23                 *
24                 * Note that concurrent frees may occur while we hold the
25                 * list_lock. page->inuse here is the upper limit.
26                 */
27                list_for_each_entry_safe(page, t, &n->partial, lru) {
28                        int free = page->objects - page->inuse;
29 
30                        /* Do not reread page->inuse */
31                        barrier();
32 
33                        /* We do not keep full slabs on the list */
34                        BUG_ON(free <= 0);
35 
36                        if (free == page->objects) {
37                                list_move(&page->lru, &discard);
38                                n->nr_partial--;
39                        } else if (free <= SHRINK_PROMOTE_MAX)
40                                list_move(&page->lru, promote + free - 1);
41                }
42 
43                /*
44                 * Promote the slabs filled up most to the head of the
45                 * partial list.
46                 */
47                for (i = SHRINK_PROMOTE_MAX - 1; i >= 0; i--)
48                        list_splice(promote + i, &n->partial);
49 
50                spin_unlock_irqrestore(&n->list_lock, flags);
51 
52                /* Release empty slabs */
53                list_for_each_entry_safe(page, t, &discard, lru)
54                        discard_slab(s, page);
55 
56                if (slabs_node(s, node))
57                        ret = 1;
58        }
59 
60        return ret;
61}

요청한 슬랩 캐시에서 해지가능한 슬랩 페이지들을 찾아 모두 해지시키기 위해 다음과 같은 과정을 수행한다.

  • n->partial 리스트의 슬랩 페이지들 중 사용중인 object가 없는 경우 슬랩 페이지들을 버디 시스템으로 되돌린다.
  • n->partial 리스트의 슬랩 페이지들 중 32개 미만의 free object들은 asscending 정렬한다.

 

  • 코드 라인 13에서 슬랩 캐시의 per cpu 슬랩 페이지들을 모두 n->partial 리스트로 옮긴다.
  • 코드 라인 14~17에서 슬랩 캐시의 노드를 순회하며 discard 리스트를 초기화하고, SHRINK_PROMOTE_MAX(32) 만큼 promote[] 리스트를 초기화한다.
  • 코드 라인 27~41에서 n->partial 리스트의 슬랩 페이지들을 순회하며 object가 모두 free object들로 구성된 슬랩 페이지는 discard 리스트에 추가하고, free object 수가 SHRINK_PROMOTE_MAX(32) 이하인 경우 promote[free-1] 리스트에 추가한다.
  • 코드 라인 47~48에서 n->partial 리스트의 선두에 promote[i] 리스트를 추가한다.
    • 결국 free object가 1~32개 순으로 정렬되어 선두에 추가된다.
    • 가장 선두에 free object가 가장 적은 슬랩 페이지를 위로 배치하여 object 할당 시 슬랩 페이지가 리스트 관리에서 빨리 없어질 수 있게 한다.
  • 코드 라인 53~54에서 discard 리스트에 있는 슬랩 페이지들을 모두 해제하여 버디 시스템으로 돌려준다.
  • 코드 라인 56~57에서 slub 디버그깅 중에 노드에 슬랩이 남아있는 경우 함수 종료 시 1을 반환하게 한다.

 

다음 그림은 현재 슬랩 캐시의 모든 슬랩 페이지들을 n->partial 리스트로 옮기고 정리하는 모습을 보여준다.

__kmem_cache_shrink-1

 

kick_all_cpus_sync()

kernel/smp.c

01/**
02 * kick_all_cpus_sync - Force all cpus out of idle
03 *
04 * Used to synchronize the update of pm_idle function pointer. It's
05 * called after the pointer is updated and returns after the dummy
06 * callback function has been executed on all cpus. The execution of
07 * the function can only happen on the remote cpus after they have
08 * left the idle function which had been called via pm_idle function
09 * pointer. So it's guaranteed that nothing uses the previous pointer
10 * anymore.
11 */
1void kick_all_cpus_sync(void)
2{
3        /* Make sure the change is visible before we kick the cpus */
4        smp_mb();
5        smp_call_function(do_nothing, NULL, 1);
6}
7EXPORT_SYMBOL_GPL(kick_all_cpus_sync);

IPI call을 사용하여 각 cpu를 호출하여 더미 callback을 수행시키게 한다.

  • pm_idle 함수 포인터의 update의 동기화를 하는데 사용된다.
  • do_nothing()은 비어 있는 함수이다.

 

참고

댓글 남기기