cgroup_init_early()

<kernel v5.0>

Control Group의 초기화는 두 단계에 걸쳐 수행된다.

  • 부트업 초반에 cgroup_init_early()를 통해 초기화
  • 부트업 후반에 cgroup_init()을 통해 초기화

 

다음 그림은 cgroup_init_early() 이하 함수와의 호출 관계를 보여준다.

 

cgroup_init_early()

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_sb_opts __initdata opts;
        struct cgroup_subsys *ss;
        int i;

        init_cgroup_root(&cgrp_dfl_root, &opts);
        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;
}

부트업 초반에 cgroup을 early 초기화하고 earlyinit 설정이 된 서브시스템들을 초기화한다. (cgroup의 정규 초기화함수는 커널 부트업 마지막 부분에서 호출되는 cgroup_init()이다.)

  • 코드 라인 3에서 __initdata는 __section(.init.data)을 의미한다.
  • 코드 라인 7에서 cgroup 루트를 초기화한다.
  • 코드 라인 9에서 static하게 초기 설정된 init_css_set RCU 변수의 주소를 init_task.cgroups로 저장한다.
    • RCU : Read Copy Update
      • 커널 락을 없애려는 시도로 도입된 동기화 기법으로 read 요청이 많은 lock 구현에서 사용된다.
  • 코드 라인 11~26에서 모든 cgroup 서브시스템들을 대상으로 earlyinit이 설정된 서브시스템을 초기화한다.

 

다음 그림은 3개의 cgroup 서브시스템이 early 초기화된 후의 모습을 보여준다.

 

init_cgroup_root()

kernel/cgroup/cgroup.c

void init_cgroup_root(struct cgroup_root *root, struct cgroup_sb_opts *opts)
{
        struct cgroup *cgrp = &root->cgrp;

        INIT_LIST_HEAD(&root->root_list);
        atomic_set(&root->nr_cgrps, 1);
        cgrp->root = root;
        init_cgroup_housekeeping(cgrp);
        idr_init(&root->cgroup_idr);

        root->flags = opts->flags;
        if (opts->release_agent)
                strscpy(root->release_agent_path, opts->release_agent, PATH_MAX);
        if (opts->name)
                strscpy(root->name, opts->name, MAX_CGROUP_ROOT_NAMELEN);
        if (opts->cpuset_clone_children)
                set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
}
cgroup 루트를 초기화한다.
  • 코드 라인 5~7에서 루트 리스트를 초기화하고 cgroup 수를 1로 한다.
  • 코드 라인 cgrp과 모든 서브시스템에 있는 e_csets[] 리스트를 깨끗하게 초기화한다.
  • 코드 라인 cgroup에 생성 시마다 id를 발급하기 위해 idr 초기화를 한다.
    • IDR은 radix tree의 일종으로 정수 ID와 특정한 포인터 값을 연결시키는 역할을 해 준다.
    • 원래는 POSIX timer 관련 시스템 콜 구현을 위해 작성된 것으로 특정한 timer 객체를 다룰 수 있는 ID를 생성해 주는 역할을 하였으나 현재는 각종 장치 드라이버나 VFS 레이어에서도 널리 사용된다.

 

다음 그림은 디폴트 루트 그룹의 초기화된 모습을 간략히 보여준다.

 

init_cgroup_housekeeping()

kernel/cgroup/cgroup.c

static void init_cgroup_housekeeping(struct cgroup *cgrp)
{
        struct cgroup_subsys *ss;
        int ssid;

        INIT_LIST_HEAD(&cgrp->self.sibling);
        INIT_LIST_HEAD(&cgrp->self.children);
        INIT_LIST_HEAD(&cgrp->cset_links);
        INIT_LIST_HEAD(&cgrp->pidlists);
        mutex_init(&cgrp->pidlist_mutex);
        cgrp->self.cgroup = cgrp;
        cgrp->self.flags |= CSS_ONLINE;
        cgrp->dom_cgrp = cgrp;
        cgrp->max_descendants = INT_MAX;
        cgrp->max_depth = INT_MAX;
        INIT_LIST_HEAD(&cgrp->rstat_css_list);
        prev_cputime_init(&cgrp->prev_cputime);

        for_each_subsys(ss, ssid)
                INIT_LIST_HEAD(&cgrp->e_csets[ssid]);

        init_waitqueue_head(&cgrp->offline_waitq);
        INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent);
}
요청한 cgroup과 모든 서브시스템에 있는 e_csets[] 리스트를 깨끗하게 초기화한다.

 

__init cgroup_init_subsys()

kernel/cgroup/cgroup.c

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);

        mutex_lock(&cgroup_mutex);

        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(cgroup_css(&cgrp_dfl_root.cgrp, ss));
        /* 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_free_callback |= (bool)ss->free << 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));

        mutex_unlock(&cgroup_mutex);
}

요청한 서브시스템을 초기화한다.

  • 코드 라인 13~17에서 cgroup 서브시스템의 루트를 생성하고, 초기화한 후 연결을 구성한다.
  • 코드 라인 23~31에서 cgroup 서브시스템들의  루트는 절대 소멸하지 않는다. 따라서 참조 카운터 관리가 필요하지 않는다. 가장 처음 부트업 타임에 early하게 생성된 루트들은 id 값으로 1을 할당받고, 그렇지 않은 경우는 cgroup 각 서브시스템의 idr을 통해 id를 할당받는다.
  • 코드 라인 37에서 init_css_set에 subsys[] 배열 중 요청한 서브시스템에 해당하는 배열 인덱스 항목에 루트 css가 등록된다.
    • css
      • struct cgroup_subsys_state
      • 서브시스템별/cgroup 마다 생성된다.
  • 코드 라인 39~42에서 cgroup에 태스크가 fork/exit/free/canfork 된 후 서브시스템의 관련 후크 함수를 호출할 지 여부를 결정하는 비트마스크이다. 이 곳에 요청한 서브시스템에 해당하는 플래그를 설정해둔다.
  • 코드 라인 47에서 cgroup이 early 초기화되는 이 시점에서 init_task 아래에 하위 태스크가 존재하지 않아야 한다.

 

예) 리눅스 부트와 함께 early 초기화되는 서브시스템들

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct

 

참고

 

댓글 남기기