On Mon, Aug 14, 2023 at 3:35 AM Arvind Yadav <Arvind.Yadav@xxxxxxx> wrote: > > This patch adds a function which will allow to > change the GPU power profile based on a submitted job. > This can optimize the power performance when the > workload is on. A few minor comments inline below. One thing to double check is that we properly cancel this work before a suspend or driver unload. We need to make sure this is taken care of before we take down the SMU. Alex > > Cc: Shashank Sharma <shashank.sharma@xxxxxxx> > Cc: Christian Koenig <christian.koenig@xxxxxxx> > Cc: Alex Deucher <alexander.deucher@xxxxxxx> > Signed-off-by: Arvind Yadav <Arvind.Yadav@xxxxxxx> > --- > drivers/gpu/drm/amd/amdgpu/Makefile | 2 +- > drivers/gpu/drm/amd/amdgpu/amdgpu.h | 3 + > drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 + > drivers/gpu/drm/amd/amdgpu/amdgpu_workload.c | 156 ++++++++++++++++++ > drivers/gpu/drm/amd/include/amdgpu_workload.h | 44 +++++ > 5 files changed, 206 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_workload.c > create mode 100644 drivers/gpu/drm/amd/include/amdgpu_workload.h > > diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile > index 415a7fa395c4..6a9e187d61e1 100644 > --- a/drivers/gpu/drm/amd/amdgpu/Makefile > +++ b/drivers/gpu/drm/amd/amdgpu/Makefile > @@ -60,7 +60,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \ > amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o amdgpu_rap.o \ > amdgpu_fw_attestation.o amdgpu_securedisplay.o \ > amdgpu_eeprom.o amdgpu_mca.o amdgpu_psp_ta.o amdgpu_lsdma.o \ > - amdgpu_ring_mux.o > + amdgpu_ring_mux.o amdgpu_workload.o > > amdgpu-$(CONFIG_PROC_FS) += amdgpu_fdinfo.o > > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h > index 02b827785e39..1939fa1af8a6 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h > @@ -107,6 +107,7 @@ > #include "amdgpu_fdinfo.h" > #include "amdgpu_mca.h" > #include "amdgpu_ras.h" > +#include "amdgpu_workload.h" > > #define MAX_GPU_INSTANCE 16 > > @@ -1050,6 +1051,8 @@ struct amdgpu_device { > > bool job_hang; > bool dc_enabled; > + > + struct amdgpu_smu_workload smu_workload; > }; > > static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev) > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > index 5c7d40873ee2..0ec18b8fe29f 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c > @@ -3672,6 +3672,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, > > INIT_WORK(&adev->xgmi_reset_work, amdgpu_device_xgmi_reset_func); > > + amdgpu_smu_workload_init(adev); > + > adev->gfx.gfx_off_req_count = 1; > adev->gfx.gfx_off_residency = 0; > adev->gfx.gfx_off_entrycount = 0; > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_workload.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_workload.c > new file mode 100644 > index 000000000000..ce0339d75c12 > --- /dev/null > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_workload.c > @@ -0,0 +1,156 @@ > +// SPDX-License-Identifier: MIT > +/* > + * Copyright 2023 Advanced Micro Devices, Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > + * OTHER DEALINGS IN THE SOFTWARE. > + * > + */ > + > +#include "amdgpu.h" > + > +/* 100 millsecond timeout */ > +#define SMU_IDLE_TIMEOUT msecs_to_jiffies(100) > + > +static enum PP_SMC_POWER_PROFILE > +ring_to_power_profile(uint32_t ring_type) > +{ > + switch (ring_type) { > + case AMDGPU_RING_TYPE_GFX: > + return PP_SMC_POWER_PROFILE_FULLSCREEN3D; > + case AMDGPU_RING_TYPE_COMPUTE: > + return PP_SMC_POWER_PROFILE_COMPUTE; > + case AMDGPU_RING_TYPE_UVD: > + case AMDGPU_RING_TYPE_VCE: > + case AMDGPU_RING_TYPE_UVD_ENC: > + case AMDGPU_RING_TYPE_VCN_DEC: > + case AMDGPU_RING_TYPE_VCN_ENC: > + case AMDGPU_RING_TYPE_VCN_JPEG: > + return PP_SMC_POWER_PROFILE_VIDEO; > + default: > + return PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT; > + } > +} > + > +static void > +amdgpu_power_profile_set(struct amdgpu_device *adev, > + enum PP_SMC_POWER_PROFILE profile) > +{ > + int ret = amdgpu_dpm_switch_power_profile(adev, profile, true); > + > + if (ret == 0) { > + /* Set the bit for the submitted workload profile */ > + adev->smu_workload.submit_workload_status |= (1 << profile); > + atomic_inc(&adev->smu_workload.power_profile_ref[profile]); > + } else { > + DRM_ERROR("Failed to set power profile, error %d\n", ret); > + } > + > +} > + > +static void > +amdgpu_power_profile_clear(struct amdgpu_device *adev, > + enum PP_SMC_POWER_PROFILE profile) > +{ > + int ret = amdgpu_dpm_switch_power_profile(adev, profile, false); > + > + if (ret == 0) { > + /* Clear the bit for the submitted workload profile */ > + adev->smu_workload.submit_workload_status &= ~(1 << profile); > + } else > + DRM_ERROR("Failed to clear power profile, error %d\n", ret); > + > +} > + > +static void amdgpu_smu_idle_work_handler(struct work_struct *work) How about amdgpu_power_profile_idle_work_handler() for consistency? > +{ > + > + struct amdgpu_smu_workload *wl = container_of(work, > + struct amdgpu_smu_workload, > + smu_delayed_work.work); > + struct amdgpu_device *adev = wl->adev; > + bool reschedule = false; > + > + mutex_lock(&adev->smu_workload.workload_lock); > + for (int index = fls(adev->smu_workload.submit_workload_status); > + index >= 0; index--) { > + if (!atomic_read(&adev->smu_workload.power_profile_ref[index]) && > + adev->smu_workload.submit_workload_status & (1 << index)) { > + amdgpu_power_profile_clear(adev, index); > + } else if (atomic_read(&adev->smu_workload.power_profile_ref[index])) > + reschedule = true; > + } > + > + if (reschedule) > + schedule_delayed_work(&adev->smu_workload.smu_delayed_work, > + SMU_IDLE_TIMEOUT); > + > + mutex_unlock(&adev->smu_workload.workload_lock); > +} > + > +void amdgpu_put_workload_profile(struct amdgpu_device *adev, amdgpu_workload_profile_put() for consistency. > + uint32_t ring_type) > +{ > + > + enum PP_SMC_POWER_PROFILE profile = ring_to_power_profile(ring_type); > + > + if (profile == PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT) > + return; > + > + mutex_lock(&adev->smu_workload.workload_lock); > + atomic_dec(&adev->smu_workload.power_profile_ref[profile]); > + schedule_delayed_work(&adev->smu_workload.smu_delayed_work, SMU_IDLE_TIMEOUT); > + mutex_unlock(&adev->smu_workload.workload_lock); > +} > + > +void amdgpu_set_workload_profile(struct amdgpu_device *adev, > + uint32_t ring_type) amdgpu_workload_profile_set() for consistency. > +{ > + enum PP_SMC_POWER_PROFILE profile = ring_to_power_profile(ring_type); > + > + if (profile == PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT) > + return; > + > + mutex_lock(&adev->smu_workload.workload_lock); > + cancel_delayed_work_sync(&adev->smu_workload.smu_delayed_work); > + > + amdgpu_power_profile_set(adev, profile); > + > + /* Clear the already finished jobs of higher power profile*/ > + for (int index = fls(adev->smu_workload.submit_workload_status); > + index > profile; index--) { > + if (!atomic_read(&adev->smu_workload.power_profile_ref[index]) && > + adev->smu_workload.submit_workload_status & (1 << index)) { > + amdgpu_power_profile_clear(adev, index); > + } > + } > + > + mutex_unlock(&adev->smu_workload.workload_lock); > +} > + > +void amdgpu_smu_workload_init(struct amdgpu_device *adev) amdgpu_workload_profile_init() for consistency. > +{ > + struct amdgpu_smu_workload wl; > + > + wl.adev = adev; > + wl.submit_workload_status = 0; > + adev->smu_workload = wl; > + > + mutex_init(&adev->smu_workload.workload_lock); > + INIT_DELAYED_WORK(&adev->smu_workload.smu_delayed_work, amdgpu_smu_idle_work_handler); > +} > diff --git a/drivers/gpu/drm/amd/include/amdgpu_workload.h b/drivers/gpu/drm/amd/include/amdgpu_workload.h > new file mode 100644 > index 000000000000..09804c3d2869 > --- /dev/null > +++ b/drivers/gpu/drm/amd/include/amdgpu_workload.h > @@ -0,0 +1,44 @@ > +/* SPDX-License-Identifier: MIT */ > +/* > + * Copyright 2023 Advanced Micro Devices, Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR > + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > + * OTHER DEALINGS IN THE SOFTWARE. > + * > + */ > + > +#ifndef _AMDGPU_WORKLOAD_H_ > +#define _AMDGPU_WORKLOAD_H_ > + > +struct amdgpu_smu_workload { > + struct amdgpu_device *adev; > + struct mutex workload_lock; > + struct delayed_work smu_delayed_work; > + uint32_t submit_workload_status; > + atomic_t power_profile_ref[PP_SMC_POWER_PROFILE_COUNT]; > +}; > + > +void amdgpu_set_workload_profile(struct amdgpu_device *adev, > + uint32_t ring_type); > + > +void amdgpu_put_workload_profile(struct amdgpu_device *adev, > + uint32_t ring_type); > + > +void amdgpu_smu_workload_init(struct amdgpu_device *adev); > + > +#endif > -- > 2.34.1 >