時間子係統16_soft lockup機製
// 1.debug選項LOCKUP_DETECTOR,開啟/關閉kernel中的soft lockup和hard lockup探測 // 2.實現:kernel/watchdog.c // 3.實現原理: // 1.涉及到了3部分內容:kernel線程,時鍾中斷,NMI中斷 // 優先級:kernel線程 < 時鍾中斷 < NMI中斷。 // 2.利用它們之間優先級的區別,調試係統運行中的兩種問題: // 搶占被長時間關閉而導致進程無法調度(soft lockup) // 中斷被長時間關閉而導致更嚴重的問題(hard lockup) // 參考:https://blog.csdn.net/panzhenjie/article/details/10074551 // 內核版本 3.8.6 // smp per-cpu watchdog核心線程 1.1 static struct smp_hotplug_thread watchdog_threads = { .store = &softlockup_watchdog, .thread_should_run = watchdog_should_run, .thread_fn = watchdog, .thread_comm = "watchdog/%u", .setup = watchdog_enable, .park = watchdog_disable, .unpark = watchdog_enable, }; // lockup detector模塊初始化 // 函數任務: // 1.計算hrtimer運行的頻率 // 2.注冊watchdog核心線程 // 注: 1.2 void __init lockup_detector_init(void) { //計算hrtimer運行的頻率 set_sample_period(); //注冊watchdog核心線程 if (smpboot_register_percpu_thread(&watchdog_threads)) { pr_err("Failed to create watchdog threads, disabled\n"); watchdog_disabled = -ENODEV; } } // 設置watchdog timer運行頻率 // 調用路徑: lockup_detector_init->get_softlockup_thresh // 注: // 1.sample_period,即watchdog timer運行的頻率 // 2.watchdog timer在一次soft lockup時間閾值內運行5次 1.3 static void set_sample_period(void) { sample_period = get_softlockup_thresh() * ((u64)NSEC_PER_SEC / 5); } // 認定發生了soft lockup的時間閾值 // 注:如果watchdog kthread在watchdog_thresh * 2 時間內未被調度, // 則認為發生了soft lockup. 1.4 static int get_softlockup_thresh(void) { return watchdog_thresh * 2; } // 啟動指定cpu上lockup檢測 // 函數任務: // 1.初始化watchdog timer // 2.初始化hard lockup的nmi中斷事件 // 3.啟動watchdog timer // 4.設置watchdog kthread調度策略FIFO // 5.更新watchdog時間戳 // 注:設置watchdog kthread為FIFO的調度策略保證了watchdog timer // 喚醒kthread之後,它可以因高優先級切換到cpu上執行。 2.1 static void watchdog_enable(unsigned int cpu) { struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); //lockup檢測使用的hrtimer hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer->function = watchdog_timer_fn; //第一次啟動watchdog,暫停current if (!watchdog_enabled) { kthread_park(current); return; } //hard lockup檢測機製 watchdog_nmi_enable(cpu); //hrtimer sample時間之後運行 hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL_PINNED); //watchdog進程FIFO策略 watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1); //執行一次更新 __touch_watchdog(); } // 關閉指定cpu上的lockup檢測 // 函數任務: // 1.恢複watchdog正常優先級 // 2.取消hrtimer // 3.關閉hard lockup檢測機製的nmi中斷 2.2 static void watchdog_disable(unsigned int cpu) { struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer); //恢複watchdog正常優先級 watchdog_set_prio(SCHED_NORMAL, 0); //取消hrtimer hrtimer_cancel(hrtimer); //關閉hard lockup檢測機製的nmi中斷 watchdog_nmi_disable(cpu); } // watchdog核心線程可運行的條件 // 函數任務: // 1.保證watchdog kthread 運行頻率 <= watchdog timer 運行頻率 // 注: // soft_lockup_hrtimer_cnt代表watchdog核心線程運行的次數 // hrtimer_interrupts代表watchdog timer運行的次數 2.3 static int watchdog_should_run(unsigned int cpu) { return __this_cpu_read(hrtimer_interrupts) != __this_cpu_read(soft_lockup_hrtimer_cnt); } // watchdog核心線程函數 // 函數任務: // 1.更新soft_lockup_hrtimer_cnt=hrtimer_interrupts // 2.更新watchdog運行時間戳 2.4 static void watchdog(unsigned int cpu) { __this_cpu_write(soft_lockup_hrtimer_cnt, __this_cpu_read(hrtimer_interrupts)); __touch_watchdog(); } // 更新watchdog運行時間戳 2.5 static void __touch_watchdog(void) { int this_cpu = smp_processor_id(); __this_cpu_write(watchdog_touch_ts, get_timestamp(this_cpu)); } // 定時器函數 // 函數主要任務: // 1.獲取watchdog上次運行的時間戳 // 2.遞增watchdog timer運行次數 // 3.檢查watchdog時間戳,是否發生了soft lockup // 3.1 如果發生了,dump堆棧,打印信息 // 4.重調度timer // 注: // 在watchdog timer運行時喚醒watchdog kthread,保證kthread與timer相同的運行頻率 3.1 static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer) { //watchdog上次運行的時間戳 unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts); struct pt_regs *regs = get_irq_regs(); int duration; //在喚醒watchdog kthread之前遞增hrtimer_interrupts,保證kthread更新其時間戳 watchdog_interrupt_count(); //喚醒watchdog kthread,保證kthread與timer相同的運行頻率 wake_up_process(__this_cpu_read(softlockup_watchdog)); //再次調度hrtimer下一個周期運行 hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period)); ... //檢測是否發生soft lockup duration = is_softlockup(touch_ts); if (unlikely(duration)) { printk(KERN_EMERG "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n", smp_processor_id(), duration, current->comm, task_pid_nr(current)); print_modules(); print_irqtrace_events(current); //dump 寄存器和堆棧 if (regs) show_regs(regs); else dump_stack(); if (softlockup_panic) panic("softlockup: hung tasks"); } return HRTIMER_RESTART; } // 檢查搶占被關閉的時間間隔 // watchdog kthread在watchdog timer的中斷上下文中被喚醒, // 當中斷退出時,kthread會搶占cpu上的當前進程。如果 // 搶占被關閉的話,則不會發生搶占,watchdog便無法更新時 // 間戳,當搶占關閉的時間超過閾值時,核心認為發生了soft // lock up。 // 注:soft lockup閾值 watchdog_thresh * 2 (20s) 3.2 static int is_softlockup(unsigned long touch_ts) { //當前時間戳 unsigned long now = get_timestamp(smp_processor_id()); //watchdog在 watchdog_thresh * 2 時間內未被調度過 if (time_after(now, touch_ts + get_softlockup_thresh())) return now - touch_ts; return 0; }
最後更新:2017-04-03 14:54:38