閱讀119 返回首頁    go 阿裏雲 go 技術社區[雲棲]


時間子係統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

  上一篇:go 時間子係統17_hard lockup機製
  下一篇:go IP地址 A\B\C類