[커널 19차] 99 주차
2024.03.30 17:56
kmem_cache_destroy()
슬랩 캐시 자체를 삭제해주는 함수다.
void kmem_cache_destroy(struct kmem_cache *s)
{
int err = -EBUSY;
bool rcu_set;
if (unlikely(!s) || !kasan_check_byte(s))
return;
- kmem_cache가 NULL인 경우 리턴한다.
cpus_read_lock();
mutex_lock(&slab_mutex);
rcu_set = s->flags & SLAB_TYPESAFE_BY_RCU;
s->refcount--;
if (s->refcount)
goto out_unlock;
err = shutdown_cache(s);
WARN(err, "%s %s: Slab cache still has objects when called from %pS",
__func__, s->name, (void *)_RET_IP_);
- CPU read lock을 획득하는 이유는 모르겠다.
- 슬랩 캐시들을 리스트로 가지고 있는 slab_caches에서 인자로 넘어온 캐시를 제거해야 됨으로 slab_mutext를 확보한다.
- 슬랩 캐시 merge와 같이 여러 군데에서 슬랩 캐시를 사용하고 있는 경우가 있으므로 refconunt가 0인 경우만 detroy를 진행한다.
- shutdown_cache()에서 슬랩 캐시를 삭제한다.
out_unlock:
mutex_unlock(&slab_mutex);
cpus_read_unlock();
if (!err && !rcu_set)
kmem_cache_release(s);
}
EXPORT_SYMBOL(kmem_cache_destroy);
- sysfs에 만들었던 파일과 링크들을 kmem_cache_release()통해 삭제한다.
shutdown_cache()
static int shutdown_cache(struct kmem_cache *s)
{
/* free asan quarantined objects */
kasan_cache_shutdown(s);
if (__kmem_cache_shutdown(s) != 0)
return -EBUSY;
list_del(&s->list);
if (s->flags & SLAB_TYPESAFE_BY_RCU) {
list_add_tail(&s->list, &slab_caches_to_rcu_destroy);
schedule_work(&slab_caches_to_rcu_destroy_work);
} else {
kfence_shutdown_cache(s);
debugfs_slab_release(s);
}
return 0;
}
- __kmem_cache_shutdown()에서 슬랩 캐시를 삭제한다. 만약 아직 할당 해제되지 않은 객체들이 있어서 캐시 삭제에 실패한 경우 EBUSY를 리턴해서 삭제를 중단한다.
- 캐시 삭제에 성공한 경우 slab_caches 리스트에서 해당 캐시를 삭제한다.
/*
* Release all resources used by a slab cache.
*/
int __kmem_cache_shutdown(struct kmem_cache *s)
{
int node;
struct kmem_cache_node *n;
flush_all_cpus_locked(s);
/* Attempt to free all objects */
for_each_kmem_cache_node(s, node, n) {
free_partial(s, n);
if (n->nr_partial || node_nr_slabs(n))
return 1;
}
return 0;
}
- 모든 CPU에 있는 CPU 슬랩과 CPU partial에 있는 슬랩들을 노드로 보내거나 버디로 삭제한다.
- 모든 노드에 있는 슬랩을 버디로 돌려보낸다.
- 만약 삭제되지 않은 슬랩이 있는 경우 캐시 삭제에 실패한다.
flush_all_cpus_locked()
static DEFINE_MUTEX(flush_lock);
static DEFINE_PER_CPU(struct slub_flush_work, slub_flush);
static void flush_all_cpus_locked(struct kmem_cache *s)
{
struct slub_flush_work *sfw;
unsigned int cpu;
lockdep_assert_cpus_held();
mutex_lock(&flush_lock);
for_each_online_cpu(cpu) {
sfw = &per_cpu(slub_flush, cpu);
if (!has_cpu_slab(cpu, s)) {
sfw->skip = true;
continue;
}
INIT_WORK(&sfw->work, flush_cpu_slab);
sfw->skip = false;
sfw->s = s;
queue_work_on(cpu, flushwq, &sfw->work);
}
- 각 CPU에 있는 CPU 슬랩과, partial 슬랩들을 직접 삭제하지 않고 각 CPU의 워크큐를 통해 삭제한다.
- CPU에 있는 slub_flush를 초기화하고 실행해야하므로 flush_lock을 잡는다.
- 각 CPU를 순회하면서 slub_flush를 가지고 오고, work 함수를 flush_cpu_slab로 초기화한다.
- queue_work_on을 통해 워크큐에 작업을 넣는다. 이렇게하면서 해당하는 CPU에서 해당 잡을 알아서 실행한다.
- 만약 CPU에 슬랩과 partial 슬랩이 아무것도 없는 경우는 스킵한다.
for_each_online_cpu(cpu) {
sfw = &per_cpu(slub_flush, cpu);
if (sfw->skip)
continue;
flush_work(&sfw->work);
}
mutex_unlock(&flush_lock);
}
- flush_work()를 통해 워크큐에 보냈던 잡이 다 끝날 때까지 기다린다.
flush_cpu_slab()
/*
* Flush cpu slab.
*
* Called from CPU work handler with migration disabled.
*/
static void flush_cpu_slab(struct work_struct *w)
{
struct kmem_cache *s;
struct kmem_cache_cpu *c;
struct slub_flush_work *sfw;
sfw = container_of(w, struct slub_flush_work, work);
s = sfw->s;
c = this_cpu_ptr(s->cpu_slab);
if (c->slab)
flush_slab(s, c);
unfreeze_partials(s);
}
- 워크큐에 의해서 각 CPU에서 실행되는 함수로 CPU 슬랩은 flush_slab() 통해 CPU partial은 unfreeze_partials() 통해 슬랩들을 노드로 보내거나 삭제한다.
flush_slab()
static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
{
unsigned long flags;
struct slab *slab;
void *freelist;
local_lock_irqsave(&s->cpu_slab->lock, flags);
slab = c->slab;
freelist = c->freelist;
c->slab = NULL;
c->freelist = NULL;
c->tid = next_tid(c->tid);
local_unlock_irqrestore(&s->cpu_slab->lock, flags);
if (slab) {
deactivate_slab(s, slab, freelist);
stat(s, CPUSLAB_FLUSH);
}
}
- CPU 슬랩과 kmem_cache_cpu와의 관계를 끊고 CPU 슬랩을 deactivate_slab()을 통해 노드로 보내거나 삭제한다.
unfreeze_partials()
/*
* Unfreeze all the cpu partial slabs.
*/
static void unfreeze_partials(struct kmem_cache *s)
{
struct slab *partial_slab;
unsigned long flags;
local_lock_irqsave(&s->cpu_slab->lock, flags);
partial_slab = this_cpu_read(s->cpu_slab->partial);
this_cpu_write(s->cpu_slab->partial, NULL);
local_unlock_irqrestore(&s->cpu_slab->lock, flags);
if (partial_slab)
__unfreeze_partials(s, partial_slab);
}
- kmem_cache_cpu 구조체의 partail을 NULL로 바꿔 partial 리스트와의 관계를 끊는다.
- __unfreeze_partials()통해 partial 리스트에 있는 슬랩을 삭제하거나 노드로 보낸다.
__unfreeze_partials()
static void __unfreeze_partials(struct kmem_cache *s, struct slab *partial_slab)
{
struct kmem_cache_node *n = NULL, *n2 = NULL;
struct slab *slab, *slab_to_discard = NULL;
unsigned long flags = 0;
while (partial_slab) {
struct slab new;
struct slab old;
slab = partial_slab;
partial_slab = slab->next;
- partial 리스트인 partial_slab을 하나씩 순회한다.
n2 = get_node(s, slab_nid(slab));
if (n != n2) {
if (n)
spin_unlock_irqrestore(&n->list_lock, flags);
n = n2;
spin_lock_irqsave(&n->list_lock, flags);
}
- CPU partial 리스트에 있는 slab은 remote 할당 경우와 같이 다른 노드에서 슬랩을 할당받아 왔을 경우가 있는데, 그런 경우는 해당 노드의 lock을 획득한다.
do {
old.freelist = slab->freelist;
old.counters = slab->counters;
VM_BUG_ON(!old.frozen);
new.counters = old.counters;
new.freelist = old.freelist;
new.frozen = 0;
} while (!__slab_update_freelist(s, slab,
old.freelist, old.counters,
new.freelist, new.counters,
"unfreezing slab"));
- 현재 슬랩의 frozen을 0으로 해준다.
- 중간에 객체를 해제하는 경우 등 cmpxchg가 실패하는 경우가 있으므로 성공할 때까지 시도한다.
if (unlikely(!new.inuse && n->nr_partial >= s->min_partial)) {
slab->next = slab_to_discard;
slab_to_discard = slab;
} else {
add_partial(n, slab, DEACTIVATE_TO_TAIL);
stat(s, FREE_ADD_PARTIAL);
}
}
- 현재 슬랩이 empty 상태이고 노드의 최소 슬랩 유지 개수를 넘었다면 discard 리스트에 삽입한다.
- 그게 아니라면, 슬랩을 노드의 partial에 추가한다.
if (n)
spin_unlock_irqrestore(&n->list_lock, flags);
while (slab_to_discard) {
slab = slab_to_discard;
slab_to_discard = slab_to_discard->next;
stat(s, DEACTIVATE_EMPTY);
discard_slab(s, slab);
stat(s, FREE_SLAB);
}
}
- discard 리스트에 있는 슬랩들을 discard_slab()을 통해 버디로 삭제한다.
free_partial()
/*
* Attempt to free all partial slabs on a node.
* This is called from __kmem_cache_shutdown(). We must take list_lock
* because sysfs file might still access partial list after the shutdowning.
*/
static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
{
LIST_HEAD(discard);
struct slab *slab, *h;
BUG_ON(irqs_disabled());
spin_lock_irq(&n->list_lock);
list_for_each_entry_safe(slab, h, &n->partial, slab_list) {
if (!slab->inuse) {
remove_partial(n, slab);
list_add(&slab->slab_list, &discard);
} else {
list_slab_objects(s, slab,
"Objects remaining in %s on __kmem_cache_shutdown()");
}
}
spin_unlock_irq(&n->list_lock);
list_for_each_entry_safe(slab, h, &discard, slab_list)
discard_slab(s, slab);
}
- 노드의 partial 리스트를 순회하면서
- 슬랩이 empty 상태일 경우 슬랩을 discard 리스트에 추가한다.
- 슬랩에 아직 할당된 객체가 남아있는 경우 list_slab_objects()을 통해 할당된 객체의 정보를 출력한다.
- discard 리스트에 등록된 슬랩들을 discard_slab()을 통해 버디로 삭제한다.
list_slab_objects()
static void list_slab_objects(struct kmem_cache *s, struct slab *slab,
const char *text)
{
#ifdef CONFIG_SLUB_DEBUG
void *addr = slab_address(slab);
void *p;
slab_err(s, slab, text, s->name);
spin_lock(&object_map_lock);
__fill_map(object_map, s, slab);
for_each_object(p, s, addr, slab->objects) {
if (!test_bit(__obj_to_index(s, addr, p), object_map)) {
pr_err("Object 0x%p @offset=%tu\\n", p, p - addr);
print_tracking(s, p);
}
}
spin_unlock(&object_map_lock);
#endif
}
- __fill_map을 통해 현재 슬랩의 freelist에 있는 객체의 인덱스를 알아내서 map에 있는 해당 인덱스를 1로 세팅한다.
- 슬랩에 속한 모든 객체를 순회하면서 map에 1로 체크되어 있지 않는 객체에 대해서는 에러 메세지를 출력해준다.
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
공지 | [공지] 스터디 정리 노트 공간입니다. | woos | 2016.05.14 | 627 |
248 | [커널 19차] 103 주차 | Min | 2024.04.28 | 10 |
247 | [커널 20차] 48주차 | 무한질주 | 2024.04.25 | 25 |
246 | [커널 19차] 102 주차 | Min | 2024.04.20 | 40 |
245 | [커널 19차] 101 주차 | Min | 2024.04.13 | 63 |
244 | [커널 19차] 100 주차 | Min | 2024.04.13 | 16 |
» | [커널 19차] 99 주차 | Min | 2024.03.30 | 82 |
242 | [커널 19차] 98 주차 | Min | 2024.03.23 | 55 |
241 | [커널 19차] 97 주차 | Min | 2024.03.16 | 50 |
240 | [커널 19차] 96 주차 | Min | 2024.03.14 | 32 |
239 | [커널 19차] 95 주차 [2] | Min | 2024.03.03 | 111 |
238 | [커널 20차] 32주차 | brw | 2023.12.16 | 387 |
237 | [커널 20차] 29주차 | brw | 2023.11.27 | 161 |
236 | [커널 20차] 27주차 | brw | 2023.11.21 | 86 |
235 | [커널 20차] 26주차 | brw | 2023.11.21 | 49 |
234 | [커널 20차] 28주차 | 이민찬 | 2023.11.19 | 64 |
233 | [커널 20차] 25주차 | 이민찬 | 2023.10.30 | 120 |
232 | [커널 20차] 24주차 | 이민찬 | 2023.10.22 | 752 |
231 | [커널 20차] 23주차 | 이민찬 | 2023.10.14 | 81 |
230 | [커널 20차] 22주차 | 이민찬 | 2023.10.08 | 76 |
229 | [커널 20차] 21주차 | 이민찬 | 2023.09.23 | 116 |
.