watchdog(二)-----softlockup


如果CPU可以响应中断,但是在长时间内不能调度(比如禁止抢占时间太长),此时就需要一种机制(softlockup)来检测这种情形。

本文基于3.14.25

记录下第二种比较严重的“死锁”:禁止抢占的时间太长,此时依旧可以相应中断,但是本CPU上在长时间内没有发生调度。                      
检测机制:softlockup

reset_init
|-->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

kernel_init
|-->kernel_init_freeable();
    |-->lockup_detector_init();
        |-->watchdog_enable_all_cpus(false);
            |-->smpboot_register_percpu_thread(&watchdog_threads);

 

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,
};

smpboot_register_percpu_thread(&watchdog_threads)
|-->__smpboot_create_thread(&watchdog_threads, cpu);

165
static int __smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu) 166 { 167 struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu); 168 struct smpboot_thread_data *td; 169 +-- 4 lines: if (tsk)--- 173 td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu)); 174 +--- 2 lines: if (!td)--- 176 td->cpu = cpu; 177 td->ht = ht; 178 +-- 8 lines: tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,---- 186 *per_cpu_ptr(ht->store, cpu) = tsk; 187 +-- 12 lines: if (ht->create) {--- 199 return 0; 200 }

 

创建了一个线程A task,该线程的主函数为 smpboot_thread_fun,该线程存储于watchdog_threads.store中
过程(阅读smpboot_thread_fn):
1、调用setup:watchdog_enable;
2、调用thread_should_run:watchdog_should_run判定是否需要运行线程主函数
3、线程主函数thread_fn:watchdog

watchdog_enable
|-->struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);
|-->hrtimer->function = watchdog_timer_fn;
|-->hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL_PINNED);
|-->watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1);//实时优先级

这个函数比较重要,因为:
A task中主函数为smpboot_thread_fun,其中将会检测是否运行thread_fn:watchdog
watchdog_should_run
|-->return __this_cpu_read(hrtimer_interrupts) != __this_cpu_read(soft_lockup_hrtimer_cnt);
因此如果hrtimer_interrupts == soft_lockup_hrtimer_cnt成立则运行调度器(放弃CPU使用权),否则运行thread_fn:watchdog

我们看下thread_fn的职责:
watchdog:
|-->__this_cpu_write(soft_lockup_hrtimer_cnt, __this_cpu_read(hrtimer_interrupts));
|--> __this_cpu_write(watchdog_touch_ts, get_timestamp());

再看下hrtimer定时的职责:
static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
|-->unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts);
|-->__this_cpu_inc(hrtimer_interrupts); 记录hrtimer中断次数
|-->wake_up_process(__this_cpu_read(softlockup_watchdog));//唤醒创建的 A task,虽然被唤醒,但并不意味着就能得到调度
|-->hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
|-->duration = is_softlockup(touch_ts);//判定当前时间与touch_ts的时间差是否超过20s,
|   如果超过,说明发生了softlockup,否则说明没有发生。

 

总结:更新watchdog_touch_ts的是一个具有实时优先级的线程A task,既然是线程就存在是否得到调度的问题。如果在20s内,该线程没有得到调度,则说
明发生了softlockup。因为按照原理来说,实时优先级线程A task一定比普通优先级线程先得到调度。如果一个线程B禁止调度长达20s,则无论如何
wake_up_process,A task都是不会被调度到的,即watchdog_touch_ts是不会得到更新的。而softlockup的检测是放在一个hrtimer处理函数中的。关于该函数是在中断上半部运行还是下半部运行,我还不清楚,但是由于其在中断上下文运行,想必也是具有高”优先级“的。
至于hrtimer的运行周期为:watchdog_thresh * 2 / 5 = 4s,我认为只是加大了唤醒A task的次数(wake_up_process),毕竟在20s尝试4次唤醒A task时,只要A task有一次得到调度的机会就可以更新 wathchdog_touch_ts,也就不会判定发生softlockup.

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM