时间子系统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)

//	参考:http://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;
}
时间: 2024-10-30 10:05:47

时间子系统16_soft lockup机制的相关文章

时间子系统17_hard lockup机制

// 使能hard lockup探测 // 调用路径:watchdog_enable->watchdog_nmi_enable // 函数任务: // 1.初始化hard lockup检测事件 // 2.hard lockup阈值为10s // 2.向performance monitoring子系统注册hard lockup检测事件 // 3.使能hard lockup检测事件 // 注: // performance monitoring,x86中的硬件设备,当cpu clock经过了指定个

Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】

转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 数据结构 低分辨率下的动态时钟 1  切换至动态时钟模式 2  低分辨率动态时钟下的事件中断处理函数 3  动态时钟停止周期tick时钟事件 3  动态时钟重新开启周期tick时钟事件 高精度模式下的动态时钟 动态时钟对中断的影响   在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由

Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()【转】

  转自:http://blog.csdn.net/droidphone/article/details/8104433 我们已经在前面几章介绍了低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep等等,这些API基于低分辨率定时器或高精度定时器来实现,本章的内容就是讨论这些方便.好用的API是如何利用定时器系统来完成所需的功能的. /*************************

时间子系统2_tick device管理机制

// 1.每个cpu都具有一个tick_device,维护周期时钟. // 2.tick_device依赖一个clockevent设备,提供周期事件. // 3.cpu电源状态的改变会影响tick_device,通过tick_notifier监听电源状态. // 4.全局广播设备接管进入省电模式cpu的周期时间维护. // 4.broadcast_mask保存开启广播模式的cpu, broadcast_oneshot_mask保存进入省电模式的cpu. // 监听clockevent设备状态 /

时间子系统12_clockevent设备注册

// 向系统注册时钟事件设备 // 函数任务: // 1.添加设备到clockevent_devices链表 // 2.通知tick device管理机制有clockevent设备注册 1.1 void clockevents_register_device(struct clock_event_device *dev) { unsigned long flags; raw_spin_lock_irqsave(&clockevents_lock, flags); //将设备添加到clockeve

时间子系统9_timekeeper初始化

//Timekeeping // Timekeeping子系统负责更新xtime, 调整误差, 及提供get/settimeofday接口. //Times in Kernel //kernel的time基本类型: // 1) system time // A monotonically increasing value that represents the amount of time the system has been running. // 单调增长的系统运行时间, 可以通过time

时间子系统6_高分辨率定时器框架初始化

// 高分辨率定时器框架初始化 // 调用路径:start_kernel->hrtimers_init // 函数任务: // 1.创建cpu时钟基础 // 2.注册监听cpu状态变化 // 3.注册高分辨率模式下的定时器软中断 // 注: // 1.高分辨率定时器框架的通用部分总是编译进内核 // 2.高分辨率定时器框架初始为未激活状态,由低分辨率定时器软中断中切换到高分辨率 1.1 void __init hrtimers_init(void) { //通知clockevent设备管理,创建

时间子系统14_全局时间维护

// 更新全局时间(由动态时钟调用) // 函数任务: // 1.更新last_jiffies_update,记录距离上次更新jiffies经历的ns // 2.更新jiffies_64,墙上时间,计算cpu负载 // 3.更新下次周期时钟的到期时间 // 注: // 1.在关中断情况下调用该函数 // 2.last_jiffies_update,记录距离上次更新经历的时钟周期(ns) 1.1 static void tick_do_update_jiffies64(ktime_t now) {

时间子系统15_获取系统时间

// 获取系统时间 // 注:将timespec转换为timeval 1.1 void do_gettimeofday(struct timeval *tv) { struct timespec now; getnstimeofday(&now); tv->tv_sec = now.tv_sec; tv->tv_usec = now.tv_nsec/1000; } // 获取纳秒级系统时间 // 函数任务: // 1.获取墙上时间t // 2.获取距离上次更新xtime以来经历的纳秒d