背景:用户程序(名称为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 */