cpuidle_governor_latency_req() is evaluated on every idle-state
selection.  It aggregates the per-CPU resume latency with the global
CPU latency and wakeup latency QoS limits by repeatedly calling
get_cpu_device() and pm_qos_read_value() cpu_latency_qos_limit()
cpu_wakeup_latency_qos_limit().

Use ftrace's function_graph, we can see:
parent:          do_idle
parent_total_ns: 36671010505
parent_count:    5994

SYMBOL                                                      TIME_NS    %ROOT  
%PARENT      COUNT
--------------------------------------------------------------------------------------------------
do_idle                                                 36671010505  100.00%  
100.00%       5994
  cpuidle_idle_call                                     35566528731   96.99%   
96.99%       8570
    call_cpuidle                                        35476606844   96.74%   
99.75%       8561
      cpuidle_enter                                     35472932468   96.73%   
99.99%       8526
    cpuidle_select                                         52097031    0.14%    
0.15%       8580
      menu_select                                          49555181    0.14%   
95.12%       8580
        tick_nohz_get_sleep_length                         28843887    0.08%   
58.21%       8570
        cpuidle_governor_latency_req                        9567488    0.03%   
19.31%       8580
        tick_nohz_tick_stopped                              2057031    0.01%    
4.15%      15695
    cpuidle_reflect                                        11427201    0.03%    
0.03%       8561
      menu_reflect                                          6579506    0.02%   
57.58%       8526
        tick_nohz_idle_got_tick                             2231559    0.01%   
33.92%       8526
      __sysvec_apic_timer_interrupt                          105520    0.00%    
0.92%          3
    tick_nohz_idle_stop_tick                                8279641    0.02%    
0.02%       1475
    ---- skip

The majority of the time spent in cpuidle_enter for CPUs entering
idle state has already been charged to the idle path. Among the
remaining contributors, cpuidle_governor_latency_req() accounts 
for a non-negligible portion of the overall latency.

Under the menu governor this shows up hot: ftrace data shows,
 cpuidle_governor_latency_req() accounts for about 19.9% of
menu_select() time (~1.9 us/call).  After caching the aggregated
value per CPU and invalidating via QoS notifiers, that share drops to
about 4.2% (~0.3 us/call), roughly a 6x reduction on this path.

The ftrace data before and after the optimization is shown below:
1) original
parent:          menu_select
parent_total_ns: 160492937
parent_count:    16718

SYMBOL                                                      TIME_NS    %ROOT  
%PARENT      COUNT
--------------------------------------------------------------------------------------------------
menu_select                                               160492937  100.00%  
100.00%      16718
  tick_nohz_get_sleep_length                              100262940   62.47%   
62.47%      16698
    tick_nohz_next_event                                   67891299   42.30%   
67.71%      16689
      rcu_needs_cpu                                         2825649    1.76%    
4.16%      16689
      timekeeping_max_deferment                             2380377    1.48%    
3.51%      15296
    hrtimer_next_event_without                             17865162   11.13%   
17.82%      15296
      hrtimer_bases_next_event_without                      2707631    1.69%   
15.16%      15296
      _raw_spin_lock_irqsave                                2461132    1.53%   
13.78%      15296
        native_queued_spin_lock_slowpath                        177    0.00%    
0.01%          1
      _raw_spin_unlock_irqrestore                           2364647    1.47%   
13.24%      15296
    can_stop_idle_tick                                      2906072    1.81%    
2.90%      16698
  cpuidle_governor_latency_req                             31988150   19.93%   
19.93%      16718
    get_cpu_device                                          4122502    2.57%   
12.89%      16718
    pm_qos_read_value                                       3427318    2.14%   
10.71%      16718
    cpu_latency_qos_limit                                   3005804    1.87%    
9.40%      16718
    cpu_wakeup_latency_qos_limit                            3005475    1.87%    
9.40%      16718
  tick_nohz_tick_stopped                                    4551981    2.84%    
2.84%      29496

%ROOT = share of menu_select; %PARENT = share of immediate caller (inclusive)

2) post-optimized
parent:          menu_select
parent_total_ns: 55428604
parent_count:    7626

SYMBOL                                                      TIME_NS    %ROOT  
%PARENT      COUNT
--------------------------------------------------------------------------------------------------
menu_select                                                55428604  100.00%  
100.00%       7626
  tick_nohz_get_sleep_length                               37464607   67.59%   
67.59%       7544
    tick_nohz_next_event                                   24076913   43.44%   
64.27%       7522
      get_next_timer_interrupt                             16332381   29.47%   
67.83%       5854
      rcu_needs_cpu                                         1489633    2.69%    
6.19%       7522
      timekeeping_max_deferment                              870851    1.57%    
3.62%       5586
    hrtimer_next_event_without                              6140153   11.08%   
16.39%       5586
      hrtimer_bases_next_event_without                       979000    1.77%   
15.94%       5586
      _raw_spin_lock_irqsave                                 808786    1.46%   
13.17%       5586
      _raw_spin_unlock_irqrestore                            785518    1.42%   
12.79%       5586
    can_stop_idle_tick                                      1583137    2.86%    
4.23%       7544
  cpuidle_governor_latency_req                              2321119    4.19%    
4.19%       7626
  tick_nohz_tick_stopped                                    1863015    3.36%    
3.36%      12751

%ROOT = share of menu_select; %PARENT = share of immediate caller (inclusive)

A self-test case is introduced in patch 5 to validate that the existing
functionality remains intact. This can be handled as a standalone task.

This series:
  - adds notifier APIs for the global CPU/wakeup latency QoS
  - lets cpuidle subscribe and maintain a per-CPU generation
  - invalidates only the affected CPU on per-CPU resume latency
    changes
  - caches the aggregated constraint in cpuidle_governor_latency_req()
  - adds a kselftest covering the three QoS input paths

Yaxiong Tian (5):
  pm: qos: add notifiers for CPU latency and wakeup latency QoS
  cpuidle: subscribe to global latency QoS notifiers
  cpuidle: invalidate latency gen on per-CPU resume QoS changes
  cpuidle: cache aggregated governor latency QoS constraint
  selftests/cpuidle: add latency_req QoS idle-state selection test

 drivers/cpuidle/cpuidle.c                     |  15 +-
 drivers/cpuidle/cpuidle.h                     |   2 +
 drivers/cpuidle/governor.c                    | 141 +++++++-
 include/linux/pm_qos.h                        |  23 ++
 kernel/power/qos.c                            |  50 +++
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/cpuidle/Makefile      |   6 +
 tools/testing/selftests/cpuidle/config        |   3 +
 .../cpuidle/cpuidle_latency_req_qos.py        | 331 ++++++++++++++++++
 tools/testing/selftests/cpuidle/settings      |   2 +
 10 files changed, 566 insertions(+), 8 deletions(-)
 create mode 100644 tools/testing/selftests/cpuidle/Makefile
 create mode 100644 tools/testing/selftests/cpuidle/config
 create mode 100755 tools/testing/selftests/cpuidle/cpuidle_latency_req_qos.py
 create mode 100644 tools/testing/selftests/cpuidle/settings

-- 
2.43.0

Reply via email to