티스토리 뷰
개발/코드로 알아보는 ARM 리눅스 커널 TIL
20241123 4.7.6 슬랩 객체 할당 (p449) & 4.7.7 슬랩 객체 해제
clucle 2024. 11. 30. 15:044.7.6 슬랩 객체 할당 (p449)
4-137 p464 new_slab_objects
- node 의 partial list 를 cpu 캐시로 옮겨온다, 없는 경우 버디 시스템에서 할당 받는다.
- slub 에서 node 는 partial list 만 갖고 있고, cpu cache 에서는 freelist, page, partial 로 관리된다.
freelist = get_partial() // cpu 캐시로 노드의 partial list 에서 가져오기
if (freelist) return freelist;
page = new_slab(s, flags, node); // 없는 경우 버디시스템에서 할당
freelist = page->freelist // 첫 객체
return freelist
4-138 p467 get_partial
- 노드의 partial 리스트에서 page 를 cpu 캐시로 가져온다.
- 첫 slub 은 cpu_slab 의 page 와 free_list 로 올라가고, 나머지는 cpu_slab 의 partial 에 추가된다.
- cpu_slab->cpu_partial 의 절반을 초과하는 만큼 가져간다
// node 를 지정하지 않았다면 로컬 node 나 가장 가까운 node 사용
// 내 node 에 present_page 가 없다면 인접 노드에서 획득
node_to_mem_node() to determine the fallback node
static inline struct kmem_cache_node *get_node(struct kmem_cache *s, int node)
{
return s->node[node];
}
object = get_partial_node(s, get_node(s, searchnode), c, flags);
4-139 p468 get_partial_node
- node 의 partial 리스트에서 슬랩 페이지 가져오기
for (page = node->partial) { // node 의 partial 리스트를 순회
t = acquire_partial(...) // 4-141
}
// 처음 획득시에는 바로 kmem_cache_cpu 의 page 에 등록
c->page = page
// 이후는 kmem_cache_cpu 의 partial 리스트에 등록
put_cpu_partial(...) // 4-147
list_for_each_entry_safe(page, page2, &n->partial, lru) {
n->partial 이 가리키고 있는 리스트가 lru 를 가리키고 있어서 이 주소를 역으로 page 로 돌리는 코드로 보임
4-140 get_any_partial
리모트 노드에서 슬랩 페이지 획득
// 다른 노드에서 slab 페이지를 획득할 확률 지정.
// 0 인경우 무조건 로컬, 100인경우 대부분 리모트에서 획득 가능
remote_node_defrag_ratio
mempolicy_slab_node // NUMA policy 에 따라 사용 가능한 node 선택
node_zonelist // node 의 0번 zonelist 나 1번 zonelist 선택
zone 을 순회하면서 가능한 node 에 대해 get_partial_node 호출
cpuset 이 변경됐는지 확인
static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
{
smp_rmb();
return __read_seqcount_retry(s, start);
}
static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
{
return unlikely(s->sequence != start);
}
4-141 p471 acquire_slab
- get_partial_node 에서 호출했던 코드
- node 의 partial list 에서 슬랩 페이지를 제거하고 freelist 를 리턴
// page = node->partialist 를 순회하며 가져옴
t = acquire_slab(s, n, page, object == NULL, &objects);
// 처음 객체인 경우 모든 객체를 사용중으로 변경, 그렇지 않다면 freelist 만 설정
// 그리고 슬랩 페이지로 사용하게 되니 frozen 켜주기
freelist = page->freelist;
counters = page->counters;
new.counters = counters;
*objects = new.objects - new.inuse;
if (mode) {
new.inuse = page->objects;
new.freelist = NULL;
} else {
new.freelist = freelist;
}
VM_BUG_ON(new.frozen);
new.frozen = 1;
4.7.7 슬랩 객체 해제
4-142 p473 kmem_cache_free
4-143 p473 cache_from_obj
- kmem_cache_free 로 슬랩 객체 해제 요청
- pcpu 캐시의 page 로 돌리기 > pcpu 캐시의 partial list 에 추가 > node 의 partial 에 추가 > 버디 시스템에 추가 순으로 동작
cache_from_obj(struct kmem_cache *s, void *x){
page = virt_to_head_page -> 객체가 들어있던 첫 페이지
// page->slab_cache 가 s 와 같거나 루트페이지라면 리턴
cachep = page->slab_cache;
if (slab_equal_or_root(cachep, s))
return cachep;
// 그렇지 않다면 경고 출력후 인자로 받았던 s 리턴
return s;
}
// 슬랩 객체인 x 를 page 로 바꾸고 compound page 인경우 헤드 페이지를 리턴
// compound page 는 두개이상의 페이지를 하나의 페이지처럼 사용할 때 사용했었음
virt_to_head_page
4-144 p474 slab_free
slab_free(s, virt_to_head_page(x), x, NULL, 1, _RET_IP_);
static __always_inline void slab_free(
struct kmem_cache *s, struct page *page, void *head, void *tail, int cnt, unsigned long addr) {
slab_free_freelist_hook(s, head, tail); // 디버깅 옵션에 따라 후킹 함수를 호출 (slab 에 소멸자가 없긴할거같음, 소멸자와 다른걸로 보임)
if (likely(page == c->page)) { // fastpath 로 바로 cpucache->page 에 등록
freelist -> head ~ tail -> prev freelist
// 그렇지 않은 경우 slow path
__slab_free(s, page, head, tail_obj, cnt, addr);
}
4-145 p474 __slab_free 1/2
- slowpath 로 슬랩객체 해제
// page 가 cpu cache 의 page 가 아닌 상태에서 이 함수에 들어왔음
static void __slab_free(struct kmem_cache *s, struct page *page,
void *head, void *tail, int cnt,
unsigned long addr)
// 결국 인자로 들어온 페이지로 들어가게 된다.
// frozen 되지 않은 페이지는 kmem_cache 에서 관리하지 않는 슬랩 페이지 이거나 노드에서 관리하는 슬랩 페이지 이다.
prior : 이전의
// 다 쓴 페이지나, 하나도 안쓰는 페이지일때 옮겨갈 수 있는걸로 보임.
// 이미 frozen 이었던애는 신경 안써도 됨
if ((!new.inuse || !prior) && !was_frozen) {
// 1. s 가 percpu 캐시이고
// 2. freelist 에 객체가 없다면(?) -> 빈 객체가 없다 -> 그럼 사용중이라는것..?!
// pcpu cache 로 사용하면 되니까 frozen 시키자
if (kmem_cache_has_cpu_partial(s) && !prior) {
/*
* Slab was on no list before and will be
* partially empty
* We can defer the list move and instead
* freeze it.
*/
new.frozen = 1;
// 만약 pcpu 캐시가 아니거나, -> 얘는 frozen 안되는게 명확
// 빈객체가 있다면 -> 사용중인 객체가 있는 partial 상태..?
} else { /* Needs to be taken off a list */
n = get_node(s, page_to_nid(page));
/*
* Speculatively acquire the list_lock.
* If the cmpxchg does not succeed then we may
* drop the list_lock without any processing.
*
* Otherwise the list_lock will synchronize with
* other processors updating the list of slabs.
*/
spin_lock_irqsave(&n->list_lock, flags);
}
}
PageSlab
#define TESTPAGEFLAG(uname, lname, policy) \
static __always_inline int Page##uname(struct page *page) \
{ return test_bit(PG_##lname, &policy(page, 0)->flags); }
4-146 p477 __slab_free 2/2
- p479 그림을 잘 보자
if 옮겨가야할 노드가 없던 경우 {
!was_frozen && frozen -> cpu 캐시의 partial list 에 추가
put_cpu_partial(s, page, 1);
}
// 사용중인게 없는데, 노드의 partial list 로 못들어가는 경우
if (unlikely(!new.inuse && n->nr_partial >= s->min_partial))
goto slab_empty;
// cpu partial list 를 못쓰고, 빈 객체가 없던 경우. -> node 의 partial 에서 아예 빠져있었단 얘기인것 같다
if (!kmem_cache_has_cpu_partial(s) && unlikely(!prior)) {
// node 의 partial list 에 추가
slab_empty:
// 버디로 보내자
node 의 partial 리스트에 들어있었다면 제거
if (prior) {
/*
* Slab on the partial list.
*/
remove_partial(n, page);
stat(s, FREE_REMOVE_PARTIAL);
그게 아니었다면 full list 에 있었을거다?
} else {
/* Slab must be on the full list */
remove_full(s, n, page);
}
// 버디행
discard_slab(s, page);
static inline void remove_partial(struct kmem_cache_node *n,
struct page *page)
{
lockdep_assert_held(&n->list_lock);
list_del(&page->lru);
n->nr_partial--;
}
static void remove_full(struct kmem_cache *s, struct kmem_cache_node *n, struct page *page)
{
if (!(s->flags & SLAB_STORE_USER))
return;
lockdep_assert_held(&n->list_lock);
list_del(&page->lru);
}
4-147 p480 put_cpu_partial
put_cpu_partial(s, page, 1);
static void put_cpu_partial(struct kmem_cache *s, struct page *page, int drain)
struct page {
int pages; /* Nr of partial slabs left */
int pobjects; /* Approximate # of objects */
}
struct kmem_cache {
int cpu_partial; /* Number of per cpu partial objects to keep around */
}
// partial list 의 첫 페이지를 가져온다
oldpage = this_cpu_read(s->cpu_slab->partial);
if (oldpage) {
pobjects = oldpage->pobjects;
pages = oldpage->pages;
drain 옵션이 켜져있을 때, 빈객체의 수가 cpu_cache 에서 관리할 수 있는 객체의 수보다 커지면 ->cpu_cache 의 partial 을 모두 node 로
if (drain && pobjects > s->cpu_partial) {
unfreeze_partials(s, this_cpu_ptr(s->cpu_slab));
// partial list 의 다음 페이지는 next 에서 관리한다
page->next = oldpage;
add_partial
lockdep_assert_held -> dep: dependency : 이미 락이 잡혀있는지 디버깅때 확인
node partial 리스트의 page->lru head 또는 tail 에 추가
4-148 p483 remove_partial
node partial 리스트에서 제거
4-149, 4-150 p483 unfreeze_partials 1,2
- cpu 캐시의 partial list 에 있는 모든 page 를 node 의 partial list tail 에 추가. 만약 노드에서도 초과되면 버디로 돌려줌
c->partial 리스트를 순회 {
page 의 node 를 획득하고 node 의 list 에 lock
frozen 만 0으로 변경 -> 노드로 들어가기때문
// 만약 inuse 가 0인데 n->nr_partial >= s->min_partial 이면 discard_page 로 리스트를 관리
// 그게 아니라면 노드로 들어감
if (unlikely(!new.inuse && n->nr_partial >= s->min_partial)) {
page->next = discard_page;
discard_page = page;
} else {
add_partial(n, page, DEACTIVATE_TO_TAIL);
stat(s, FREE_ADD_PARTIAL);
}
}
여기서 discard 된 애들이 있다면 버디로 돌아감
'개발 > 코드로 알아보는 ARM 리눅스 커널 TIL' 카테고리의 다른 글
20241207 4.9.2 per-cpu (p516) first_chunk 할당 과정 (0) | 2024.12.07 |
---|---|
20241130 4.8 kmalloc 과 vmalloc (p487) ~ 4.9 per-cpu 할당자 (0) | 2024.12.07 |
20241116 4.7.5슬랩 페이지와 슬랩 객체 할당 (p442) (1) | 2024.11.21 |
20241109 4.7.4 kmem 캐시 생성 (p426) (0) | 2024.11.21 |
241102 메모리 관리 (p394) & 4.7 슬랩 할당자 (p410) (2) | 2024.11.21 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- RVO
- 카카오
- shared_from_this
- vr핏
- cockroach db
- 에러 위치 찾기
- 봄날에 스케치
- Visual Studio
- Golang
- 클래스 맴버 변수 출력하기
- C++
- Reciprocal n-body Collision Avoidance
- 면접
- it's called a vrpit
- hole-punching
- SuffixArray
- boost
- mysql
- 우리는 vr핏이라고 부릅니다
- 잘못된 빨간줄
- chrome-extension
- ad skip
- 코어 남기기
- Quest2
- print shared_ptr class member variable
- red underline
- vrpit
- set value
- 영상 픽셀화 하기
- Obstacle Avoidance
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함