tick_init()

 

커널 v2.6.21에서 tickless라는 개념이 도입 되기전 커널에서는 각 cpu의 스케쥴러들은 고정된 주기(hz)의 time tick을 받아 태스크들의 스케쥴링을 수행했다. time tick을 줄여서 그냥 tick으로 불리고 있다. 현재는 커널 v3.10에 이르러 다음과 같이 3가지의 커널 옵션을 구분해서 사용한다.

  • CONFIG_HZ_PERIODIC
    • cpu의 idle 상태와 상관 없이 기존 커널과 같이 항상 tick을 발생한다.
    • 커널 버전 및 응용에 따라 다음 중 하나의 hz를 사용한다.
      • 100hz, 200hz, 250hz, 300hz, 500hz, 1000hz
        • rpi2: 100hz
  • CONFIG_NO_HZ_IDLE
    • cpu가 idle 상태로 들어가면 tick을 끈다.
      • rcu callback 처리가 남아 있는 경우 해당 cpu는 no hz 모드로 진입하지 못한다.
    • rpi2: 현재 이 모드를 사용한다.
    • dynticks idle 또는 tickless idle 이라고도 불린다.
    • 참고: Clockevents and dyntick | LWN.net
  •  CONFIG_NO_HZ_FULL
    • cpu가 idle 상태로 들어가면 tick을 끈다.
      • 최대 1000hz -> 0hz로 줄여 전력 소모를 대폭 줄인다.
      • rcu callback 처리가 남아 있는 경우 해당 cpu는 no hz 모드로 진입하지 못한다.
    • single task가 동작중인 경우 tick을 1Hz 로 낮춘다. (많은 경우에 해당한다)
      • 최대 1000hz -> 1hz로 줄여 성능이 빨라지는 효과가 있다.
      • 디버깅 모드에서 1hz가 아니라 no hz로 바꾸어 문제점 분석에 사용할 수 있다.
    • full dynticks 또는 full tickless 라고도 불린다.
    • “nohz_full=” 부트 타임 커널 파라메터를 사용하는 경우에만 동작시킬 수 있다.
      • 설정되지 않는 경우 CONFIG_NO_HZ_IDLE 커널 옵션과 동일하게 동작한다.
      • 예)
        • “nohz_full=0-3”
          • cpu#0~#3까지 적용
        • “nohz_full=4,8-15”
          • cpu#4, #8~#15까지 적용
    • 참고:

 

1 HZ

  • 초당 하나의 타이머 인터럽트

 

NO_HZ

  • 특징
    • 최소한 하나의 cpu는 주기적으로  tick을 받는다.
    • SMP에서 유효하다.
    • 구현을 위해 하드웨어적으로 hr-timer가 필요하다.
  • 장점
    • 전력 소모가 크게 줄어 든다.
  • 단점
    • 시간 계산과 RCU callback 처리를 위해 구현이 복잡해졌다.
    • rcu callback 처리가 남아 있는 경우 해당 cpu는 no hz 모드로 진입하지 못한다.

 

Tick Broadcast

cpu가 깊은 idle 상태에 있을 때 tick broadcast를 수신받아 idle 상태에서 벗어난 후 리스케쥴 여부를 확인하고 진행할 태스크가 있는 경우 수행을 하게 한다.

  • 실행할 task가 없으면 cpu는 cpuidle_idle_call() 함수를 통해 idle loop 상태에 진입한다.
    • bootup을 담당한 첫 번째 cpu는 rest_init() 함수의 마지막 cpu_startup_entry() -> cpu_idle_loop()에서 idle 루프를 돈다.
    • 그 외 cpu 들을 online 상태로 변경하는 경우 secondary_start_kernel() 함수의 마지막 cpu_startup_entry() -> cpu_idle_loop()에서 idle 루프를 돈다.
  • Tick이 발생하여 리스케쥴링이 발생하는 경우 idle 상태를 벗어난다.
    • no hz에서 tick이 없는 경우 tick broadcast에 의해 깨어나 idle 상태를 탈출할 수 있다.
  • tick device 사용
    • 클럭소스는 clock_event_device를 사용한다.
    • tick_device 구조체를 사용하여 표현한다.
    • tick device 모드
      • periodic 모드
        • tick_broadcast_mask cpu 비트맵을 대상으로 tick_broadcast_start_periodic() 함수를 사용해 broadcast tick을 주기적으로 보낼 수 있다.
      • oneshot 모드
    • tick 디바이스의 등록 함수
      • tick_install_broadcast_device(newdev)

 

Generic clock events

  • tick 구현에 대한 generic core 루틴

 

tick_init()

kernel/time/tick-common.c

/**
 * tick_init - initialize the tick control
 */
void __init tick_init(void)
{
        tick_broadcast_init();
        tick_nohz_init();
}

tick broadcast framework 및 full nohz framework을 준비한다.

 

tick_broadcast_init()

kernel/time/tick-broadcast.c

void __init tick_broadcast_init(void)
{
        zalloc_cpumask_var(&tick_broadcast_mask, GFP_NOWAIT);
        zalloc_cpumask_var(&tick_broadcast_on, GFP_NOWAIT);
        zalloc_cpumask_var(&tmpmask, GFP_NOWAIT);
#ifdef CONFIG_TICK_ONESHOT
        zalloc_cpumask_var(&tick_broadcast_oneshot_mask, GFP_NOWAIT);
        zalloc_cpumask_var(&tick_broadcast_pending_mask, GFP_NOWAIT);
        zalloc_cpumask_var(&tick_broadcast_force_mask, GFP_NOWAIT);
#endif
}

tick broadcast framework을 준비한다.

  • tick_broadcast_mask
    • idle(sleep) 모드에 있는 cpu 비트맵
  • tick_broadcast_on
    • 주기적으로 broadcast가 수행되는 cpu 비트맵
  • tick_broadcast_oneshot_mask
    • oneshot으로 broadcast 해야 할 cpu 비트맵
  • tick_broadcast_pending_mask
    • broadcast가 지연된 cpu 비트맵
  • tick_broadcast_force_mask
    • 강제로 broadcast해야 할 cpu 비트맵
  • 참고: The tick broadcast framework | LWN.net

 

tick_nohz_init()

kernel/time/tick-sched.c

void __init tick_nohz_init(void)
{
        int cpu;

        if (!tick_nohz_full_running) {
                if (tick_nohz_init_all() < 0)
                        return;
        }

        if (!alloc_cpumask_var(&housekeeping_mask, GFP_KERNEL)) {
                WARN(1, "NO_HZ: Can't allocate not-full dynticks cpumask\n");
                cpumask_clear(tick_nohz_full_mask);
                tick_nohz_full_running = false; 
                return;
        }

        /*
         * Full dynticks uses irq work to drive the tick rescheduling on safe
         * locking contexts. But then we need irq work to raise its own
         * interrupts to avoid circular dependency on the tick 
         */
        if (!arch_irq_work_has_interrupt()) {
                pr_warning("NO_HZ: Can't run full dynticks because arch doesn't "
                           "support irq work self-IPIs\n");
                cpumask_clear(tick_nohz_full_mask);
                cpumask_copy(housekeeping_mask, cpu_possible_mask);
                tick_nohz_full_running = false;
                return;
        }
        
        cpu = smp_processor_id();

        if (cpumask_test_cpu(cpu, tick_nohz_full_mask)) {
                pr_warning("NO_HZ: Clearing %d from nohz_full range for timekeeping\n", cpu);
                cpumask_clear_cpu(cpu, tick_nohz_full_mask);
        }

        cpumask_andnot(housekeeping_mask,
                       cpu_possible_mask, tick_nohz_full_mask);

        for_each_cpu(cpu, tick_nohz_full_mask)
                context_tracking_cpu_set(cpu);

        cpu_notifier(tick_nohz_cpu_down_callback, 0);
        pr_info("NO_HZ: Full dynticks CPUs: %*pbl.\n",
                cpumask_pr_args(tick_nohz_full_mask));
}

CONFIG_NO_HZ_FULL 커널 옵션을 사용한 경우 full tickless(no hz)로 동작을 하기 위한 framework을 준비한다.

  • 코드 라인 5~8에서 “nohz_full=” 부트 타임 커널 파라메터를 사용하지 않은 경우 처리를 포기한다.
  • 코드 라인 10~15에서 housekeeping_mask에 cpu 마스크를 할당한다. 할당이 실패하는 경우 처리를 포기한다.
  • 코드 라인 22~29에서 SMP 머신이 아닌 경우 처리를 포기한다.
  • 코드 라인 31~36에서 tick_nohz_fullmask에서 현재 cpu에 해당하는 비트를 클리어한다.
  • 코드 라인 38~39에서 housekeeping_mask에 possible cpu들에 대해 nohz full이 설정되지 않은 cpu들만 설정한다.
    • housekeeping_mask <- cpu_possible_mask & ~tick_nohz_full_mask
  • 코드 라인 41~42에서 CONFIG_CONTEXT_TRACKING 커널 옵션을 사용하는 경우 nohz full 설정된 cpu들에 대해서만 context tracking을 허용하도록 설정한다.
  • 코드 라인 44에서 cpu 상태 변화 시 호출되도록 우선 순위를 0으로 cpu notify chain에 tick_nohz_cpu_down_callback() 함수를 추가한다.
  • 코드 라인 45~46에서 “NO_HZ: Full dynticks CPUs:”  정보 메시지를 출력한다.

 

참고

답글 남기기

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