lkml.org 
[lkml]   [2023]   [Feb]   [22]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/8] timers/nohz: Only ever update sleeptime from idle exit
    Date
    The idle and io sleeptime statistics appearing in /proc/stat can be
    currently updated from two sites: locally on idle exit and remotely
    by cpufreq. However there is no synchronization mechanism protecting
    concurrent updates. It is therefore possible to account the sleeptime
    twice, among all the possible broken scenarios.

    To prevent from breaking the sleeptime accounting source, restrict the
    sleeptime updates to the local idle exit site. If there is a delta to
    add since the last update, IO/Idle sleep time readers will now only
    compute the delta without actually writing it back to the internal idle
    statistic fields.

    This fixes a writer VS writer race. Note there are still two known
    reader VS writer races to handle. A subsequent patch will fix one.

    Reported-by: Yu Liao <liaoyu15@huawei.com>
    Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
    Cc: Hillf Danton <hdanton@sina.com>
    Cc: Yu Liao <liaoyu15@huawei.com>
    Cc: Ingo Molnar <mingo@kernel.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Wei Li <liwei391@huawei.com>
    Cc: Alexey Dobriyan <adobriyan@gmail.com>
    Cc: Mirsad Goran Todorovac <mirsad.todorovac@alu.unizg.hr>
    Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
    ---
    kernel/time/tick-sched.c | 103 ++++++++++++++++-----------------------
    1 file changed, 41 insertions(+), 62 deletions(-)

    diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
    index b0e3c9205946..9058b9eb8bc1 100644
    --- a/kernel/time/tick-sched.c
    +++ b/kernel/time/tick-sched.c
    @@ -637,31 +637,21 @@ static void tick_nohz_update_jiffies(ktime_t now)
    touch_softlockup_watchdog_sched();
    }

    -/*
    - * Updates the per-CPU time idle statistics counters
    - */
    -static void
    -update_ts_time_stats(int cpu, struct tick_sched *ts, ktime_t now, u64 *last_update_time)
    -{
    - ktime_t delta;
    -
    - if (ts->idle_active) {
    - delta = ktime_sub(now, ts->idle_entrytime);
    - if (nr_iowait_cpu(cpu) > 0)
    - ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
    - else
    - ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
    - ts->idle_entrytime = now;
    - }
    -
    - if (last_update_time)
    - *last_update_time = ktime_to_us(now);
    -
    -}
    -
    static void tick_nohz_stop_idle(struct tick_sched *ts, ktime_t now)
    {
    - update_ts_time_stats(smp_processor_id(), ts, now, NULL);
    + ktime_t delta;
    +
    + if (WARN_ON_ONCE(!ts->idle_active))
    + return;
    +
    + delta = ktime_sub(now, ts->idle_entrytime);
    +
    + if (nr_iowait_cpu(smp_processor_id()) > 0)
    + ts->iowait_sleeptime = ktime_add(ts->iowait_sleeptime, delta);
    + else
    + ts->idle_sleeptime = ktime_add(ts->idle_sleeptime, delta);
    +
    + ts->idle_entrytime = now;
    ts->idle_active = 0;

    sched_clock_idle_wakeup_event();
    @@ -674,6 +664,30 @@ static void tick_nohz_start_idle(struct tick_sched *ts)
    sched_clock_idle_sleep_event();
    }

    +static u64 get_cpu_sleep_time_us(struct tick_sched *ts, ktime_t *sleeptime,
    + bool compute_delta, u64 *last_update_time)
    +{
    + ktime_t now, idle;
    +
    + if (!tick_nohz_active)
    + return -1;
    +
    + now = ktime_get();
    + if (last_update_time)
    + *last_update_time = ktime_to_us(now);
    +
    + if (ts->idle_active && compute_delta) {
    + ktime_t delta = ktime_sub(now, ts->idle_entrytime);
    +
    + idle = ktime_add(*sleeptime, delta);
    + } else {
    + idle = *sleeptime;
    + }
    +
    + return ktime_to_us(idle);
    +
    +}
    +
    /**
    * get_cpu_idle_time_us - get the total idle time of a CPU
    * @cpu: CPU number to query
    @@ -691,27 +705,9 @@ static void tick_nohz_start_idle(struct tick_sched *ts)
    u64 get_cpu_idle_time_us(int cpu, u64 *last_update_time)
    {
    struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
    - ktime_t now, idle;
    -
    - if (!tick_nohz_active)
    - return -1;
    -
    - now = ktime_get();
    - if (last_update_time) {
    - update_ts_time_stats(cpu, ts, now, last_update_time);
    - idle = ts->idle_sleeptime;
    - } else {
    - if (ts->idle_active && !nr_iowait_cpu(cpu)) {
    - ktime_t delta = ktime_sub(now, ts->idle_entrytime);
    -
    - idle = ktime_add(ts->idle_sleeptime, delta);
    - } else {
    - idle = ts->idle_sleeptime;
    - }
    - }
    -
    - return ktime_to_us(idle);

    + return get_cpu_sleep_time_us(ts, &ts->idle_sleeptime,
    + !nr_iowait_cpu(cpu), last_update_time);
    }
    EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);

    @@ -732,26 +728,9 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time_us);
    u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
    {
    struct tick_sched *ts = &per_cpu(tick_cpu_sched, cpu);
    - ktime_t now, iowait;

    - if (!tick_nohz_active)
    - return -1;
    -
    - now = ktime_get();
    - if (last_update_time) {
    - update_ts_time_stats(cpu, ts, now, last_update_time);
    - iowait = ts->iowait_sleeptime;
    - } else {
    - if (ts->idle_active && nr_iowait_cpu(cpu) > 0) {
    - ktime_t delta = ktime_sub(now, ts->idle_entrytime);
    -
    - iowait = ktime_add(ts->iowait_sleeptime, delta);
    - } else {
    - iowait = ts->iowait_sleeptime;
    - }
    - }
    -
    - return ktime_to_us(iowait);
    + return get_cpu_sleep_time_us(ts, &ts->iowait_sleeptime,
    + nr_iowait_cpu(cpu), last_update_time);
    }
    EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);

    --
    2.34.1
    \
     
     \ /
      Last update: 2023-03-27 00:32    [W:4.213 / U:0.272 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site