cgroup_init_early()

소스 분석

  • CGroup을 초기화하고 earlyinit 설정이 된 서브시스템들도 초기화한다.

cgroup_init_early

 

cgroup_init_early()

/**
 * 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 name:id=%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->early_init)
                        cgroup_init_subsys(ss, true);
        }
        return 0;
}
  • __initdata
    • __section(.init.data)
  • RCU_INIT_POINTER(init_task.cgroups, &init_css_set);
    • init_css_set RCU 변수를 static하게 초기화하고 그 포인터를 init_task.cgroups로 저장한다.
    • RCU : Read Copy Update
      • 커널 락을 없애려는 시도로 도입된 동기화 기법으로 read 요청이 많은 lock 구현에서 사용된다.
  • init_cgroup_root(&cgrp_dfl_root, &opts);
    • cgroup을 초기화한다.
  • cgroup_init_subsys(ss, true);
    • earlyinit이 설정된 경우엔 해당 서브시스템을 미리 초기화한다.

 

init_cgroup_root()

static 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)
                strcpy(root->release_agent_path, opts->release_agent);
        if (opts->name)
                strcpy(root->name, opts->name);
        if (opts->cpuset_clone_children)
                set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
}
  • INIT_LIST_HEAD(&root->root_list);
    • 루트 리스트를 초기화한다.
  • atomic_set(&root->nr_cgrps, 1);
    • root->nr_cgrps에 1을 atomic operation을 이용하여 저장한다.
    • cgroup 수를 1로 한다.
  • init_cgroup_housekeeping(cgrp);
    • cgrp과 모든 서브시스템에 있는 e_csets[] 리스트를 깨끗하게 초기화한다.
  • idr_init(&root->cgroup_idr);
    • IDR은 radix tree의 일종으로 정수 ID와 특정한 포인터 값을 연결시키는 역할을 해 준다.
    • 원래는 POSIX timer 관련 시스템 콜 구현을 위해 작성된 것으로 특정한 timer 객체를 다룰 수 있는 ID를 생성해 주는 역할을 하였으나 현재는 각종 장치 드라이버나 VFS 레이어에서도 널리 사용된다.
    • 참조: [Linux] IDR & IDA – integer ID 관리 | F/OSS

 

init_cgroup_housekeeping()

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;

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

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

 

idr_init()

/**
 * idr_init - initialize idr handle
 * @idp:        idr handle
 *
 * This function is use to set up the handle (@idp) that you will pass
 * to the rest of the functions.
 */
void idr_init(struct idr *idp)
{
        memset(idp, 0, sizeof(struct idr));
        spin_lock_init(&idp->lock);
}
EXPORT_SYMBOL(idr_init);
  • 인수로 지정된 idr 구조체를 깨끗하게 지운후 spin lock 변수를 초기화한다.
    • spin lock의 초기 값은 1(unlock)이다.

 

RCU_INIT_POINTER() 매크로

  • RCU 포인터영역을 Sparse 체크하고 RCU 변수 v를 초기화한다.
#define RCU_INIT_POINTER(p, v) \
        do { \
                rcu_dereference_sparse(p, __rcu); \
                p = RCU_INITIALIZER(v); \
        } while (0)
  • sparse:
    • 리눅스 커널의 문제를 찾아주는 툴
    • 스파스는 정적 분석 도구이고, 설치된 후 gcc extension으로 동작
    • 지원 속성으로 noderef, address_space, lock(acquires, releases), …
  • __rcu:
    • __attribute__((noderef, address_space(4)))
    • noderef:
      • 포인터 변수를 사용하여 직접 참조할 수 없다.
      • & 연산자를 사용해서 직접 참조해야 한다
    • address_space:
      • 커널에는 몇개의 주소공간이 있다.
      • 0 : kernel, 1: user, 2: iomem, 3: percpu, 4: __rcu 공간

 

__init cgroup_init_subsys()

static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
{
        struct cgroup_subsys_state *css;

        printk(KERN_INFO "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;

        need_forkexit_callback |= ss->fork || ss->exit;

        /* 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);
}

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.