안녕하세요 호서대학교 석사(과정) 이우영 입니다.


오늘은 cpu_idle 에서 잠깐 다뤘던 tickless 에 대해서 알아 보도록 하겠습니다.


tickless란?


tickless 모드는 전력 조모를 줄이기 위한 방법의 하나이다.


전통적으로 리눅스 커널은 각각의 CPU에 대한 정기적인 타이머를 사용한다.


이 주기적인 타이머 틱은 idle 상태에서도 쉬지 않고 동작 합니다.


idle 상태에서는 타이머가 동작할 이유가 없습니다.


그래서 이시간에는 타이머를 쉬게 하자 해서 나온것이 이 tickless 모드 입니다.


tickless 모드는 리눅스 커널 2.6.21 커널부터 mainstream에 포함되었다.


소스코드는 kernel/time/tick-sched.c 에  구현되어 있다.


tick_nohz_stop_sched_tick(1); tickless 모드로 진입한다.


 206/**
 207 * tick_nohz_stop_sched_tick - stop the idle tick from the idle task
 208 *
 209 * When the next event is more than a tick into the future, stop the idle tick
 210 * Called either from the idle loop or from irq_exit() when an idle period was
 211 * just interrupted by an interrupt which did not cause a reschedule.
 212 */
 213void tick_nohz_stop_sched_tick(int inidle)
 214{
 215        unsigned long seq, last_jiffies, next_jiffies, delta_jiffies, flags;
 216        struct tick_sched *ts;
 217        ktime_t last_update, expires, now;
 218        struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
 219        int cpu;
 220
 221        local_irq_save(flags);
 222
 223        cpu = smp_processor_id();
 224        ts = &per_cpu(tick_cpu_sched, cpu);
 225        now = tick_nohz_start_idle(ts);
 226
 227        /*
 228         * If this cpu is offline and it is the one which updates
 229         * jiffies, then give up the assignment and let it be taken by
 230         * the cpu which runs the tick timer next. If we don't drop
 231         * this here the jiffies might be stale and do_timer() never
 232         * invoked.
 233         */
 234        if (unlikely(!cpu_online(cpu))) {
 235                if (cpu == tick_do_timer_cpu)
 236                        tick_do_timer_cpu = TICK_DO_TIMER_NONE;
 237        }
 238
 239        if (unlikely(ts->nohz_mode == NOHZ_MODE_INACTIVE))
 240                goto end;
 241
 242        if (!inidle && !ts->inidle)
 243                goto end;
 244
 245        ts->inidle = 1;
 246
 247        if (need_resched())
 248                goto end;
 249
 250        if (unlikely(local_softirq_pending() && cpu_online(cpu))) {
 251                static int ratelimit;
 252
 253                if (ratelimit < 10) {
 254                        printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n",
 255                               local_softirq_pending());
 256                        ratelimit++;
 257                }
 258                goto end;
 259        }
 260
 261        ts->idle_calls++;
 262        /* Read jiffies and the time when jiffies were updated last */
 263        do {
 264                seq = read_seqbegin(&xtime_lock);
 265                last_update = last_jiffies_update;
 266                last_jiffies = jiffies;
 267        } while (read_seqretry(&xtime_lock, seq));
 268
 269        /* Get the next timer wheel timer */
 270        next_jiffies = get_next_timer_interrupt(last_jiffies);
 271        delta_jiffies = next_jiffies - last_jiffies;
 272
 273        if (rcu_needs_cpu(cpu) || printk_needs_cpu(cpu))
 274                delta_jiffies = 1;
 275        /*
 276         * Do not stop the tick, if we are only one off
 277         * or if the cpu is required for rcu
 278         */
 279        if (!ts->tick_stopped && delta_jiffies == 1)
 280                goto out;
 281
 282        /* Schedule the tick, if we are at least one jiffie off */
 283        if ((long)delta_jiffies >= 1) {
 284
 285                /*
 286                * calculate the expiry time for the next timer wheel
 287                * timer
 288                */
 289                expires = ktime_add_ns(last_update, tick_period.tv64 *
 290                                   delta_jiffies);
 291
 292                /*
 293                 * If this cpu is the one which updates jiffies, then
 294                 * give up the assignment and let it be taken by the
 295                 * cpu which runs the tick timer next, which might be
 296                 * this cpu as well. If we don't drop this here the
 297                 * jiffies might be stale and do_timer() never
 298                 * invoked.
 299                 */
 300                if (cpu == tick_do_timer_cpu)
 301                        tick_do_timer_cpu = TICK_DO_TIMER_NONE;
 302
 303                if (delta_jiffies > 1)
 304                        cpumask_set_cpu(cpu, nohz_cpu_mask);
 305
 306                /* Skip reprogram of event if its not changed */
 307                if (ts->tick_stopped && ktime_equal(expires, dev->next_event))
 308                        goto out;
 309
 310                /*
 311                 * nohz_stop_sched_tick can be called several times before
 312                 * the nohz_restart_sched_tick is called. This happens when
 313                 * interrupts arrive which do not cause a reschedule. In the
 314                 * first call we save the current tick time, so we can restart
 315                 * the scheduler tick in nohz_restart_sched_tick.
 316                 */
 317                if (!ts->tick_stopped) {
 318                        if (select_nohz_load_balancer(1)) {
 319                                /*
 320                                 * sched tick not stopped!
 321                                 */
 322                                cpumask_clear_cpu(cpu, nohz_cpu_mask);
 323                                goto out;
 324                        }
 325
 326                        ts->idle_tick = hrtimer_get_expires(&ts->sched_timer);
 327                        ts->tick_stopped = 1;
 328                        ts->idle_jiffies = last_jiffies;
 329                        rcu_enter_nohz();
 330                }
 331
 332                ts->idle_sleeps++;
 333
 334                /*
 335                 * delta_jiffies >= NEXT_TIMER_MAX_DELTA signals that
 336                 * there is no timer pending or at least extremly far
 337                 * into the future (12 days for HZ=1000). In this case
 338                 * we simply stop the tick timer:
 339                 */
 340                if (unlikely(delta_jiffies >= NEXT_TIMER_MAX_DELTA)) {
 341                        ts->idle_expires.tv64 = KTIME_MAX;
 342                        if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
 343                                hrtimer_cancel(&ts->sched_timer);
 344                        goto out;
 345                }
 346
 347                /* Mark expiries */
 348                ts->idle_expires = expires;
 349
 350                if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
 351                        hrtimer_start(&ts->sched_timer, expires,
 352                                      HRTIMER_MODE_ABS);
 353                        /* Check, if the timer was already in the past */
 354                        if (hrtimer_active(&ts->sched_timer))
 355                                goto out;
 356                } else if (!tick_program_event(expires, 0))
 357                                goto out;
 358                /*
 359                 * We are past the event already. So we crossed a
 360                 * jiffie boundary. Update jiffies and raise the
 361                 * softirq.
 362                 */
 363                tick_do_update_jiffies64(ktime_get());
 364                cpumask_clear_cpu(cpu, nohz_cpu_mask);
 365        }
 366        raise_softirq_irqoff(TIMER_SOFTIRQ);
 367out:
 368        ts->next_jiffies = next_jiffies;
 369        ts->last_jiffies = last_jiffies;
 370        ts->sleep_length = ktime_sub(dev->next_event, now);
 371end:
 372        local_irq_restore(flags);
 373}


tick_nohz_restart_sched_tick();  tickless 모드에서 빠져 나온다.


 413/**
 414 * tick_nohz_restart_sched_tick - restart the idle tick from the idle task
 415 *
 416 * Restart the idle tick when the CPU is woken up from idle
 417 */
 418void tick_nohz_restart_sched_tick(void)
 419{
 420        int cpu = smp_processor_id();
 421        struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
 422#ifndef CONFIG_VIRT_CPU_ACCOUNTING
 423        unsigned long ticks;
 424#endif
 425        ktime_t now;
 426
 427        local_irq_disable();
 428        tick_nohz_stop_idle(cpu);
 429
 430        if (!ts->inidle || !ts->tick_stopped) {
 431                ts->inidle = 0;
 432                local_irq_enable();
 433                return;
 434        }
 435
 436        ts->inidle = 0;
 437
 438        rcu_exit_nohz();
 439
 440        /* Update jiffies first */
 441        select_nohz_load_balancer(0);
 442        now = ktime_get();
 443        tick_do_update_jiffies64(now);
 444        cpumask_clear_cpu(cpu, nohz_cpu_mask);
 445
 446#ifndef CONFIG_VIRT_CPU_ACCOUNTING
 447        /*
 448         * We stopped the tick in idle. Update process times would miss the
 449         * time we slept as update_process_times does only a 1 tick
 450         * accounting. Enforce that this is accounted to idle !
 451         */
 452        ticks = jiffies - ts->idle_jiffies;
 453        /*
 454         * We might be one off. Do not randomly account a huge number of ticks!
 455         */
 456        if (ticks && ticks < LONG_MAX)
 457                account_idle_ticks(ticks);
 458#endif
 459
 460        touch_softlockup_watchdog();
 461        /*
 462         * Cancel the scheduled timer and restore the tick
 463         */
 464        ts->tick_stopped  = 0;
 465        ts->idle_exittime = now;
 466
 467        tick_nohz_restart(ts, now);
 468
 469        local_irq_enable();
 470}


그리고 tickless 모드를 사용하기 위해서는 커널 옵션에서 설정을 해주어야 한다.


s3c6410 에서는 idle 진입 할 때 tickless 모드도 지원한다.


idle 상태에 들어가면 tick_nohz_stop_sched_tick(1); 를 호출하여 tickless 모드에 진입하고

무한루프를 돌게 됩니다 idle 상태에서 빠져나오면 스케줄러를 호출하기 전에 

tick_nohz_restart_sched_tick();를 호출하여 tickless 모드에서 빠져 나옵니다.

오늘은 간단하게 끝냈습니다.


그럼 다음시간에 만나요~