• soft lockup和hard lockup介绍


    转自:http://www.cnblogs.com/openix/p/4034530.html

    转自:http://blog.csdn.net/panzhenjie/article/details/10074551/

    在linux kernel里,有一个debug选项LOCKUP_DETECTOR。

    使能它可以打开kernel中的soft lockup和hard lockup探测。

    这两个东西到底有什么用处那?

    首先,soft/hard lockup的实现在kernel/watchdog.c中,

    主体涉及到了3个东西:kernel线程,时钟中断,NMI中断(不可屏蔽中断)。

    这3个东西具有不一样的优先级,依次是kernel线程 < 时钟中断 < NMI中断。

    而正是用到了他们之间优先级的区别,所以才可以调试系统运行中的两种问题:

    1. 抢占被长时间关闭而导致进程无法调度(soft lockup)

    2. 中断被长时间关闭而导致更严重的问题(hard lockup)

    接下来我们从具体代码入手分析linux(3.10)是如何实现这两种lockup的探测的:

    [cpp] view plain copy
     
    1. static struct smp_hotplug_thread watchdog_threads = {  
    2.     .store          = &softlockup_watchdog,  
    3.     .thread_should_run  = watchdog_should_run,  
    4.     .thread_fn      = watchdog,  
    5.     .thread_comm        = "watchdog/%u",  
    6.     .setup          = watchdog_enable,  
    7.     .park           = watchdog_disable,  
    8.     .unpark         = watchdog_enable,  
    9. };  
    10.   
    11. void __init lockup_detector_init(void)  
    12. {  
    13.     set_sample_period();  
    14.     if (smpboot_register_percpu_thread(&watchdog_threads)) {  
    15.         pr_err("Failed to create watchdog threads, disabled ");  
    16.         watchdog_disabled = -ENODEV;  
    17.     }  
    18. }  

    首先,系统会为每个cpu core注册一个一般的kernel线程,名字叫watchdog/0, watchdog/1...以此类推。

    这个线程会定期得调用watchdog函数

    [cpp] view plain copy
     
    1. static void __touch_watchdog(void)  
    2. {  
    3.     __this_cpu_write(watchdog_touch_ts, get_timestamp());  
    4. }  
    5.   
    6. static void watchdog(unsigned int cpu)  
    7. {  
    8.     __this_cpu_write(soft_lockup_hrtimer_cnt,  
    9.              __this_cpu_read(hrtimer_interrupts));  
    10.     __touch_watchdog();  
    11. }  

    我们先不理会这个线程处理函数watchdog多久被调用一次,我们就先简单的认为,这个线程是负责更新watchdog_touch_ts的。

    然后我们要看一下时钟中断了:

    [cpp] view plain copy
     
    1. static void watchdog_enable(unsigned int cpu)  
    2. {  
    3.     struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);  
    4.   
    5.     /* kick off the timer for the hardlockup detector */  
    6.     hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);  
    7.     hrtimer->function = watchdog_timer_fn;  
    8.   
    9.     /* done here because hrtimer_start can only pin to smp_processor_id() */  
    10.     hrtimer_start(hrtimer, ns_to_ktime(sample_period),  
    11.               HRTIMER_MODE_REL_PINNED);  
    12. }  

    时钟中断处理函数是watchdog_timer_fn

    [cpp] view plain copy
     
    1. static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)  
    2. {  
    3.     unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts);  
    4.     int duration;  
    5.   
    6.     /* kick the hardlockup detector */  
    7.     watchdog_interrupt_count();  
    8.   
    9.     duration = is_softlockup(touch_ts);  
    10.     if (unlikely(duration)) {  
    11.         if (softlockup_panic)  
    12.             panic("softlockup: hung tasks");  
    13.         __this_cpu_write(soft_watchdog_warn, true);  
    14.     } else  
    15.         __this_cpu_write(soft_watchdog_warn, false);  
    16.   
    17.     return HRTIMER_RESTART;  
    18. }  

    这个函数主要做2件事情:

    1. 更新hrtimer_interrupts变量。

    [cpp] view plain copy
     
    1. static void watchdog_interrupt_count(void)  
    2. {  
    3.     __this_cpu_inc(hrtimer_interrupts);  
    4. }  

    这里我们就要回顾之前创建的那个kernel线程了,多久调用一次就和hrtimer_interrupts的值密切相关。

    [cpp] view plain copy
     
    1. static int watchdog_should_run(unsigned int cpu)  
    2. {  
    3.     return __this_cpu_read(hrtimer_interrupts) !=  
    4.         __this_cpu_read(soft_lockup_hrtimer_cnt);  
    5. }  

    只有在hrtimer_interrupts发生了更新的情况下,kernel线程才会被得到运行。

    那就是说,kernel线程和时钟中断函数的频率是相同的。默认情况是10*2/5=4秒一次。

    [cpp] view plain copy
     
    1. int __read_mostly watchdog_thresh = 10;  
    2.   
    3. static int get_softlockup_thresh(void)  
    4. {  
    5.     return watchdog_thresh * 2;  
    6. }  
    7.   
    8. static void set_sample_period(void)  
    9. {  
    10.     /* 
    11.      * convert watchdog_thresh from seconds to ns 
    12.      * the divide by 5 is to give hrtimer several chances (two 
    13.      * or three with the current relation between the soft 
    14.      * and hard thresholds) to increment before the 
    15.      * hardlockup detector generates a warning 
    16.      */  
    17.     sample_period = get_softlockup_thresh() * ((u64)NSEC_PER_SEC / 5);  
    18. }  

    2.就是要探测是否有soft lockup发生。

    [cpp] view plain copy
     
    1. static int is_softlockup(unsigned long touch_ts)  
    2. {  
    3.     unsigned long now = get_timestamp();  
    4.   
    5.     /* Warn about unreasonable delays: */  
    6.     if (time_after(now, touch_ts + get_softlockup_thresh()))  
    7.         return now - touch_ts;  
    8.   
    9.     return 0;  
    10. }  

    很容易理解,其实就是查看watchdog_touch_ts变量在最近20秒的时间内,有没有被创建的kernel thread更新过。

    假如没有,那就意味着线程得不到调度,所以很有可能就是在某个cpu core上抢占被关闭了,所以调度器没有办法进行调度。

    这种情况下,系统往往不会死掉,但是会很慢。

    有了soft lockup的机制,我们就能尽早的发现这样的问题了。

    分析完soft lockup,我们继续分析hard lockup

    [cpp] view plain copy
     
    1. static int watchdog_nmi_enable(unsigned int cpu)  
    2. {  
    3.     struct perf_event_attr *wd_attr;  
    4.   
    5.     wd_attr = &wd_hw_attr;  
    6.     wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);  
    7.   
    8.     /* Try to register using hardware perf events */  
    9.     event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);  
    10. }  

    perf_event_create_kernel_counter函数主要是注册了一个硬件的事件。

    这个硬件在x86里叫performance monitoring,这个硬件有一个功能就是在cpu clock经过了多少个周期后发出一个NMI中断出来。

    [cpp] view plain copy
     
    1. u64 hw_nmi_get_sample_period(int watchdog_thresh)  
    2. {  
    3.     return (u64)(cpu_khz) * 1000 * watchdog_thresh;  
    4. }  

    在这里,根据当前cpu的频率,算出一个值,也就是20秒cpu clock经过的周期数。

    这样一来,当cpu全负荷跑完20秒后,就会有一个NMI中断发出,而这个中断的出路函数就是watchdog_overflow_callback。

    [cpp] view plain copy
     
    1. static void watchdog_overflow_callback(struct perf_event *event,  
    2.          struct perf_sample_data *data,  
    3.          struct pt_regs *regs)  
    4. {  
    5.     if (is_hardlockup()) {  
    6.         int this_cpu = smp_processor_id();  
    7.   
    8.         if (hardlockup_panic)  
    9.             panic("Watchdog detected hard LOCKUP on cpu %d", this_cpu);  
    10.         else  
    11.             WARN(1, "Watchdog detected hard LOCKUP on cpu %d", this_cpu);  
    12.   
    13.         return;  
    14.     }  
    15.   
    16.     return;  
    17. }  

    这个函数主要就是调用is_hardlockup

    [cpp] view plain copy
     
    1. static int is_hardlockup(void)  
    2. {  
    3.     unsigned long hrint = __this_cpu_read(hrtimer_interrupts);  
    4.   
    5.     if (__this_cpu_read(hrtimer_interrupts_saved) == hrint)  
    6.         return 1;  
    7.   
    8.     __this_cpu_write(hrtimer_interrupts_saved, hrint);  
    9.     return 0;  
    10. }  

    而这个函数主要就是查看hrtimer_interrupts变量在时钟中断处理函数里有没有被更新。

    假如没有更新,就意味着中断出了问题,可能被错误代码长时间的关中断了。
    那这样,相应的问题也就暴露出来了。

  • 相关阅读:
    Win10以管理员身份运行Loadrunner11时候提示“管理员已阻止你运行此应用”
    logrotate日志切割
    Scala的型变
    Caused by: com.esotericsoftware.kryo.KryoException: Buffer overflow. Available: 0, required: 134217728
    ERROR BatchJobMain: Task not serializable
    Spark创建HiveContext报错tez的问题
    Spark初始换HiveContext空指针异常
    windows10专业版 操作系统无法监听远程端口
    windows环境如何上传项目到gitee
    canal-kakfa-flink实现mysql数据的实时同步(一)
  • 原文地址:https://www.cnblogs.com/dirt2/p/5616757.html
Copyright © 2020-2023  润新知