Start_Kernel (Week 3)
Start_Kernel (Week 3)

Start_Kernel (Week 3)

카테고리
⚙️ Start Kernel
작성자
박용성박용성
작성일
2024년 06월 02일
태그
C
Linux
Slug
start-kernel-3
 

init_vmlinux_build_id()

💡
커널 이미지의 빌드 ID를 초기화

  • 커널 빌드 ID는 커널 이미지를 구별하는 데 사용됨, 커널이 컴파일될 때 생성
  • 빌드 ID는 해당 이미지를 빌드한 컴퓨터 환경과 관련된 정보를 담고 있음
    • 디버깅, 크래시 리포트 및 분석, 보안 검사 등등에 사용됨
 
  • ELF
    • Exectuable and Linkable Format 파일. 빌드 ID가 이 파일의 노트 섹션에 저장됨
 

정의 : lib/buildid.c

#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_CRASH_CORE) unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init; /** * init_vmlinux_build_id - Compute and stash the running kernel's build ID */ void __init init_vmlinux_build_id(void) { extern const void __start_notes __weak; // 정의되지 않으면 NULL extern const void __stop_notes __weak; // 정의되지 않으면 NULL unsigned int size = &__stop_notes - &__start_notes; build_id_parse_buf(&__start_notes, vmlinux_build_id, size); } #endif
  • CONFIG_STACKRACE_BUILD_ID : 스택 트레이스(함수 호출 시퀀스)를 수집할 때 빌드 ID도 함께 수집
  • CONFIG_CRASH_CORE : 커널 크래시 발생했을 때 덤프 생성 (크래시가 발생한 지점의 메모리 내용, 레지스터 상태, 프로세스 정보)
 
두 옵션이 모두 활성화되어 있다면 init_vmlinux_build_id 가 실행된다.
→ 디버깅 및 런타임 정보가 저장된 포인터를 불러와 build_id_parse_buf 에 넘겨준다.
 

Depth 1 : build_id_parse_buf

/** * build_id_parse_buf - Get build ID from a buffer * @buf: ELF note section(s) to parse * @buf_size: Size of @buf in bytes * @build_id: Build ID parsed from @buf, at least BUILD_ID_SIZE_MAX long * * Return: 0 on success, -EINVAL otherwise */ int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size) { return parse_build_id_buf(build_id, NULL, buf, buf_size); }
  • 버퍼에서 build ID를 추출해 성공하면 0 , 아니라면 -EINVAL을 반환함
 

Depth 2 : parse_build_id_buf

/* * Parse build id from the note segment. This logic can be shared between * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are * identical. */ static int parse_build_id_buf(unsigned char *build_id, __u32 *size, const void *note_start, Elf32_Word note_size) { Elf32_Word note_offs = 0, new_offs; while (note_offs + sizeof(Elf32_Nhdr) < note_size) { Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs); if (nhdr->n_type == BUILD_ID && nhdr->n_namesz == sizeof("GNU") && !strcmp((char *)(nhdr + 1), "GNU") && nhdr->n_descsz > 0 && nhdr->n_descsz <= BUILD_ID_SIZE_MAX) { memcpy(build_id, note_start + note_offs + ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr), nhdr->n_descsz); memset(build_id + nhdr->n_descsz, 0, BUILD_ID_SIZE_MAX - nhdr->n_descsz); if (size) *size = nhdr->n_descsz; return 0; } new_offs = note_offs + sizeof(Elf32_Nhdr) + ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4); if (new_offs <= note_offs) /* overflow */ break; note_offs = new_offs; } return -EINVAL; }
  • unsigned char *build_id : build ID 저장될 배열
  • __u32 *size : build ID의 크기 저장할 포인터 (__u32 : 부호없는 32비트 정수형)
  • const void *note_start : ELF 노트 섹션의 시작점
  • Elf32_Word note_size : ELF 노트 섹션 크기
 
  1. note_offs 변수를 활용해 노트 섹션을 반복적으로 탐색
    1. 현재 노트 유형이 BUILD_ID 와 같은지 확인
    2. 노트 이름이 "GNU" 인지 확인
    3. 노트 크기가 0보다 큰지 확인
    4. 노트 크기가 BUILD_ID_SIZE_MAX 이하인지 확인
  1. 조건에 맞다면 해당 빌드 ID를 build_id 배열에 복사
 

cgroup_init_early()


💡
cgroup(제어 그룹) 관련 초기화를 수행 (Control Group)
 

정의 : kernel/cgroup/cgroup.c

/** * cgroup_init_early - cgroup initialization at system boot * * Initialize cgroups at system boot, and initialize any * subsystems that request early init. */ int __init cgroup_init_early(void) { static struct cgroup_fs_context __initdata ctx; struct cgroup_subsys *ss; int i; ctx.root = &cgrp_dfl_root; init_cgroup_root(&ctx); cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF; RCU_INIT_POINTER(init_task.cgroups, &init_css_set); for_each_subsys(ss, i) { WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id, "invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p id:name=%d:%s\n", i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free, ss->id, ss->name); WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN, "cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]); ss->id = i; ss->name = cgroup_subsys_name[i]; if (!ss->legacy_name) ss->legacy_name = cgroup_subsys_name[i]; if (ss->early_init) cgroup_init_subsys(ss, true); } return 0; }
  1. static struct cgroup_fs_context __initdata ctx 를 선언해 제어 그룹 파일 시스템의 context를 초기화. 제어 그룹의 초기 설정 정보를 담고 있음
  1. ctx.root = &cgrp_dfl_root; : 컨트롤 그룹의 기본 루트 설정
  1. 를 호출해 제어 그룹 루트 초기화
  1. RCU_INIT_POINTER(init_task.cgroups, &init_css_set); : 초기 프로세스가 속한 제어 그룹을 설정
    1. /** * RCU_INIT_POINTER() - initialize an RCU protected pointer * @p: The pointer to be initialized. * @v: The value to initialized the pointer to. * * Initialize an RCU-protected pointer in special cases where readers * do not need ordering constraints on the CPU or the compiler. These * special cases are: * * 1. This use of RCU_INIT_POINTER() is NULLing out the pointer *or* * 2. The caller has taken whatever steps are required to prevent * RCU readers from concurrently accessing this pointer *or* * 3. The referenced data structure has already been exposed to * readers either at compile time or via rcu_assign_pointer() *and* * * a. You have not made *any* reader-visible changes to * this structure since then *or* * b. It is OK for readers accessing this structure from its * new location to see the old state of the structure. (For * example, the changes were to statistical counters or to * other state where exact synchronization is not required.) * * Failure to follow these rules governing use of RCU_INIT_POINTER() will * result in impossible-to-diagnose memory corruption. As in the structures * will look OK in crash dumps, but any concurrent RCU readers might * see pre-initialized values of the referenced data structure. So * please be very careful how you use RCU_INIT_POINTER()!!! * * If you are creating an RCU-protected linked structure that is accessed * by a single external-to-structure RCU-protected pointer, then you may * use RCU_INIT_POINTER() to initialize the internal RCU-protected * pointers, but you must use rcu_assign_pointer() to initialize the * external-to-structure pointer *after* you have completely initialized * the reader-accessible portions of the linked structure. * * Note that unlike rcu_assign_pointer(), RCU_INIT_POINTER() provides no * ordering guarantees for either the CPU or the compiler. */ #define RCU_INIT_POINTER(p, v) \ do { \ rcu_check_sparse(p, __rcu); \ WRITE_ONCE(p, RCU_INITIALIZER(v)); \ } while (0)
       
      → 작동 원리는 잘 모르겠지만, 다른 스레드에 영향을 주지 않고 안전하게 업데이트 할 수 있는 동기성 제어 기법이다.
      notion image
  1. for_each_subsys 루프를 통해 각 제어 그룹 하위 시스템 초기화 작업을 수행
 

Depth 1-1 : init_cgroup_root

void init_cgroup_root(struct cgroup_fs_context *ctx) { struct cgroup_root *root = ctx->root; struct cgroup *cgrp = &root->cgrp; INIT_LIST_HEAD_RCU(&root->root_list); atomic_set(&root->nr_cgrps, 1); cgrp->root = root; init_cgroup_housekeeping(cgrp); /* DYNMODS must be modified through cgroup_favor_dynmods() */ root->flags = ctx->flags & ~CGRP_ROOT_FAVOR_DYNMODS; if (ctx->release_agent) strscpy(root->release_agent_path, ctx->release_agent, PATH_MAX); if (ctx->name) strscpy(root->name, ctx->name, MAX_CGROUP_ROOT_NAMELEN); if (ctx->cpuset_clone_children) set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags); }
  1. 전달받은 ctx , 컨트롤 그룹을 사용해 *root 초기화
  1. INIT_LIST_HEAD_RCU 로 루트 리스트의 헤드 초기화
    1. 이 리스트는 컨트롤 그룹 트리 내의 모든 루트를 관리하는 데 사용됨
  1. atomic_set 으로 루트의 속한 제어 그룹의 수를 1로 설정
 

Depth 1-2 : cgroup_init_subsys

static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early) { struct cgroup_subsys_state *css; pr_debug("Initializing cgroup subsys %s\n", ss->name); cgroup_lock(); idr_init(&ss->css_idr); INIT_LIST_HEAD(&ss->cfts); /* Create the root cgroup state for this subsystem */ ss->root = &cgrp_dfl_root; css = ss->css_alloc(NULL); /* We don't handle early failures gracefully */ BUG_ON(IS_ERR(css)); init_and_link_css(css, ss, &cgrp_dfl_root.cgrp); /* * Root csses are never destroyed and we can't initialize * percpu_ref during early init. Disable refcnting. */ css->flags |= CSS_NO_REF; if (early) { /* allocation can't be done safely during early init */ css->id = 1; } else { css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); BUG_ON(css->id < 0); } /* Update the init_css_set to contain a subsys * pointer to this state - since the subsystem is * newly registered, all tasks and hence the * init_css_set is in the subsystem's root cgroup. */ init_css_set.subsys[ss->id] = css; have_fork_callback |= (bool)ss->fork << ss->id; have_exit_callback |= (bool)ss->exit << ss->id; have_release_callback |= (bool)ss->release << ss->id; have_canfork_callback |= (bool)ss->can_fork << ss->id; /* At system boot, before all subsystems have been * registered, no tasks have been forked, so we don't * need to invoke fork callbacks here. */ BUG_ON(!list_empty(&init_task.tasks)); BUG_ON(online_css(css)); cgroup_unlock(); }
  1. 디버깅을 위해 서브시스템 이름 로그로 출력
  1. Lock
    1. https://elixir.bootlin.com/linux/v6.8.1/source/include/linux/cgroup.h#L366 static inline void cgroup_lock(void) { mutex_lock(&cgroup_mutex); }
  1. idr_init 호출해 서브시스템의 css_idr 초기화
    1. 서브시스템 상태를 저장하기 위한 IDR(ID Register) 초기화하는 작업
  1. INIT_LIST_HEAD 로 cgroup 파일 타입 연결리스트 초기화
 
 

local_irq_disable


💡
현재 CPU의 인터럽트를 비활성화 인터럽트 처리 도중 재진입을 방지하고자 할 때 사용
 

정의 : include/linux/irqflags.h

#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
 

Depth 1 : raw_local_irq_disable

#define raw_local_irq_disable() arch_local_irq_disable()
 

Depth 2 : arch_local_irq_disable

static inline void arch_local_irq_disable(void) { #ifdef CONFIG_COLDFIRE asm volatile ( "move %/sr,%%d0 \n\t" "ori.l #0x0700,%%d0 \n\t" "move %%d0,%/sr \n" : /* no outputs */ : : "cc", "%d0", "memory"); #else asm volatile ("oriw #0x0700,%%sr" : : : "memory"); #endif }
 
 

댓글

guest