使用ftrace定位xenomai实时内核中断延时的一个实例

1,背景

1)硬件:飞腾ft2000ahk,双核,1.8G

2)软件:

   内核:4.14.4

   桌面:银河麒麟sp2

   实时方案:Xenomai 3.1

3)描述:麒麟内核4.14.4版本加xenomai的实时方案,硬件环境为飞腾ft2000ahk处理器开发板,测试标准是在空载情况下,cyclictest的延时小于50us。

2,问题描述

运行cyclictest -p 90 -m -c 0 -i 10 -n -h 100 -q -l 10000000对系统实时性进行验证,要求系统空载的情况下,所得延时小于50us,但实际测试值通常为60us~100us。

3,Debug过程
1)Ftrace irqsoff
步骤:
echo irqsoff > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
cyclictest -p 90 -m -c 0 -i 10 -n -h 100 -q -l 10000000
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace > xxx.txt
Note:cyclictest测试用例timer周期设置为10us,但时间间隔太短了,日志就不大容易看清楚过程,所以我有时候使用-i 100来调试,这样在log中能够看到清晰的代码过程和时间戳。
 
日志1 -- -i为10us的日志:
<...>-1874    0d..3 13767414us : CoBaLt_clock_nanosleep <-handle_head_syscall
   <...>-1874    0d..3 13767416us : __cobalt_clock_nanosleep <-CoBaLt_clock_nanosleep
   <...>-1874    0d..3 13767417us : ___xnlock_get <-__cobalt_clock_nanosleep
   <...>-1874    0d..3 13767418us : xnthread_suspend <-__cobalt_clock_nanosleep
   <...>-1874    0d..3 13767419us : ___xnlock_get <-xnthread_suspend
   <...>-1874    0d..3 13767420us : xntimer_start <-xnthread_suspend
   <...>-1874    0d..3 13767421us : xnclock_core_ns_to_ticks <-xntimer_start
 
   <...>-1874    0d..3 13767422us : ___xnlock_put <-__cobalt_clock_nanosleep
   <...>-1874    0d..3 13767424us : __ipipe_restore_head <-__cobalt_clock_nanosleep
 
   <...>-1874    0d..3 13767425us : ipipe_fastcall_hook <-el0_svc_naked
   <...>-1874    0d..3 13767427us : handle_head_syscall <-ipipe_fastcall_hook
 
分析(代码流程分析):
cobalt_clock_nanosleep
    xnthread_suspend
        xntimer_start
            xnclock_core_ns_to_ticks
可以看出来,很多时候,在xntimer_start函数里,由于时间比较短,就直接返回了,这样并没有引起进程上下文的切换。有的时候也会发生进程上下文的切换,切换的过程见100us的日志。
 
日志1 -- -i为100us的日志:
<...>-1874    0d..3 13773888us : CoBaLt_clock_nanosleep <-handle_head_syscall
   <...>-1874    0d..3 13773890us : __cobalt_clock_nanosleep <-CoBaLt_clock_nanosleep
   <...>-1874    0d..3 13773891us : ___xnlock_get <-__cobalt_clock_nanosleep
   <...>-1874    0d..3 13773892us : xnthread_suspend <-__cobalt_clock_nanosleep
   <...>-1874    0d..3 13773893us : ___xnlock_get <-xnthread_suspend
   <...>-1874    0d..3 13773894us : xntimer_start <-xnthread_suspend
   <...>-1874    0d..3 13773895us : xnclock_core_ns_to_ticks <-xntimer_start
   <...>-1874    0d..3 13773896us : xntimer_enqueue_and_program <-xntimer_start
   <...>-1874    0d..3 13773897us : xntimer_heading_p <-xntimer_enqueue_and_program
   <...>-1874    0d..3 13773899us : xnclock_core_local_shot <-xntimer_enqueue_and_program
   <...>-1874    0d..3 13773900us : ipipe_timer_set <-xnclock_core_local_shot
   <...>-1874    0d..3 13773901us : arch_timer_set_next_event_phys <-ipipe_timer_set  
                                     设置时钟硬件寄存器
 
<...>-1874    0d..3 13773902us : ___xnsched_run <-xnthread_suspend
   <...>-1874    0d..3 13773903us : xnarch_escalate <-___xnsched_run
   <...>-1874    0d..3 13773904us : ___xnlock_get <-___xnsched_run
   <...>-1874    0d..3 13773905us : xnsched_pick_next <-___xnsched_run
   <...>-1874    0d..3 13773907us : xnarch_switch_to <-___xnsched_run
   <...>-1874    0d..3 13773908us : fpsimd_thread_switch <-__switch_to
   <...>-1874    0d..3 13773909us : hw_breakpoint_thread_switch <-__switch_to
   <...>-1874    0d..3 13773910us : uao_thread_switch <-__switch_to
                                      切换上下文
 
                                 gic_handle_irq <-el1_irq
                                      时钟中断
<...>-1872    0d..3 13773914us : __ipipe_exit_irq <-__ipipe_grab_irq
   <...>-1872    0d..3 13773915us : irq_find_mapping <-gic_handle_irq
   <...>-1872    0d..3 13773916us : __ipipe_grab_irq <-gic_handle_irq
   <...>-1872    0d..3 13773917us : __ipipe_dispatch_irq <-__ipipe_grab_irq
 
 
   <...>-1872    0d..3 13773918us : irq_to_desc <-__ipipe_dispatch_irq
   <...>-1872    0d..3 13773920us : __ipipe_ack_hrtimer_irq <-__ipipe_dispatch_irq
   <...>-1872    0d..3 13773921us : __ipipe_ack_fasteoi_irq <-__ipipe_ack_hrtimer_irq
   <...>-1872    0d..3 13773922us : gic_hold_irq <-__ipipe_ack_fasteoi_irq
   <...>-1872    0d..3 13773923us : arch_itimer_ack_phys <-__ipipe_ack_hrtimer_irq
   <...>-1872    0d..3 13773924us : __ipipe_end_fasteoi_irq <-__ipipe_ack_hrtimer_irq
   <...>-1872    0d..3 13773925us : gic_release_irq <-__ipipe_end_fasteoi_irq
   <...>-1872    0d..3 13773926us : dispatch_irq_head <-__ipipe_dispatch_irq
                                  时钟中断处理函数xnintr_core_clock_handle
   <...>-1872    0d..3 13773927us : xnintr_core_clock_handler <-dispatch_irq_head
   <...>-1872    0d..3 13773928us : ___xnlock_get <-xnintr_core_clock_handler
   <...>-1872    0d..3 13773930us : xnclock_tick <-xnintr_core_clock_handler
   <...>-1872    0d..3 13773931us : timeout_handler <-xnclock_tick
 
 
   <...>-1872    0d..3 13773932us : xnthread_resume <-timeout_handler
   <...>-1872    0d..3 13773933us : ___xnlock_get <-xnthread_resume
   <...>-1872    0d..3 13773934us : __xntimer_stop <-xnthread_resume
   <...>-1872    0d..3 13773935us : xnclock_core_local_shot <-__xntimer_stop
   <...>-1872    0d..3 13773936us : xnclock_core_local_shot <-xnclock_tick
   <...>-1872    0d..3 13773937us : ___xnlock_put <-xnintr_core_clock_handler
   <...>-1872    0d..3 13773939us : ___xnsched_run <-xnintr_core_clock_handler
   <...>-1872    0d..3 13773940us : xnarch_escalate <-___xnsched_run
   <...>-1872    0d..3 13773941us : ___xnlock_get <-___xnsched_run
   <...>-1872    0d..3 13773942us : xnsched_pick_next <-___xnsched_run
   <...>-1872    0d..3 13773943us : xnarch_switch_to <-___xnsched_run
   <...>-1872    0d..3 13773944us : fpsimd_thread_switch <-__switch_to
   <...>-1872    0d..3 13773946us : hw_breakpoint_thread_switch <-__switch_to
   <...>-1872    0d..3 13773947us : uao_thread_switch <-__switch_to  
                                  抢占和切换上下文
 
   <...>-1874    0d..3 13773948us : xnthread_switch_fpu <-___xnsched_run
   <...>-1874    0d..3 13773949us : ___xnlock_put <-__cobalt_clock_nanosleep
   <...>-1874    0d..3 13773950us : __ipipe_restore_head <-__cobalt_clock_nanosleep
                                 回到__cobalt_clock_nanosleep中
 
分析(代码流程分析):
cobalt_clock_nanosleep
    xnthread_suspend
        xntimer_start
            xnclock_core_ns_to_ticks
                xntimer_enqueue_and_program
                    xnclock_program_shot()
                        xnclock_core_local_shot
                            ipipe_timer_set
 
gic_handle_irq
    ipipe_handle_domain_irq -- _ipipe_grab_irq
        irq_find_mapping
        __ipipe_grab_irq(hrtimer_irq)
            __ipipe_dispatch_irq
                irq_to_desc
                ack_fn -- __ipipe_ack_hrtimer_irq --ipipe_ack
                                __ipipe_ack_fasteoi_irq
                                    irq_hold -- gic_hold_irq
                                timer_ack -- arch_itimer_ack_phys
                                ipipe_end -- __ipipe_end_fasteoi_irq
                                    gic_release_irq
                dispatch_irq_head
                    handler -- xnintr_core_clock_handler
                        ___xnlock_get
                            xnclock_tick
                                timeout_handler
                                    xnthread_resume
                                        ....__cobalt_clock_nanosleep            
 
                or
                __ipipe_sync_pipeline
                    __ipipe_do_sync_pipeline
                        __ipipe_sync_stage -- __ipipe_do_sync_stage
 
 
2)Ftrace events
方案
在cobalt-core.h中定义两个trace event。
在__cobalt_clock_nanosleep函数中增加两个检测点,如下:
int __cobalt_clock_nanosleep(clockid_t clock_id, int flags,
                 const struct timespec *rqt,
                 struct timespec *rmt)
{
.............
增加检测点in
xnthread_suspend(cur, XNDELAY, timeout + 1,
             clock_flag(flags, clock_id), NULL);
增加检测点out
两个检测点的时间戳应该相差100us(我通常使用100us的间隔来调试这个问题)
............
 
}
问题
一开始使用相同的步骤抓取event的日志,如下:
echo irqsoff > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
cyclictest -p 90 -m -c 0 -i 10 -n -h 100 -q -l 10000000
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace > xxx.txt
 
但cyclictest进程的event并没有在日志中显示出来,到现在也不知道为什么。
 
后来写邮件问了一下社区,推荐使用trace-cmd -e ‘all’或trace-cmd -e “cobalt*”,这样确实是可以显示cyclictest进程的event了,但里面也充斥这大量的trace-cmd本身的日志,因为trace-cmd会一边从trace文件抓log,一遍将log存到trace.dat里,所以日志中有很多trace-cmd进程分配内存,写文件这样的日志,会影响对cyclictest日志的分析。
 
我又仔细看来一下cyclictest本身带有的ftrace测试功能,发现-b参数可以打开ftrace event,同时还可以设置一个tracelimit,也就是一旦超过这个延时,cyclictest进程马上停止,这样,日志文件最后的部分,应该就是出问题的部分。
 
cyclictest -p 90 -m -c 0 -i 10 -n -h 100 -q -l 1000000 -b 50
 
正常的日志
cyclicte-3924    1...2 8606754us : cobalt_timer_aaaaa_in: timer=ffffff800998b898
                                检测点in
cyclicte-3924    1...2 8606756us : cobalt_thread_suspend: pid=3924 mask=0x4 timeout=3338491094741 timeout_mode=1 wchan=          (null)
cyclicte-3924    1...2 8606757us : cobalt_timer_start: timer222=ffffff800998b898(cyclictest) value222=3338491094741 interval222=0 mode222=abs
cyclicte-3924    1...2 8606758us : cobalt_schedule: status=0x10000000
cyclicte-3924    1...2 8606760us : cobalt_switch_context: prev_name=cyclictest  
prev_pid=3924 prev_prio=90 prev_state=0x4c044 ==> next_name=ROOT/1 next_pid=0  
next_prio=-1
                                 放弃cpu,切换上下文
  -0       1...1 8606764us : cpu_idle: state=4294967295 cpu_id=1
  -0       1d..1 8606774us+: cpu_idle: state=1 cpu_id=1
 
  -0       1...1 8606838us : cobalt_clock_entry: clock_irq=4
                                中断来了
  -0       1...1 8606839us : cobalt_timer_expire: timer=ffffff800998b898
  -0       1...1 8606840us : cobalt_thread_resume: name=cyclictest pid=3924 mask=0x4
  -0       1...1 8606841us : cobalt_timer_stop: timer=ffffff800998b898
  -0       1...1 8606842us : cobalt_clock_exit: clock_irq=4
  -0       1...1 8606844us : cobalt_schedule: status=0x10000000
  -0       1...1 8606845us : cobalt_switch_context: prev_name=ROOT/1 prev_pid=0 prev_prio=-1 prev_state=0x18008 ==> next_name=cyclictest next_pid=3924 next_prio=90
                                cyclictest抢占任务,回到函数cobalt_clock_nanosleep,约100us
cyclicte-3924    1...2 8606847us : cobalt_timer_aaaaa_out: timer=ffffff800998b898
 
异常的日志
  -0       1dns2 136867724us : cobalt_clock_entry: clock_irq=4
                                   时钟中断
  -0       1dns2 136867725us : cobalt_timer_expire: timer=ffffff800997b898
  -0       1dns2 136867727us : cobalt_thread_resume: name=cyclictest pid=1731 mask=0x4
  -0       1dns2 136867728us : cobalt_timer_stop: timer=ffffff800997b898
  -0       1dns2 136867729us : cobalt_clock_exit: clock_irq=4
  -0       1dns2 136867731us : cobalt_schedule: status=0x10000000
  -0       1dns2 136867732us : cobalt_switch_context: prev_name=ROOT/1 prev_pid=0 prev_prio=-1 prev_state=0x18008 ==> next_name=cyclictest next_pid=1731 next_prio=90
cyclicte-1731    1d..2 136867735us : cobalt_timer_aaaaa_out: timer=ffffff800997b898                  
cyclicte-1731    1d..2 136867736us : cobalt_head_sysexit: result=0
cyclicte-1731    1d..2 136867739us : cobalt_head_sysentry: syscall=clock_nanosleep
cyclicte-1731    1d..2 136867741us : cobalt_clock_nanosleep: clock_id=1 flags=0x1(TIMER_ABSTIME) rqt=(162.050295680)
cyclicte-1731    1d..2 136867742us : cobalt_timer_aaaaa_in: timer=ffffff800997b898
                                  检测点in
cyclicte-1731    1d..2 136867743us : cobalt_thread_suspend: pid=1731 mask=0x4 timeout=162050295681 timeout_mode=1 wchan=          (null)
cyclicte-1731    1d..2 136867744us : cobalt_timer_start: timer222=ffffff800997b898(cyclictest) value222=162050295681 interval222=0 mode222=abs
cyclicte-1731    1d..2 136867746us : cobalt_schedule: status=0x10000000
cyclicte-1731    1d..2 136867747us+: cobalt_switch_context: prev_name=cyclictest prev_pid=1731 prev_prio=90 prev_state=0x4c044 ==> next_name=ROOT/1 next_pid=0 next_prio=-1
                                  放弃cpu,切换上下文
  -0       1.ns1 136867758us : softirq_exit: vec=7 [action=SCHED]
  -0       1.ns1 136867759us : softirq_entry: vec=9 [action=RCU]
  -0       1.ns1 136867761us : rcu_utilization: Start RCU core
  -0       1.ns1 136867768us : rcu_utilization: End RCU core
  -0       1.ns1 136867769us : softirq_exit: vec=9 [action=RCU]
  -0       1.n.1 136867775us : cpu_idle: state=4294967295 cpu_id=1
  -0       1dn.1 136867782us : rcu_utilization: Start context switch
  -0       1dn.1 136867784us : rcu_utilization: End context switch
  -0       1d..2 136867787us : sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=kworker/1:1 next_pid=20 next_prio=120
kworker/-20      1.... 136867797us : workqueue_execute_start: work struct ffffffc0568dd180: function radeon_fence_check_lockup
kworker/-20      1.... 136867803us : workqueue_execute_end: work struct ffffffc0568dd180
kworker/-20      1.... 136867806us+: workqueue_execute_start: work struct ffffffc0568dcf10: function radeon_fence_check_lockup
kworker/-20      1.... 136867870us : cobalt_clock_entry: clock_irq=4                 ===应该在824所有来中断,晚了46us,很有可能就是kworker里的radeon_fence_check_lockup
导致的中断延时到来
kworker/-20      1.... 136867871us : cobalt_timer_expire: timer=ffffff800997b898
kworker/-20      1.... 136867873us : cobalt_thread_resume: name=cyclictest pid=1731 mask=0x4
kworker/-20      1.... 136867874us : cobalt_timer_stop: timer=ffffff800997b898
kworker/-20      1.... 136867875us : cobalt_clock_exit: clock_irq=4
kworker/-20      1.... 136867877us : cobalt_schedule: status=0x10000000
kworker/-20      1.... 136867878us : cobalt_switch_context: prev_name=ROOT/1 prev_pid=0 prev_prio=-1 prev_state=0x18008 ==> next_name=cyclictest next_pid=1731 next_prio=90
cyclicte-1731    1...2 136867880us : cobalt_timer_aaaaa_out: timer=ffffff800997b898
                                   回到cobalt_clock_nanosleep
cyclicte-1731    1...2 136867882us+: cobalt_head_sysexit: result=0
cyclicte-1731    1...2 136867912us : cobalt_head_sysentry: syscall=write
cyclicte-1731    1...2 136867917us : cobalt_fd_write_status: device=          (null) fd=5 pid=1731 comm=cyclictest
cyclicte-1731    1...2 136867919us : cobalt_head_sysexit: result=-9
cyclicte-1731    1...2 136867922us : cobalt_shadow_gorelax: reason=syscall
cyclicte-1731    1...2 136867924us : cobalt_lostage_request: request=wakeup pid=1731 comm=cyclictest
cyclicte-1731    1...2 136867925us : cobalt_thread_suspend: pid=1731 mask=0x80 timeout=0 timeout_mode=0 wchan=          (null)
cyclicte-1731    1...2 136867927us : cobalt_schedule: status=0x10000000
 
分析
Irqsoff把代码的运行路径打印得很清楚,有助于帮助我们了解代码的逻辑,但并没有给出具体是哪个函数或事件导致的延时。
在event的log中,可以清楚的看到中断晚到了将近50us,那么在两个检测点中发生的event就是值得怀疑的,我找到了radeon_fence_check_lockup,cpu1在中断到来之前正在运行这个软中断。
对radeon不是很熟悉,简单的分析了一下,里面有spin_lock_irqsave,存在关闭本地中断的可能,导致时钟中断延误。
此外,使用ipipe的frozen log,也找到了radeon_fence_check_lockup的问题。
:  +func                -128      0.862  radeon_fence_check_lockup+0x24 (process_one_work+0x134)
:  +func                -128      0.960  down_read_trylock+0x14 (radeon_fence_check_lockup+0x54)
:  +func                -127+   2.098  radeon_fence_activity+0x34 (radeon_fence_check_lockup+0x78)
:  +func                -125      0.823  si_gfx_is_lockup+0x18 (radeon_fence_check_lockup+0xb4)
+func                -124!  59.078  si_gpu_check_soft_reset+0x14 (si_gfx_is_lockup+0x20)
:| +func                 -65+   1.019  gic_handle_irq+0x28 (el1_irq+0xc0)
:| +func                 -64      0.843  irq_find_mapping+0x1c (gic_handle_irq+0x6c)

可以看出si_gpu_check_soft_reset消耗了很多cpu时间,进一步看看si_gpu_check_soft_reset,调用了RREG32 -- r100_mm_rreg -- r100_mm_rreg_slow中有关中断的行为。
 
4,修复方案
先简单的把radeon_fence_check_lockup变为空函数,延时果然从60,70us降到了10us到20us,radeon涉及到显示,目测对显示没有什么影响。
接下来需要阅读一下radeon的代码,然后在根据代码逻辑看看radeon_fence_check_lockup的优化方案
 
5,其他
1)printk只能看看代码运行路径,不能用来调试时间,一个是时间戳不准,另外,串口打印本身也耗费时间。
2)如果cyclictest运行在cpu0上,为什么cyclictest不能在cpu0的中断关闭的时候,迁移到一个cpu1上进行抢占,里面涉及到hrtimer的硬件和cpu中断的关系,ipipe如何分配中断,以及xenomai如何对进程进行迁移(migration)。
3)Xenomai还是会被非实时域影响,存在隐患。
 
6,参考文档
https://www.cnblogs.com/arnoldlu/p/7211249.html
https://segmentfault.com/a/1190000020940236?utm_source=tag-newest
http://www.360doc.com/content/16/0126/13/10366845_530659813.shtml
https://blog.csdn.net/wukongmingjing/article/details/81986155
https://events.static.linuxfound.org/slides/2010/linuxcon_japan/linuxcon_jp2010_rostedt.pdf
https://www.cnblogs.com/arnoldlu/p/9014365.html
https://www.cnblogs.com/yangykaifa/p/7238419.html
https://www.ibm.com/developerworks/cn/linux/1609_houp_ftrace/

你可能感兴趣的:(使用ftrace定位xenomai实时内核中断延时的一个实例)