From nobody Fri Sep 25 06:03:57 2026 Received: from mxct.zte.com.cn (mxct.zte.com.cn [183.62.165.209]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D5523CA4AF; Wed, 16 Sep 2026 06:09:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=183.62.165.209 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789538981; cv=none; b=Ur8Sf7RdGbfA3lPusA8cTkwYYhv+MlccN1x5hDQ4GytRYKWRoRUVxZbly3a3N11eK8GcB/2biI5+BMuaVF51/f4BMqytCgjhTg1c11rqxzFLuxx6Vk57NZCHOpB/IYC+RrIwgr4Sf0+v4pd6zt7Y9Qqf8+GZLYIC0ogyO7of/90= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789538981; c=relaxed/simple; bh=vDErkoi7e/5qWch4mGVd8lRjKHPnQXY4oat/gseFrM8=; h=Message-ID:Date:Mime-Version:From:To:Cc:Subject:Content-Type; b=DaOj29pV3QJ1yLc6NJZqlk0vUHTebdcu12bos5XLmkte4oCmWYExMXy5+8m2Mz+JSeif1lpsr9i+9Jtzzi31EjeLvR+R673NEEFvJaaYKbnYuSHL+lyqyZ99WmB6pGC21nt8jUuHj/Ofk8z8luY0aeutXkd8bR2+qSyHq9DRFRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn; spf=pass smtp.mailfrom=zte.com.cn; arc=none smtp.client-ip=183.62.165.209 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=zte.com.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zte.com.cn Received: from mse-fl1.zte.com.cn (unknown [10.5.228.132]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mxct.zte.com.cn (FangMail) with ESMTPS id 4hl7l049Gmz4xPSq; Wed, 16 Sep 2026 14:09:32 +0800 (CST) Received: from xaxapp01.zte.com.cn ([10.88.99.176]) by mse-fl1.zte.com.cn with SMTP id 68G69ONI001113; Wed, 16 Sep 2026 14:09:25 +0800 (+08) (envelope-from hu.shengming@zte.com.cn) Received: from mapi (xaxapp02[null]) by mapi (Zmail) with MAPI id mid32; Wed, 16 Sep 2026 14:09:26 +0800 (CST) X-Zmail-TransId: 2afa6aaa329631a-a143b X-Mailer: Zmail v1.0 Message-ID: <20260916140926740-DCokXh6dAgtqDn1EWdCh@zte.com.cn> Date: Wed, 16 Sep 2026 14:09:26 +0800 (CST) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 From: To: , , Cc: , , , Subject: =?UTF-8?B?W1BBVENIIHYyXSBjcHVmcmVxOiBjb25zZXJ2YXRpdmU6IFJlc3RyaWN0IGRlZmVycmVkIGRvd25zY2FsaW5nIHRvIGxvdyBsb2Fk?= X-MAIL: mse-fl1.zte.com.cn 68G69ONI001113 X-TLS: YES X-ENVELOPE-SENDER: hu.shengming@zte.com.cn X-SOURCE-IP: 10.5.228.132 unknown Wed, 16 Sep 2026 14:09:32 +0800 X-CLEAN: YES X-Fangmail-Anti-Spam-Filtered: true X-Fangmail-MID-QID: 6AAA329C.001/4hl7l049Gmz4xPSq Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Shengming Hu For a shared cpufreq policy, dbs_update() derives the load used for the frequency decision from the maximum load among its CPUs, but it may also record deferred idle periods accumulated by an idle CPU in the policy. Consequently, a single update can contain both a high decision load from one CPU and multiple deferred idle periods from another CPU. The conservative governor applies the deferred down steps before evaluating the decision load. If that load subsequently triggers an up step, the deferred down steps can outweigh the single up step and produce a net frequency reduction. This was observed on a policy shared by CPUs 2 and 3. A CPU-bound SCHED_EXT task kept CPU 2 fully utilized while CPU 3 remained idle. On this system, SCHED_EXT generated update-util callbacks less frequently than CFS, resulting in sparse DBS updates such as: load=3D100 idle_periods=3D7 interval=3D59 ms load=3D100 idle_periods=3D4 interval=3D39 ms load=3D100 idle_periods=3D2 interval=3D19 ms load=3D100 idle_periods=3D7 interval=3D59 ms With the default 5% frequency step and a 2.6 GHz policy maximum, seven deferred periods reduce the requested frequency by seven 130 MHz steps before the high decision load adds only one step. Repeated updates therefore keep the policy near 530 MHz even though CPU 2 is fully utilized. There are two load values relevant to this behavior: * sample_load is calculated from the busy and elapsed time of the current sample. * decision_load is returned by dbs_update() for the governor's frequency decision. After a long idle interval, it may reuse prev_load to improve the response to a waking task and can therefore differ from sample_load. The problem is that idle_periods is derived from accumulated idle time and is applied independently of whether the current sample load is in the conservative governor's downscaling region. A high sample load and multiple deferred idle periods can therefore coexist, particularly when updates are sparse. As suggested by Zhongqiu Han, calculate the sample load separately from the decision load. If no time has elapsed, use prev_load for the sample load as the existing load calculation does, since no load can be calculated for that interval. Record the maximum sample load for the policy and apply deferred down steps only when it is below down_threshold. This makes deferred downscaling follow the same threshold that the conservative governor normally uses to determine whether the policy load is low enough to reduce the frequency. The existing code applies deferred down steps whenever idle_periods is valid, regardless of the sample load. When the decision load is in the hold region, the locally adjusted frequency is not submitted unless another frequency-update branch is taken. Thus, when the sample and decision loads are the same and both are in the hold region, this change does not alter the requested frequency. The difference becomes visible when the decision load causes a frequency update. In particular, if it exceeds up_threshold, the governor applies a single up step after the deferred down steps and submits the resulting request. Multiple deferred down steps can then outweigh that up step and cause a frequency reduction even when the sample load is above down_threshold. With this change, deferred down steps are skipped whenever the maximum sample load is at or above down_threshold, keeping deferred downscaling consistent with the conservative governor's threshold semantics. The requested frequency may therefore be higher than with the existing behavior when deferred idle periods are present and the decision load causes a frequency update. This is intentional, because the lower request previously resulted from applying deferred downscaling while the sampled policy load was outside the governor's downscaling region. Accelerated deferred downscaling remains available when the policy sample load is below down_threshold, including after a workload has completed and the policy has become idle. Cc: stable@vger.kernel.org Fixes: 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for = deferred updates") Reviewed-by: Luo Haiyang Reviewed-by: Run Zhang Suggested-by: Zhongqiu Han Signed-off-by: Shengming Hu --- Changes in v2: - Calculate the sample load separately from the decision load that may reuse prev_load after a long idle interval, as suggested by Zhongqiu Han. - Preserve the existing prev_load fallback when time_elapsed is zero. - Gate deferred down steps on down_threshold. - Link to v1: https://lore.kernel.org/all/2026090215474182681fN7LLOSpqIc3s3= OqJaW@zte.com.cn/ --- drivers/cpufreq/cpufreq_conservative.c | 8 ++++--- drivers/cpufreq/cpufreq_governor.c | 29 +++++++++++++++++++------- drivers/cpufreq/cpufreq_governor.h | 2 ++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufr= eq_conservative.c index 0b32ae28ec85..02bfd46543e9 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -85,10 +85,12 @@ static unsigned int cs_dbs_update(struct cpufreq_policy= *policy) freq_step =3D get_freq_step(cs_tuners, policy); /* - * Decrease requested_freq one freq_step for each idle period that - * we didn't update the frequency. + * Apply deferred down steps only when the policy sample load is + * below down_threshold. Otherwise, multiple deferred down steps may + * cause a net frequency decrease outside the downscaling region. */ - if (policy_dbs->idle_periods < UINT_MAX) { + if (policy_dbs->max_sample_load < cs_tuners->down_threshold && + policy_dbs->idle_periods < UINT_MAX) { unsigned int freq_steps =3D policy_dbs->idle_periods * freq_step; if (requested_freq > policy->min + freq_steps) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_g= overnor.c index 710d93ec89b5..f3bd3dbe7c41 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -124,7 +124,8 @@ unsigned int dbs_update(struct cpufreq_policy *policy) struct policy_dbs_info *policy_dbs =3D policy->governor_data; struct dbs_data *dbs_data =3D policy_dbs->dbs_data; unsigned int ignore_nice =3D dbs_data->ignore_nice_load; - unsigned int max_load =3D 0, idle_periods =3D UINT_MAX; + unsigned int max_load =3D 0, max_sample_load =3D 0; + unsigned int idle_periods =3D UINT_MAX; unsigned int sampling_rate, io_busy, j; u64 cur_nice; @@ -147,7 +148,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) struct cpu_dbs_info *j_cdbs =3D &per_cpu(cpu_dbs, j); u64 update_time, cur_idle_time; unsigned int idle_time, time_elapsed; - unsigned int load; + unsigned int load, sample_load; cur_idle_time =3D get_cpu_idle_time(j, &update_time, io_busy); @@ -186,6 +187,20 @@ unsigned int dbs_update(struct cpufreq_policy *policy) j_cdbs->prev_cpu_nice =3D cur_nice; + /* + * Compute the sample load separately from the prev_load value + * that may be reused after a long idle interval. The conservative + * governor uses it to decide whether to apply deferred down steps. + * If no time has elapsed, retain the existing behavior and use + * prev_load. + */ + if (unlikely(!time_elapsed)) + sample_load =3D j_cdbs->prev_load; + else if (time_elapsed > idle_time) + sample_load =3D 100 * (time_elapsed - idle_time) / time_elapsed; + else + sample_load =3D 0; + if (unlikely(!time_elapsed)) { /* * That can only happen when this function is called @@ -220,11 +235,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) load =3D j_cdbs->prev_load; j_cdbs->prev_load =3D 0; } else { - if (time_elapsed > idle_time) - load =3D 100 * (time_elapsed - idle_time) / time_elapsed; - else - load =3D 0; - + load =3D sample_load; j_cdbs->prev_load =3D load; } @@ -237,9 +248,13 @@ unsigned int dbs_update(struct cpufreq_policy *policy) if (load > max_load) max_load =3D load; + + if (sample_load > max_sample_load) + max_sample_load =3D sample_load; } policy_dbs->idle_periods =3D idle_periods; + policy_dbs->max_sample_load =3D max_sample_load; return max_load; } diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_g= overnor.h index 73b8ed7cfaae..806d8fb4dff1 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -95,6 +95,8 @@ struct policy_dbs_info { /* Multiplier for increasing sample delay temporarily. */ unsigned int rate_mult; unsigned int idle_periods; /* For conservative */ + /* Maximum load from the current policy sample. */ + unsigned int max_sample_load; /* Status indicators */ bool is_shared; /* This object is used by multiple CPUs */ bool work_in_progress; /* Work is being queued up or in progress */ --=20 2.25.1