背景:用户程序(名称为fa_pmu.test,pid为14368)开始运行在CPU0 上,通过调用sched_setaffinity 调度至CPU2 上,CPU2 是nohz_full,isolated 的,也就是说默认没有任何程序运行,并通过在CPU2 上通过ftrace 查看地切换过程。
问题:根据nohz_full 原理,用户程序(fa_pmu.test)应该不再受到时钟中断的干扰,但从程序表现来看还是有中断,通过/proc/interrupts 的记录应该就是时钟中断。
结论:(在给出具体分析前,先给出结论。)在测试环境(内核Linux5.4.*)中,用户程序的系统调用退出时并不能保证nohz_full 功能使能,需要在下一次时钟中断的时候才使能nohz_full。也就是说安全起见,实时任务需要在系统调用后忙等一段时间,以消除遗留时钟中断影响。除了忙等,经测试,Linux 6.7+以上版本已经解决该问题。
分析步骤如下:
1 运行在idle 状态
CPU2 默认没有任务程序运行,处于idle 状态,
static void do_idle(void)
{
// …
while(!need_resched()) {
//…
cpuidle_idle_call();
}
//..
tick_nohz_idle_exit();
//..
schedle_idle();
//..
}
程序运行在do_idle()->cpuidle_idle_call()->arch_cpu_idle()->x86_idle() 中
x86_idle 实际调用的是mwait_idle 还是poll_idle 需看配置。
2 退出Idle(tick_nohz_idle_exit())
CPU2 退出 idle while 循环后,首先执行的是tick_nohz_idle_exit,
该函数主要执行:
1)tick_nohz_stop_idle()->update_ts_time_stats() 更新调度统计信息
2)__tick_nohz_idle_restart_tick()->tick_nohz_restart_sched_tick() 重启tick
void tick_nohz_idle_exit(void)
{
//…
if (idle_active || tick_stopped)
now = ktime_get();
if (idle_active)
tick_nohz_stop_idle(ts, now);
if (tick_stopped)
__tick_nohz_idle_restart_tick(ts, now);
//…
}
重启tick(tick_nohz_restart_sched_tick()->tick_nohz_restart),取消调度时钟(hrtimer_cancel()),设置下一次tick,注意这里会导致产生下一次tick。
3 切换任务(schedule_idle())
用户程序会在schedule_idle() 中切换运行。
void __sched schedule_idle(void)
{
WARN_ON_ONCE(current->state);
do {
__schedule(false);
} while (need_resched());
}
schedule_idle()->__sched() 切换过程如下:
__sched()->rcu_note_context_switch() 处理rcu 相关内容
__sched()->pick_next_task() 将选择下一个任务进行处理
__sched()->context_switch() 将完成任务切换,比如切换mm(switch_mm_irqs_off())寄存器和栈(switch_to())
在这之后用户态任务切换至fa_pmu.test,从此之后将从fa_pmu.test 的内核栈开始执行。
__sched()->context_switch()->finish_task_switch() 完成任务切换的善后工作。
4 切换完成后,将从fa_pmu.test 的系统调用(sched_setaffinity)返回
# tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | 2) 0.469 us | __srcu_read_lock(); 2) | rcu_irq_enter_irqson() { 2) | rcu_irq_enter() { 2) 0.149 us | rcu_dynticks_eqs_exit(); 2) 0.359 us | } 2) 0.568 us | } 2) | /* cpu_idle: state=4294967295 cpu_id=2 */ 2) | rcu_irq_exit_irqson() { 2) 0.106 us | rcu_irq_exit(); 2) 0.305 us | } 2) 0.103 us | __srcu_read_unlock(); 2) $ 1751875 us | } /* mwait_idle */ 2) $ 1751875 us | } /* arch_cpu_idle */ 2) $ 1751875 us | } /* default_idle_call */ 2) | rcu_idle_exit() { 2) | rcu_eqs_exit.constprop.0() { 2) 0.099 us | rcu_dynticks_eqs_exit(); 2) 0.296 us | } 2) 0.522 us | } 2) 0.107 us | arch_cpu_idle_exit(); 2) | tick_nohz_idle_exit() { 2) 0.184 us | ktime_get(); 2) | update_ts_time_stats() { 2) 0.135 us | nr_iowait_cpu(); 2) 0.329 us | } 2) | tick_nohz_restart_sched_tick() { 2) 0.119 us | timer_clear_idle(); 2) 0.132 us | calc_load_nohz_stop(); 2) | hrtimer_cancel() { 2) | hrtimer_try_to_cancel() { 2) 0.202 us | hrtimer_active(); 2) 0.390 us | } 2) 0.576 us | } 2) 0.104 us | hrtimer_forward(); 2) | hrtimer_start_range_ns() { 2) | lock_hrtimer_base() { 2) 0.170 us | _raw_spin_lock_irqsave(); 2) 0.354 us | } 2) | enqueue_hrtimer() { 2) | /* hrtimer_start: hrtimer=00000000b4a6017d function=tick_sched_timer expires=71074629358859 softexpires=71074629358859 mode=0xa */ 2) 0.232 us | } 2) | hrtimer_reprogram() { 2) | tick_program_event() { 2) | clockevents_switch_state() { 2) | lapic_timer_set_oneshot() { 2) 0.231 us | __setup_APIC_LVTT(); 2) 0.421 us | } 2) 0.704 us | } 2) | clockevents_program_event() { 2) 0.114 us | ktime_get(); 2) | lapic_next_deadline() { 2) | do_trace_write_msr() { 2) | /* write_msr: 6e0, value e8c9fcda004c */ 2) 0.194 us | } 2) 0.444 us | } 2) 0.847 us | } 2) 2.019 us | } 2) 2.206 us | } 2) 0.116 us | _raw_spin_unlock_irqrestore(); 2) 3.391 us | } 2) 4.891 us | } 2) 0.178 us | tick_nohz_account_idle_ticks.isra.0(); 2) 6.067 us | } 2) 0.098 us | sched_ttwu_pending(); 2) | schedule_idle() { 2) | rcu_note_context_switch() { 2) | /* rcu_utilization: Start context switch */ 2) 0.099 us | rcu_preempt_deferred_qs(); 2) | /* rcu_utilization: End context switch */ 2) 0.477 us | } 2) 0.138 us | _raw_spin_lock(); 2) 0.098 us | put_prev_task_idle(); 2) 0.172 us | pick_next_task_stop(); 2) 0.115 us | pick_next_task_dl(); 2) | pick_next_task_rt() { 2) | update_rt_rq_load_avg() { 2) 0.097 us | __accumulate_pelt_segments(); 2) 0.390 us | } 2) 0.618 us | } 2) | /* sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=fa_pmu.test next_pid=14368 next_prio=0 */ 2) | switch_mm_irqs_off() { 2) 0.117 us | __srcu_read_lock(); 2) | rcu_irq_enter_irqson() { 2) 0.110 us | rcu_irq_enter(); 2) 0.310 us | } 2) | /* tlb_flush: pages:-1 reason:flush on task switch (0) */ 2) | rcu_irq_exit_irqson() { 2) 0.208 us | rcu_irq_exit(); 2) 0.403 us | } 2) 0.108 us | __srcu_read_unlock(); 2) 1.738 us | } 2) | do_trace_write_msr() { 2) | /* write_msr: c0000100, value 4d8880 */ 2) 0.215 us | } ------------------------------------------ 2) <idle>-0 => fa_pmu.-14368 ------------------------------------------ 2) | finish_task_switch() { 2) | vtime_common_task_switch() { 2) | vtime_account_idle() { 2) 0.110 us | get_vtime_delta(); 2) 0.310 us | } 2) 0.121 us | arch_vtime_task_switch(); 2) 0.775 us | } 2) 0.111 us | _raw_spin_unlock_irq(); 2) | __mmdrop() { 2) | pgd_free() { 2) 0.107 us | _raw_spin_lock(); 2) 0.100 us | _raw_spin_unlock(); 2) | free_pages() { 2) | free_pages.part.0() { 2) | __free_pages() { 2) | free_unref_page() { 2) | free_pcp_prepare() { 2) | /* mm_page_free: page=000000009c017469 pfn=3247560 order=0 */ 2) | __memcg_kmem_uncharge() { 2) | __memcg_kmem_uncharge_memcg() { 2) | page_counter_uncharge() { 2) | page_counter_cancel() { 2) 0.095 us | propagate_protected_usage(); 2) 0.311 us | } 2) | page_counter_cancel() { 2) 0.099 us | propagate_protected_usage(); 2) 0.300 us | } 2) | page_counter_cancel() { 2) 0.096 us | propagate_protected_usage(); 2) 0.303 us | } 2) | page_counter_cancel() { 2) 0.107 us | propagate_protected_usage(); 2) 0.309 us | } 2) 1.706 us | } 2) | page_counter_uncharge() { 2) | page_counter_cancel() { 2) 0.116 us | propagate_protected_usage(); 2) 0.325 us | } 2) | page_counter_cancel() { 2) 0.117 us | propagate_protected_usage(); 2) 0.317 us | } 2) | page_counter_cancel() { 2) 0.116 us | propagate_protected_usage(); 2) 0.317 us | } 2) | page_counter_cancel() { 2) 0.128 us | propagate_protected_usage(); 2) 0.329 us | } 2) 1.763 us | } 2) 3.772 us | } 2) 4.062 us | } 2) 4.347 us | } 2) 0.109 us | free_unref_page_prepare.part.0(); 2) 0.198 us | free_unref_page_commit.isra.0(); 2) 5.042 us | } 2) 5.227 us | } 2) 5.412 us | } 2) 5.598 us | } 2) 6.175 us | } 2) 0.098 us | destroy_context_ldt(); 2) | kmem_cache_free() { 2) 0.134 us | __slab_free(); 2) | /* kmem_cache_free: call_site=return_to_handler+0x0/0x27 ptr=00000000d1cf1f6d */ 2) 0.501 us | } 2) 7.206 us | } 2) 0.128 us | __tick_nohz_task_switch(); 2) 8.784 us | } 2) + 41.532 us | } /* cpu_stop_queue_work */ 2) | wait_for_completion() { 2) 0.124 us | _raw_spin_lock_irq(); 2) 0.098 us | _raw_spin_unlock_irq(); 2) 0.518 us | } 2) + 42.835 us | } /* stop_one_cpu */ 2) + 50.017 us | } /* __set_cpus_allowed_ptr */ 2) | cpuset_cpus_allowed() { 2) 0.136 us | _raw_spin_lock_irqsave(); 2) 0.144 us | __rcu_read_lock(); 2) 0.104 us | __rcu_read_unlock(); 2) 0.107 us | _raw_spin_unlock_irqrestore(); 2) 1.048 us | } 2) + 54.746 us | } /* sched_setaffinity */ 2) + 55.299 us | } /* __x64_sys_sched_setaffinity */