Merge tag 'amd-drm-fixes-7.0-2026-03-25' of https://gitlab.freedesktop.org/agd5f/linux into drm-fixes

amd-drm-fixes-7.0-2026-03-25:

amdgpu:
- DSC fix
- Module parameter parsing fix
- PASID reuse fix
- drm_edid leak fix
- SMU 13.x fixes
- SMU 14.x fix
- Fence fix in amdgpu_amdkfd_submit_ib()
- LVDS fixes
- GPU page fault fix for non-4K pages

amdkfd:
- Ordering fix in kfd_ioctl_create_process()

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Alex Deucher <alexander.deucher@amd.com>
Link: https://patch.msgid.link/20260325155600.4184877-1-alexander.deucher@amd.com
This commit is contained in:
Dave Airlie
2026-03-27 14:07:09 +10:00
19 changed files with 192 additions and 60 deletions

View File

@@ -692,9 +692,9 @@ int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev,
goto err_ib_sched;
}
/* Drop the initial kref_init count (see drm_sched_main as example) */
dma_fence_put(f);
ret = dma_fence_wait(f, false);
/* Drop the returned fence reference after the wait completes */
dma_fence_put(f);
err_ib_sched:
amdgpu_job_free(job);

View File

@@ -4207,7 +4207,8 @@ static void amdgpu_device_xgmi_reset_func(struct work_struct *__work)
static int amdgpu_device_get_job_timeout_settings(struct amdgpu_device *adev)
{
char *input = amdgpu_lockup_timeout;
char buf[AMDGPU_MAX_TIMEOUT_PARAM_LENGTH];
char *input = buf;
char *timeout_setting = NULL;
int index = 0;
long timeout;
@@ -4217,9 +4218,17 @@ static int amdgpu_device_get_job_timeout_settings(struct amdgpu_device *adev)
adev->gfx_timeout = adev->compute_timeout = adev->sdma_timeout =
adev->video_timeout = msecs_to_jiffies(2000);
if (!strnlen(input, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH))
if (!strnlen(amdgpu_lockup_timeout, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH))
return 0;
/*
* strsep() destructively modifies its input by replacing delimiters
* with '\0'. Use a stack copy so the global module parameter buffer
* remains intact for multi-GPU systems where this function is called
* once per device.
*/
strscpy(buf, amdgpu_lockup_timeout, sizeof(buf));
while ((timeout_setting = strsep(&input, ",")) &&
strnlen(timeout_setting, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH)) {
ret = kstrtol(timeout_setting, 0, &timeout);

View File

@@ -35,10 +35,13 @@
* PASIDs are global address space identifiers that can be shared
* between the GPU, an IOMMU and the driver. VMs on different devices
* may use the same PASID if they share the same address
* space. Therefore PASIDs are allocated using a global IDA. VMs are
* looked up from the PASID per amdgpu_device.
* space. Therefore PASIDs are allocated using IDR cyclic allocator
* (similar to kernel PID allocation) which naturally delays reuse.
* VMs are looked up from the PASID per amdgpu_device.
*/
static DEFINE_IDA(amdgpu_pasid_ida);
static DEFINE_IDR(amdgpu_pasid_idr);
static DEFINE_SPINLOCK(amdgpu_pasid_idr_lock);
/* Helper to free pasid from a fence callback */
struct amdgpu_pasid_cb {
@@ -50,8 +53,8 @@ struct amdgpu_pasid_cb {
* amdgpu_pasid_alloc - Allocate a PASID
* @bits: Maximum width of the PASID in bits, must be at least 1
*
* Allocates a PASID of the given width while keeping smaller PASIDs
* available if possible.
* Uses kernel's IDR cyclic allocator (same as PID allocation).
* Allocates sequentially with automatic wrap-around.
*
* Returns a positive integer on success. Returns %-EINVAL if bits==0.
* Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on
@@ -59,14 +62,15 @@ struct amdgpu_pasid_cb {
*/
int amdgpu_pasid_alloc(unsigned int bits)
{
int pasid = -EINVAL;
int pasid;
for (bits = min(bits, 31U); bits > 0; bits--) {
pasid = ida_alloc_range(&amdgpu_pasid_ida, 1U << (bits - 1),
(1U << bits) - 1, GFP_KERNEL);
if (pasid != -ENOSPC)
break;
}
if (bits == 0)
return -EINVAL;
spin_lock(&amdgpu_pasid_idr_lock);
pasid = idr_alloc_cyclic(&amdgpu_pasid_idr, NULL, 1,
1U << bits, GFP_KERNEL);
spin_unlock(&amdgpu_pasid_idr_lock);
if (pasid >= 0)
trace_amdgpu_pasid_allocated(pasid);
@@ -81,7 +85,10 @@ int amdgpu_pasid_alloc(unsigned int bits)
void amdgpu_pasid_free(u32 pasid)
{
trace_amdgpu_pasid_freed(pasid);
ida_free(&amdgpu_pasid_ida, pasid);
spin_lock(&amdgpu_pasid_idr_lock);
idr_remove(&amdgpu_pasid_idr, pasid);
spin_unlock(&amdgpu_pasid_idr_lock);
}
static void amdgpu_pasid_free_cb(struct dma_fence *fence,
@@ -616,3 +623,15 @@ void amdgpu_vmid_mgr_fini(struct amdgpu_device *adev)
}
}
}
/**
* amdgpu_pasid_mgr_cleanup - cleanup PASID manager
*
* Cleanup the IDR allocator.
*/
void amdgpu_pasid_mgr_cleanup(void)
{
spin_lock(&amdgpu_pasid_idr_lock);
idr_destroy(&amdgpu_pasid_idr);
spin_unlock(&amdgpu_pasid_idr_lock);
}

View File

@@ -74,6 +74,7 @@ int amdgpu_pasid_alloc(unsigned int bits);
void amdgpu_pasid_free(u32 pasid);
void amdgpu_pasid_free_delayed(struct dma_resv *resv,
u32 pasid);
void amdgpu_pasid_mgr_cleanup(void);
bool amdgpu_vmid_had_gpu_reset(struct amdgpu_device *adev,
struct amdgpu_vmid *id);

View File

@@ -2898,6 +2898,7 @@ void amdgpu_vm_manager_fini(struct amdgpu_device *adev)
xa_destroy(&adev->vm_manager.pasids);
amdgpu_vmid_mgr_fini(adev);
amdgpu_pasid_mgr_cleanup();
}
/**
@@ -2973,14 +2974,14 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
if (!root)
return false;
addr /= AMDGPU_GPU_PAGE_SIZE;
if (is_compute_context && !svm_range_restore_pages(adev, pasid, vmid,
node_id, addr, ts, write_fault)) {
node_id, addr >> PAGE_SHIFT, ts, write_fault)) {
amdgpu_bo_unref(&root);
return true;
}
addr /= AMDGPU_GPU_PAGE_SIZE;
r = amdgpu_bo_reserve(root, true);
if (r)
goto error_unref;

View File

@@ -3170,11 +3170,11 @@ static int kfd_ioctl_create_process(struct file *filep, struct kfd_process *p, v
struct kfd_process *process;
int ret;
/* Each FD owns only one kfd_process */
if (p->context_id != KFD_CONTEXT_ID_PRIMARY)
if (!filep->private_data || !p)
return -EINVAL;
if (!filep->private_data || !p)
/* Each FD owns only one kfd_process */
if (p->context_id != KFD_CONTEXT_ID_PRIMARY)
return -EINVAL;
mutex_lock(&kfd_processes_mutex);

View File

@@ -3909,8 +3909,9 @@ void amdgpu_dm_update_connector_after_detect(
aconnector->dc_sink = sink;
dc_sink_retain(aconnector->dc_sink);
drm_edid_free(aconnector->drm_edid);
aconnector->drm_edid = NULL;
if (sink->dc_edid.length == 0) {
aconnector->drm_edid = NULL;
hdmi_cec_unset_edid(aconnector);
if (aconnector->dc_link->aux_mode) {
drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
@@ -5422,7 +5423,7 @@ static void setup_backlight_device(struct amdgpu_display_manager *dm,
caps = &dm->backlight_caps[aconnector->bl_idx];
/* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
if (!caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
drm_object_attach_property(&aconnector->base.base,
dm->adev->mode_info.abm_level_property,
ABM_SYSFS_CONTROL);
@@ -12523,6 +12524,11 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
}
if (dc_resource_is_dsc_encoding_supported(dc)) {
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
dm_new_crtc_state->mode_changed_independent_from_dsc = new_crtc_state->mode_changed;
}
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
ret = add_affected_mst_dsc_crtcs(state, crtc);

View File

@@ -984,6 +984,7 @@ struct dm_crtc_state {
bool freesync_vrr_info_changed;
bool mode_changed_independent_from_dsc;
bool dsc_force_changed;
bool vrr_supported;
struct mod_freesync_config freesync_config;

View File

@@ -1744,9 +1744,11 @@ int pre_validate_dsc(struct drm_atomic_state *state,
int ind = find_crtc_index_in_state_by_stream(state, stream);
if (ind >= 0) {
struct dm_crtc_state *dm_new_crtc_state = to_dm_crtc_state(state->crtcs[ind].new_state);
DRM_INFO_ONCE("%s:%d MST_DSC no mode changed for stream 0x%p\n",
__func__, __LINE__, stream);
state->crtcs[ind].new_state->mode_changed = 0;
dm_new_crtc_state->base.mode_changed = dm_new_crtc_state->mode_changed_independent_from_dsc;
}
}
}

View File

@@ -650,9 +650,6 @@ static struct link_encoder *dce100_link_encoder_create(
return &enc110->base;
}
if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
return NULL;
link_regs_id =
map_transmitter_id_to_phy_instance(enc_init_data->transmitter);
@@ -661,7 +658,8 @@ static struct link_encoder *dce100_link_encoder_create(
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
&link_enc_hpd_regs[enc_init_data->hpd_source]);
enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ?
NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]);
return &enc110->base;
}

View File

@@ -671,7 +671,7 @@ static struct link_encoder *dce110_link_encoder_create(
kzalloc_obj(struct dce110_link_encoder);
int link_regs_id;
if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
if (!enc110)
return NULL;
link_regs_id =
@@ -682,7 +682,8 @@ static struct link_encoder *dce110_link_encoder_create(
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
&link_enc_hpd_regs[enc_init_data->hpd_source]);
enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ?
NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]);
return &enc110->base;
}

View File

@@ -632,7 +632,7 @@ static struct link_encoder *dce112_link_encoder_create(
kzalloc_obj(struct dce110_link_encoder);
int link_regs_id;
if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
if (!enc110)
return NULL;
link_regs_id =
@@ -643,7 +643,8 @@ static struct link_encoder *dce112_link_encoder_create(
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
&link_enc_hpd_regs[enc_init_data->hpd_source]);
enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ?
NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]);
return &enc110->base;
}

View File

@@ -716,7 +716,7 @@ static struct link_encoder *dce120_link_encoder_create(
kzalloc_obj(struct dce110_link_encoder);
int link_regs_id;
if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
if (!enc110)
return NULL;
link_regs_id =
@@ -727,7 +727,8 @@ static struct link_encoder *dce120_link_encoder_create(
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
&link_enc_hpd_regs[enc_init_data->hpd_source]);
enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ?
NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]);
return &enc110->base;
}

View File

@@ -746,18 +746,16 @@ static struct link_encoder *dce60_link_encoder_create(
return &enc110->base;
}
if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
return NULL;
link_regs_id =
map_transmitter_id_to_phy_instance(enc_init_data->transmitter);
dce60_link_encoder_construct(enc110,
enc_init_data,
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
&link_enc_hpd_regs[enc_init_data->hpd_source]);
enc_init_data,
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ?
NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]);
return &enc110->base;
}

View File

@@ -752,9 +752,6 @@ static struct link_encoder *dce80_link_encoder_create(
return &enc110->base;
}
if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
return NULL;
link_regs_id =
map_transmitter_id_to_phy_instance(enc_init_data->transmitter);
@@ -763,7 +760,8 @@ static struct link_encoder *dce80_link_encoder_create(
&link_enc_feature,
&link_enc_regs[link_regs_id],
&link_enc_aux_regs[enc_init_data->channel - 1],
&link_enc_hpd_regs[enc_init_data->hpd_source]);
enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs) ?
NULL : &link_enc_hpd_regs[enc_init_data->hpd_source]);
return &enc110->base;
}

View File

@@ -59,6 +59,10 @@
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static const struct smu_feature_bits smu_v13_0_0_dpm_features = {
.bits = {
SMU_FEATURE_BIT_INIT(FEATURE_DPM_GFXCLK_BIT),
@@ -1043,8 +1047,35 @@ static bool smu_v13_0_0_is_od_feature_supported(struct smu_context *smu,
PPTable_t *pptable = smu->smu_table.driver_pptable;
const OverDriveLimits_t * const overdrive_upperlimits =
&pptable->SkuTable.OverDriveLimitsBasicMax;
int32_t min_value, max_value;
bool feature_enabled;
return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
switch (od_feature_bit) {
case PP_OD_FEATURE_FAN_CURVE_BIT:
feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
if (feature_enabled) {
smu_v13_0_0_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP,
&min_value, &max_value);
if (!min_value && !max_value) {
feature_enabled = false;
goto out;
}
smu_v13_0_0_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM,
&min_value, &max_value);
if (!min_value && !max_value) {
feature_enabled = false;
goto out;
}
}
break;
default:
feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
break;
}
out:
return feature_enabled;
}
static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,

View File

@@ -1391,7 +1391,7 @@ static int smu_v13_0_6_emit_clk_levels(struct smu_context *smu,
break;
case SMU_OD_MCLK:
if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(SET_UCLK_MAX)))
return 0;
return -EOPNOTSUPP;
size += sysfs_emit_at(buf, size, "%s:\n", "OD_MCLK");
size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n",
@@ -2122,6 +2122,7 @@ static int smu_v13_0_6_usr_edit_dpm_table(struct smu_context *smu,
{
struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
struct smu_dpm_table *uclk_table = &dpm_context->dpm_tables.uclk_table;
struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
uint32_t min_clk;
uint32_t max_clk;
@@ -2221,14 +2222,16 @@ static int smu_v13_0_6_usr_edit_dpm_table(struct smu_context *smu,
if (ret)
return ret;
min_clk = SMU_DPM_TABLE_MIN(
&dpm_context->dpm_tables.uclk_table);
max_clk = SMU_DPM_TABLE_MAX(
&dpm_context->dpm_tables.uclk_table);
ret = smu_v13_0_6_set_soft_freq_limited_range(
smu, SMU_UCLK, min_clk, max_clk, false);
if (ret)
return ret;
if (SMU_DPM_TABLE_MAX(uclk_table) !=
pstate_table->uclk_pstate.curr.max) {
min_clk = SMU_DPM_TABLE_MIN(&dpm_context->dpm_tables.uclk_table);
max_clk = SMU_DPM_TABLE_MAX(&dpm_context->dpm_tables.uclk_table);
ret = smu_v13_0_6_set_soft_freq_limited_range(smu,
SMU_UCLK, min_clk,
max_clk, false);
if (ret)
return ret;
}
smu_v13_0_reset_custom_level(smu);
}
break;

View File

@@ -59,6 +59,10 @@
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static const struct smu_feature_bits smu_v13_0_7_dpm_features = {
.bits = {
SMU_FEATURE_BIT_INIT(FEATURE_DPM_GFXCLK_BIT),
@@ -1053,8 +1057,35 @@ static bool smu_v13_0_7_is_od_feature_supported(struct smu_context *smu,
PPTable_t *pptable = smu->smu_table.driver_pptable;
const OverDriveLimits_t * const overdrive_upperlimits =
&pptable->SkuTable.OverDriveLimitsBasicMax;
int32_t min_value, max_value;
bool feature_enabled;
return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
switch (od_feature_bit) {
case PP_OD_FEATURE_FAN_CURVE_BIT:
feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
if (feature_enabled) {
smu_v13_0_7_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP,
&min_value, &max_value);
if (!min_value && !max_value) {
feature_enabled = false;
goto out;
}
smu_v13_0_7_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM,
&min_value, &max_value);
if (!min_value && !max_value) {
feature_enabled = false;
goto out;
}
}
break;
default:
feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
break;
}
out:
return feature_enabled;
}
static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,

View File

@@ -56,6 +56,10 @@
#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static const struct smu_feature_bits smu_v14_0_2_dpm_features = {
.bits = { SMU_FEATURE_BIT_INIT(FEATURE_DPM_GFXCLK_BIT),
SMU_FEATURE_BIT_INIT(FEATURE_DPM_UCLK_BIT),
@@ -922,8 +926,35 @@ static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
PPTable_t *pptable = smu->smu_table.driver_pptable;
const OverDriveLimits_t * const overdrive_upperlimits =
&pptable->SkuTable.OverDriveLimitsBasicMax;
int32_t min_value, max_value;
bool feature_enabled;
return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
switch (od_feature_bit) {
case PP_OD_FEATURE_FAN_CURVE_BIT:
feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
if (feature_enabled) {
smu_v14_0_2_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP,
&min_value, &max_value);
if (!min_value && !max_value) {
feature_enabled = false;
goto out;
}
smu_v14_0_2_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM,
&min_value, &max_value);
if (!min_value && !max_value) {
feature_enabled = false;
goto out;
}
}
break;
default:
feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit));
break;
}
out:
return feature_enabled;
}
static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,