[PATCH] drm/amdgpu: Add GFXOFF auto-tunning algorithm

Sergey Kovalenko seryoga.engineering at gmail.com
Sat Mar 22 13:12:16 UTC 2025


Predict an optimal delay to enable GFXOFF for the next interval
based on the request count:
- less than 15 requests per second - zero delay
- less than 25 requests per second - default delay
- 25 and more requests per second - don't enable GFXOFF

The algorithm allows maintaining low power consumption in idle,
as well as using the full GPU power under load by eliminating
hundreds of extra GFXOFF ON/OFF switches.

Test configuration:
- Ryzen 5 2500U
- Ryzen 5 3400G
- Chromium 134.0.6998.88 Arch Linux
- Mesa 1:24.3.4-1
- KDE Plasma 6.3.2

GFXOFF enable requests per second:
| Test                                                | min | max |
|-----------------------------------------------------|-----|-----|
| System idle                                         |   0 |  64 |
| Web browsing                                        |   0 | 127 |
| WebGL aquarium                                      |  10 | 236 |
| Heavy load: glxgears + vkcube + resizing + flipping |  39 | 677 |

Test results, Ryzen 5 2500U:
| Test                        | patched-6.13.7.arch1 |   6.13.7.arch1-1 |
|-----------------------------|----------------------|------------------|
| System idle (PkgWatt)       |               ~0.74W |          ~1.25W  |
| glxgears (vblank_mode=0)    |     ~7300 fps, ~7.3W | ~7300 fps, ~7.3W |
| WebGL aquarium 15.000 fish  |     56-60 fps, ~9.8W | 55-60 fps, ~9.8W |

Test results, Ryzen 5 3400G:
| Test                        | patched-6.13.7.arch1 |   6.13.7.arch1-1 |
|-----------------------------|----------------------|------------------|
| System idle (PkgWatt)       |                ~3.8W |           ~4.3W  |
| glxgears (vblank_mode=0)    |            ~7200 fps |        ~7200 fps |
| WebGL aquarium 30.000 fish  |          37 fps, 47W |      38 fps, 47W |

Signed-off-by: Sergey Kovalenko <seryoga.engineering at gmail.com>
Tested-by: Liam Fleming <fleming.squared at proton.me>
---
  drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c | 94 +++++++++++++++++--------
  drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h |  3 +
  2 files changed, 67 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index c1f35ded684e..5e23b956e0bf 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -781,55 +781,89 @@ int amdgpu_gfx_enable_kgq(struct amdgpu_device 
*adev, int xcc_id)
   * 3. other client can cancel their request of disable gfx off feature
   * 4. other client should not send request to enable gfx off feature 
before disable gfx off feature.
   */
-
  void amdgpu_gfx_off_ctrl(struct amdgpu_device *adev, bool enable)
  {
-	unsigned long delay = GFX_OFF_DELAY_ENABLE;
-
  	if (!(adev->pm.pp_feature & PP_GFXOFF_MASK))
  		return;

  	mutex_lock(&adev->gfx.gfx_off_mutex);

  	if (enable) {
-		/* If the count is already 0, it means there's an imbalance bug 
somewhere.
-		 * Note that the bug may be in a different caller than the one which 
triggers the
-		 * WARN_ON_ONCE.
-		 */
-		if (WARN_ON_ONCE(adev->gfx.gfx_off_req_count == 0))
+		/* This case covers multiple calls from parallel threads */
+		if (!adev->gfx.gfx_off_req_count)
  			goto unlock;

-		adev->gfx.gfx_off_req_count--;
+		/* Process only if req_count == 0 and GFXOFF is disabled */
+		if (--adev->gfx.gfx_off_req_count || adev->gfx.gfx_off_state)
+			goto unlock;
+
+		/* If going to s2idle, no need to wait */
+		if (adev->in_s0ix) {
+			if (!amdgpu_dpm_set_powergating_by_smu(
+				    adev, AMD_IP_BLOCK_TYPE_GFX, true, 0))
+				adev->gfx.gfx_off_state = true;
+
+			/* Reset delay flag */
+			adev->gfx.gfx_off_use_delay = 0;
+			goto unlock;
+		}

-		if (adev->gfx.gfx_off_req_count == 0 &&
-		    !adev->gfx.gfx_off_state) {
-			/* If going to s2idle, no need to wait */
-			if (adev->in_s0ix) {
-				if (!amdgpu_dpm_set_powergating_by_smu(adev,
-						AMD_IP_BLOCK_TYPE_GFX, true, 0))
-					adev->gfx.gfx_off_state = true;
+		++adev->gfx.gfx_off_counter;
+
+		uint64_t now = get_jiffies_64();
+		uint64_t delta =
+			jiffies_to_msecs(now - adev->gfx.gfx_off_timestamp);
+
+		if (delta >= 1000u) {
+			/*
+			 * Predict the optimal delay for the next interval
+			 * based on the current number of requests:
+			 * <15 - idle, no delay
+			 * <25 - light/medium load, default delay
+			 * 25 and more - high load, GFXOFF disabled
+			 */
+			if (adev->gfx.gfx_off_counter < 15u) {
+				adev->gfx.gfx_off_use_delay = 0;
+			} else if (adev->gfx.gfx_off_counter < 25u) {
+				adev->gfx.gfx_off_use_delay = 1;
  			} else {
-				schedule_delayed_work(&adev->gfx.gfx_off_delay_work,
-					      delay);
+				adev->gfx.gfx_off_use_delay = 2;
  			}
+
+			adev->gfx.gfx_off_counter = 0;
+			adev->gfx.gfx_off_timestamp = now;
  		}
+
+		/* Don't schedule gfxoff under heavy load */
+		if (adev->gfx.gfx_off_use_delay == 2)
+			goto unlock;
+
+		schedule_delayed_work(&adev->gfx.gfx_off_delay_work,
+				      adev->gfx.gfx_off_use_delay ?
+					      GFX_OFF_DELAY_ENABLE :
+					      GFX_OFF_NO_DELAY);
  	} else {
-		if (adev->gfx.gfx_off_req_count == 0) {
-			cancel_delayed_work_sync(&adev->gfx.gfx_off_delay_work);
+		/* GFXOFF was enabled when req_count == 0 */
+		if (++adev->gfx.gfx_off_req_count != 1)
+			goto unlock;

-			if (adev->gfx.gfx_off_state &&
-			    !amdgpu_dpm_set_powergating_by_smu(adev, AMD_IP_BLOCK_TYPE_GFX, 
false, 0)) {
-				adev->gfx.gfx_off_state = false;
+		/* Nothing to do if the work wasn't scheduled */
+		if (adev->gfx.gfx_off_use_delay == 2)
+			goto unlock;

-				if (adev->gfx.funcs->init_spm_golden) {
-					dev_dbg(adev->dev,
-						"GFXOFF is disabled, re-init SPM golden settings\n");
-					amdgpu_gfx_init_spm_golden(adev);
-				}
+		cancel_delayed_work_sync(&adev->gfx.gfx_off_delay_work);
+
+		if (adev->gfx.gfx_off_state &&
+		    !amdgpu_dpm_set_powergating_by_smu(
+			    adev, AMD_IP_BLOCK_TYPE_GFX, false, 0)) {
+			adev->gfx.gfx_off_state = false;
+
+			if (adev->gfx.funcs->init_spm_golden) {
+				dev_dbg(adev->dev,
+					"GFXOFF is disabled, re-init SPM golden settings\n");
+				amdgpu_gfx_init_spm_golden(adev);
  			}
  		}
-
-		adev->gfx.gfx_off_req_count++;
  	}

  unlock:
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
index 8b5bd63b5773..38fd445a353b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
@@ -430,7 +430,10 @@ struct amdgpu_gfx {
  	/* gfx off */
  	bool                            gfx_off_state;      /* true: enabled, 
false: disabled */
  	struct mutex                    gfx_off_mutex;      /* mutex to 
change gfxoff state */
+	uint64_t			gfx_off_timestamp;  /* gfxoff enable call timestamp */
+	uint32_t			gfx_off_use_delay;  /* flag to choose the delay range */
  	uint32_t                        gfx_off_req_count;  /* default 1, 
enable gfx off: dec 1, disable gfx off: add 1 */
+	uint32_t			gfx_off_counter;    /* count of gfxoff enable calls */
  	struct delayed_work             gfx_off_delay_work; /* async work to 
set gfx block off */
  	uint32_t                        gfx_off_residency;  /* last logged 
residency */
  	uint64_t                        gfx_off_entrycount; /* count of times 
GPU has get into GFXOFF state */
-- 
2.49.0


More information about the amd-gfx mailing list