nohz_full CPU 上的一次时钟中断

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