jiffies和HZ的相关学习

                                                                        
jiffies和HZ的相关学习                                                                        
                                                                        
最近学习kernel timer的时候,看到了jiffies。                                                                        
刚好对其不是很熟悉,趁机学习了一把。                                                                        
                                                                        
参考:https://www.cnblogs.com/arnoldlu/p/7234443.html                                                                        
                                                                        
0、    概述                                                                    
                                                                        
    HZ是时钟中断产生的频率,即1秒钟产生多少时钟中断。                                                                    
    jiffies是启动后产生的时钟中断的总计数。                                                                    
    时钟初始化的时候,会注册时钟中断,并根据HZ设置时钟中断产生的频率,即多少个tick会产生一个时钟中断。                                                                    
    时钟中断的处理函数中,会去更新jiffies。                                                                    
                                                                        
1、    jiffies和jiffies_64                                                                    
                                                                        
        在32位系统中,jiffies即是jiffies_64的低32bit.                                                                
        参考:arch/arm/kernel/vmlinux.lds.S                                                                
            #ifndef __ARMEB__                                        // 如果不是Big endian,即为little endian                    
            jiffies = jiffies_64;                                        // 直接取低32bit                    
            #else                                                            
            jiffies = jiffies_64 + 4;                                        // 偏移4字节,取低32bit。注意这里的+4是地址+4,不是值+4。                    
            #endif                                                            
                                                                        
        在64位系统中,jiffies即是jiffies_64。                                                                
        参考:arch/arm64/kernel/vmlinux.lds.S                                                                
            jiffies = jiffies_64;                                                            
                                                                        
2、    jiffies_64的初始化                                                                    
                                                                        
        kernel/timer.c:                                                                
        u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;                                                                
                                                                        
        include/linux/jiffies.h:                                                                
        /*                                                                
         * Have the 32 bit jiffies value wrap 5 minutes after boot                                                                
         * so jiffies wrap bugs show up earlier.                                                                
         */                                                                
        #define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))                                                                
                                                                        
        看注释可知,将jiffies_64初始化为-300*HZ,目的是让其在启动后5分钟即出现溢出,以尽早暴露问题。                                                                
                                                                        
        5分钟是怎么来的:                                                                
            假如HZ为100,即时钟中断产生的频率为1秒钟100次。                                                            
            -300 * 100 = -30000 , 32位系统里面,用unsinged long表示,即为:0xFFFFFFFFFFFF8AD0。                                                            
            xFFFFFFFFFFFFFFFF - 0xFFFFFFFFFFFF8AD0 = 0x752F,即29999.                                                            
            也就是说,再经过30000个时钟中断,jiffies即出现溢出。30000个时钟中断,即300*HZ,即300秒。                                                            
                                                                        
3、    jiffies是如何更新的                                                                    
                                                                        
        从以上分析可知,jiffies只是指向了jiffies_64的低4字节,增加jiffies,同时也会增加jiffies_64。                                                                
        之前不了解这个情况,代码中找了半天增加jiffies的地方,结果没找到。                                                                
                                                                        
        kernel/time/Timekeeping.c                                                                
            /*                                                            
             * Must hold jiffies_lock                                                            
             */                                                            
            void do_timer(unsigned long ticks)                                                            
            {                                                            
                jiffies_64 += ticks;                                            // 追加jiffies_64            
                calc_global_load(ticks);                                                        
            }                                                            
                                                                        
        do_timer被调用的地方:                                                                
        ①    Tick-common.c (kernel\time):87:                                                    do_timer(1);        
        ②    Tick-sched.c (kernel\time):83:                                                    do_timer(++ticks);        
        ③    Timekeeping.c (kernel\time):2386:                                                    do_timer(ticks);        
                                                                        
        ①的调用路径(mt2712平台):                                                                
            mtk_timer_interrupt -> tick_handle_periodic -> tick_periodic -> do_timer(1)                                                            
                                                                        
        ②的调用路径:                                                                
            tick_check_idle-->tick_check_nohz-->tick_nohz_update_jiffies-->tick_do_update_jiffies64                                                            
            tick_nohz_idle_enter/tick_nohz_irq_exit-->tick_nohz_stop_sched_tick-->tick_do_update_jiffies64                                                            
            tick_setup_sched_timer-->tick_sched_timer-->tick_do_update_jiffies64                                                            
                                                                        
        ③的调用路径:                                                                
            timer_interrupt -> timer_tick -> xtime_update(1) -> do_timer(ticks);                                                            
            arm架构,在arch/arm/kernel/Time.c中,有timer_tick的定义,并且只有在部分arm平台,此函数才被调用。                                                            
            arm64架构中,无timer_tick定义。                                                            
                                                                        
        ①有点意思,看起来像是注册一个timer 中断,定周期的更新jiffies。                                                                
        mtk_timer_interrupt作为中断处理函数,在mtk_timer_init中被注册。                                                                
                                                                        
        drivers/clocksource/Mtk_timer.c                                                                
            注册中断的时候,会根据dts配置,设置时钟中断源clock,并根据HZ,计算出时钟中断的触发频率。                                                            
            static int __init mtk_timer_init(struct device_node *node)                                                            
            {                                                            
                struct mtk_clock_event_device *evt;                                                        
                struct resource res;                                                        
                unsigned long rate_src = 0, rate_evt = 0;                                                        
                struct clk *clk_src, *clk_evt, *clk_bus;                                                        
                                                                        
                evt = kzalloc(sizeof(*evt), GFP_KERNEL);                                                        
                if (!evt)                                                        
                    return -ENOMEM;                                                    
                                                                        
                evt->clk32k_exist = false;                                                        
                evt->dev.name = "mtk_tick";                                                        
                evt->dev.rating = 300;                                                        
                /*                                                        
                 * CLOCK_EVT_FEAT_DYNIRQ: Core shall set the interrupt affinity                                                        
                 *                        dynamically in broadcast mode.                                                        
                 * CLOCK_EVT_FEAT_ONESHOT: Use one-shot mode for tick broadcast.                                                        
                 */                                                        
                evt->dev.features = CLOCK_EVT_FEAT_PERIODIC |                                                        
                            CLOCK_EVT_FEAT_ONESHOT |                                            
                            CLOCK_EVT_FEAT_DYNIRQ;                                            
                evt->dev.set_state_shutdown = mtk_clkevt_shutdown;                                                        
                evt->dev.set_state_periodic = mtk_clkevt_set_periodic;                                                        
                evt->dev.set_state_oneshot = mtk_clkevt_shutdown;                                                        
                evt->dev.tick_resume = mtk_clkevt_shutdown;                                                        
                evt->dev.set_next_event = mtk_clkevt_next_event;                                                        
                evt->dev.cpumask = cpu_possible_mask;                                                        
                                                                        
                evt->gpt_base = of_io_request_and_map(node, 0, "mtk-timer");                                                        
                if (IS_ERR(evt->gpt_base)) {                                                        
                    pr_err("Can't get resource\n");                                                    
                    goto err_kzalloc;                                                    
                }                                                        
                                                                        
                evt->dev.irq = irq_of_parse_and_map(node, 0);                                                        
                if (evt->dev.irq <= 0) {                                                        
                    pr_err("Can't parse IRQ\n");                                                    
                    goto err_mem;                                                    
                }                                                        
                                                                        
                clk_bus = of_clk_get_by_name(node, "bus");                                                        
                if (!IS_ERR(clk_bus))                                                        
                    clk_prepare_enable(clk_bus);                                                    
                                                                        
                clk_src = of_clk_get(node, 0);                                                        
                if (IS_ERR(clk_src)) {                                                        
                    pr_err("Can't get timer clock\n");                                                    
                    goto err_irq;                                                    
                }                                                        
                                                                        
                if (clk_prepare_enable(clk_src)) {                                                        
                    pr_err("Can't prepare clock\n");                                                    
                    goto err_clk_put_src;                                                    
                }                                                        
            /*                                                            
                这个地方是个13M的clock                                                        
                timer: timer@10008000 {                                                        
                    compatible = "mediatek,mt2712-timer",                                                    
                             "mediatek,mt6577-timer";                                                
                    reg = <0 0x10008000 0 0x80>;                                                    
                    interrupts = ;                                                    
                    clocks = <&system_clk>;                                                    
                    clock-names = "clk13m";                                                    
                };                                                        
                                                                        
                system_clk: dummy13m {                                                        
                    compatible = "fixed-clock";                                                    
                    clock-frequency = <13000000>;                                                    
                    #clock-cells = <0>;                                                    
                };                                                        
            */                                                            
                rate_src = clk_get_rate(clk_src);                                                        
                                                                        
                clk_evt = of_clk_get_by_name(node, "clk32k");                                // 是不是有32k clock                        
                if (!IS_ERR(clk_evt)) {                                                        
                    evt->clk32k_exist = true;                                                    
                    clk_prepare_enable(clk_evt);                                                    
                    rate_evt = clk_get_rate(clk_evt);                                                    
                } else {                                                        
                    rate_evt = rate_src;                                                    // 没有32k clock的话,使用rate_src
                }                                                        
                                                                        
                if (request_irq(evt->dev.irq, mtk_timer_interrupt,                                                        
                        IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {                        // 注册timer中断,关键参数evt                        
                    pr_err("failed to setup irq %d\n", evt->dev.irq);                                                    
                    if (evt->clk32k_exist)                                                    
                        goto err_clk_disable_evt;                                                
                    else                                                    
                        goto err_clk_disable_src;                                                
                }                                                        
                                                                        
                evt->ticks_per_jiffy = DIV_ROUND_UP(rate_evt, HZ);                            // 时钟为rate_evt,即1秒13M个tick,HZ是1秒内时钟中断数量,(rate_evt+(HZ-1))/HZ,得到1个jiffy中有多少个tick。                            
                                                                        
                /* Configure clock source */                                                        
                mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN,                                                        
                        TIMER_CLK_SRC_SYS13M, true);                                                
                clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC),                                                        
                        node->name, rate_src, 300, 32,                                                
                        clocksource_mmio_readl_up);                                                
                                                                        
                /* Configure clock event */                                                        
                if (evt->clk32k_exist)                                                        
                    mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT,                                                    
                            TIMER_CLK_SRC_RTC32K, false);                                            
                else                                                        
                    mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT,                                                    
                            TIMER_CLK_SRC_SYS13M, false);                                            
                clockevents_config_and_register(&evt->dev, rate_evt, 0x3,                                                        
                                0xffffffff);                                        
                                                                        
                mtk_timer_enable_irq(evt, GPT_CLK_EVT);                                                        
                                                                        
                return 0;                                                        
                                                                        
            err_clk_disable_evt:                                                            
                clk_disable_unprepare(clk_evt);                                                        
                clk_put(clk_evt);                                                        
            err_clk_disable_src:                                                            
                clk_disable_unprepare(clk_src);                                                        
                                                                        
            err_clk_put_src:                                                            
                clk_put(clk_src);                                                        
            err_irq:                                                            
                irq_dispose_mapping(evt->dev.irq);                                                        
            err_mem:                                                            
                iounmap(evt->gpt_base);                                                        
                of_address_to_resource(node, 0, &res);                                                        
                release_mem_region(res.start, resource_size(&res));                                                        
            err_kzalloc:                                                            
                kfree(evt);                                                        
                                                                        
                return -EINVAL;                                                        
            }                                                            
 

你可能感兴趣的:(Linux,Driver,Linux,Linux时钟子系统,Linux时钟子系统,嵌入式,arm)