강좌 & 팁
글 수 2,412
2011.08.22 22:45:03 (*.119.104.169)
41280
안녕하세요 호서대학교 석사(과정) 이우영 입니다.
오늘은 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 모드에서 빠져 나옵니다.
오늘은 간단하게 끝냈습니다.
그럼 다음시간에 만나요~