From: Wenjing Liu <[email protected]>

[Why]
Peak-bandwidth-under-contention validation currently needs an
external tool to manufacture memory contention while querying
peak bandwidth. Expose this as an explicit probe variant so the
driver can generate that contention itself.

[How]
Add a new probe type that builds the same gated perfmon sequence
as the plain peak-BW probe, with a DMA copy of the current
surface into a scratch buffer inserted before the measurement
window to synthesize a competing memory client. Move the scratch
buffer fields onto the base resource pool struct instead of an
ASIC-specific subclass, and reject the probe with a new status
code when the buffer was never allocated. Also fixes a stack-
corruption bug where a local variable's address was captured by
a deferred callback and used after the local went out of scope,
and a missing NULL check on a similar output parameter used by
an early-return path.

Reviewed-by: Alvin Lee <[email protected]>
Signed-off-by: Wenjing Liu <[email protected]>
Signed-off-by: Ray Wu <[email protected]>
---
 .../gpu/drm/amd/display/dc/core/dc_debug.c    |  2 ++
 .../drm/amd/display/dc/core/dc_hw_sequencer.c | 29 +++++++++++++++++++
 .../gpu/drm/amd/display/dc/core/dc_resource.c |  4 +++
 drivers/gpu/drm/amd/display/dc/dc.h           |  1 +
 drivers/gpu/drm/amd/display/dc/dc_probe.h     |  1 +
 .../display/dc/hubbub/dcn60/dcn60_hubbub.c    |  3 +-
 .../amd/display/dc/hwss/dcn60/dcn60_hwseq.c   | 26 +++++++++++++++--
 .../drm/amd/display/dc/hwss/hw_sequencer.h    | 14 +++++++++
 .../gpu/drm/amd/display/dc/inc/core_status.h  |  2 ++
 .../gpu/drm/amd/display/dc/inc/core_types.h   |  9 ++++++
 .../dc/resource/dcn60/dcn60_resource.c        | 19 ++++++++++++
 11 files changed, 106 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c 
b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
index 2933b660e3d7..f3348e0cb1d6 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c
@@ -274,6 +274,8 @@ char *dc_status_to_str(enum dc_status status)
                return "HW Cursor not supported";
        case DC_FAIL_DP_TUNNEL_BW_VALIDATE:
                return "Fail DP Tunnel BW validation";
+       case DC_NO_DRAM_BUFFER_RESOURCE:
+               return "No DRAM buffer resource";
        case DC_ERROR_UNEXPECTED:
                return "Unexpected error";
        default:
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c 
b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
index de1772f75293..b54466e4f42b 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
@@ -1797,6 +1797,9 @@ void hwss_execute_sequence(struct dc *dc,
                case DMUB_SEND_DMCUB_CMD:
                        hwss_send_dmcub_cmd(params);
                        break;
+               case LSDMA_SEND_PIO_COPY:
+                       hwss_lsdma_send_pio_copy(params);
+                       break;
                case DMUB_SUBVP_SAVE_SURF_ADDR:
                        hwss_subvp_save_surf_addr(params);
                        break;
@@ -2670,6 +2673,17 @@ void hwss_send_dmcub_cmd(union block_sequence_params 
*params)
        dc_wake_and_execute_dmub_cmd(ctx, cmd, wait_type);
 }
 
+void hwss_lsdma_send_pio_copy(union block_sequence_params *params)
+{
+       struct dc_dmub_srv *dc_dmub_srv = 
params->lsdma_send_pio_copy_params.dc_dmub_srv;
+       uint64_t src_addr = params->lsdma_send_pio_copy_params.src_addr;
+       uint64_t dst_addr = params->lsdma_send_pio_copy_params.dst_addr;
+       uint32_t byte_count = params->lsdma_send_pio_copy_params.byte_count;
+       uint32_t overlap_disable = 
params->lsdma_send_pio_copy_params.overlap_disable;
+
+       dmub_lsdma_send_pio_copy_command(dc_dmub_srv, src_addr, dst_addr, 
byte_count, overlap_disable);
+}
+
 /*
  * Helper function to add TG program global sync to block sequence
  */
@@ -5214,6 +5228,21 @@ void hwss_add_hubbub_perfmon_arm_out_of_order_bw(struct 
block_sequence_state *se
        }
 }
 
+void hwss_add_lsdma_send_pio_copy(struct block_sequence_state *seq_state,
+               struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, uint64_t 
dst_addr,
+               uint32_t byte_count, uint32_t overlap_disable)
+{
+       if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+               seq_state->steps[*seq_state->num_steps].func = 
LSDMA_SEND_PIO_COPY;
+               
seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.dc_dmub_srv
 = dc_dmub_srv;
+               
seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.src_addr
 = src_addr;
+               
seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.dst_addr
 = dst_addr;
+               
seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.byte_count
 = byte_count;
+               
seq_state->steps[*seq_state->num_steps].params.lsdma_send_pio_copy_params.overlap_disable
 = overlap_disable;
+               (*seq_state->num_steps)++;
+       }
+}
+
 void hwss_add_hubbub_perfmon_start_out_of_order_bw(struct block_sequence_state 
*seq_state,
                struct hubbub *hubbub)
 {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c 
b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index ac558e715b72..8e7a42fd3cd3 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4470,6 +4470,10 @@ enum dc_status resource_validate_probe_set(struct dc *dc,
 
                if (probes[i].scope.type != DC_PROBE_SCOPE_GLOBAL)
                        return DC_NOT_SUPPORTED;
+
+               if (probes[i].type == DC_PROBE_PEAK_MEM_BW_STRESSED &&
+                               !dc->res_pool->lsdma_scratch.buffer)
+                       return DC_NO_DRAM_BUFFER_RESOURCE;
        }
 
        return DC_OK;
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h 
b/drivers/gpu/drm/amd/display/dc/dc.h
index 4b85727aaff9..f7511580e36f 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -584,6 +584,7 @@ struct dc_config {
        bool forced_clocks;
        union allow_lttpr_non_transparent_mode allow_lttpr_non_transparent_mode;
        bool multi_mon_pp_mclk_switch;
+       bool lsdma_peak_bw_contention_support;
        bool disable_dmcu;
        bool allow_4to1MPC;
        bool enable_windowed_mpo_odm;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_probe.h 
b/drivers/gpu/drm/amd/display/dc/dc_probe.h
index ebf33b162b63..52bffbebf7ad 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_probe.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_probe.h
@@ -20,6 +20,7 @@ enum dc_probe_type {
        DC_PROBE_URGENT_RAMP_LATENCY,
        DC_PROBE_URGENT_ASSERTION_COUNT,
        DC_PROBE_PREFETCH_DATA_SIZE,
+       DC_PROBE_PEAK_MEM_BW_STRESSED, /* peak BW under synthetic memory 
contention */
 };
 
 /**
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c 
b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c
index f1e6b2a1000d..0169ffcd9343 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c
@@ -1713,7 +1713,8 @@ static uint32_t 
hubbub60_perfmon_get_in_order_bandwidth_mbps(
                return 0;
 
        measuring_duration_ns = count4 * 1000 / refclk_mhz;
-       *duration_ns = measuring_duration_ns;
+       if (duration_ns)
+               *duration_ns = measuring_duration_ns;
        if (min_duration_ns > measuring_duration_ns)
                return 0;
 
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c
index 8ee9791b2c46..0e6526e92544 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c
@@ -893,7 +893,6 @@ static void dcn60_build_hubbub_perfmon_sequence(
        uint32_t refclk_mhz = dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 
1000;
        struct timing_generator *ref_tg = 
dcn60_get_ref_tg_for_hubbub_probe(context);
        struct block_sequence_state seq_state = { .steps = block_sequence, 
.num_steps = num_steps };
-       uint32_t duration_ns = 0;
 
        if (!hubbub || !hubbub->funcs || !hubbub->funcs->perfmon.reset)
                return;
@@ -910,6 +909,7 @@ static void dcn60_build_hubbub_perfmon_sequence(
 
        switch (probe->type) {
        case DC_PROBE_PEAK_MEM_BW:
+       case DC_PROBE_PEAK_MEM_BW_STRESSED:
                /* Start at the vblank edge and stop at the next vactive so the 
counter
                 * spans exactly one prefetch window, capturing prefetch 
traffic only. */
                if 
(!hubbub->funcs->perfmon.arm_measuring_out_of_order_bandwidth ||
@@ -919,12 +919,30 @@ static void dcn60_build_hubbub_perfmon_sequence(
 
                hwss_add_hubbub_perfmon_reset(&seq_state, hubbub);
                hwss_add_hubbub_perfmon_arm_out_of_order_bw(&seq_state, hubbub);
+
+               if (probe->type == DC_PROBE_PEAK_MEM_BW_STRESSED) {
+                       struct dc_plane_state *plane = 
(context->stream_status[0].plane_count > 0) ?
+                                       
context->stream_status[0].plane_states[0] : NULL;
+
+                       if (dc->res_pool->lsdma_scratch.buffer && plane) {
+                               unsigned int surface_bytes = 
plane->plane_size.surface_pitch *
+                                               
plane->plane_size.surface_size.height;
+                               unsigned int copy_bytes = (surface_bytes < 
dc->res_pool->lsdma_scratch.size) ?
+                                               surface_bytes : 
dc->res_pool->lsdma_scratch.size;
+
+                               hwss_add_lsdma_send_pio_copy(&seq_state, 
dc->ctx->dmub_srv,
+                                               
(uint64_t)plane->address.grph.addr.quad_part,
+                                               
(uint64_t)dc->res_pool->lsdma_scratch.pa,
+                                               copy_bytes, /* overlap_disable 
*/ 1);
+                       }
+               }
+
                hwss_add_tg_wait_for_state(&seq_state, ref_tg, 
CRTC_STATE_VACTIVE);
                hwss_add_tg_wait_for_state(&seq_state, ref_tg, 
CRTC_STATE_VBLANK);
                hwss_add_hubbub_perfmon_start_out_of_order_bw(&seq_state, 
hubbub);
                hwss_add_tg_wait_for_state(&seq_state, ref_tg, 
CRTC_STATE_VACTIVE);
                hwss_add_hubbub_perfmon_get_out_of_order_bw(&seq_state, hubbub,
-                               refclk_mhz, &status->u.bandwidth_mbps, 
&duration_ns);
+                               refclk_mhz, &status->u.bandwidth_mbps, NULL);
                break;
 
        case DC_PROBE_AVG_MEM_BW:
@@ -941,7 +959,7 @@ static void dcn60_build_hubbub_perfmon_sequence(
                hwss_add_tg_wait_for_state(&seq_state, ref_tg, 
CRTC_STATE_VACTIVE);
                hwss_add_tg_wait_for_state(&seq_state, ref_tg, 
CRTC_STATE_VBLANK);
                hwss_add_hubbub_perfmon_get_in_order_bw(&seq_state, hubbub,
-                               refclk_mhz, 0, &status->u.bandwidth_mbps, 
&duration_ns);
+                               refclk_mhz, 0, &status->u.bandwidth_mbps, NULL);
                break;
 
        case DC_PROBE_MEM_LATENCY:
@@ -1006,6 +1024,7 @@ static void dcn60_update_probe_status(struct 
dc_probe_status *status)
 {
        switch (status->type) {
        case DC_PROBE_PEAK_MEM_BW:
+       case DC_PROBE_PEAK_MEM_BW_STRESSED:
        case DC_PROBE_AVG_MEM_BW:
                /* Zero bandwidth means the counter did not fire — treat as 
invalid. */
                status->valid = (status->u.bandwidth_mbps != 0);
@@ -1029,6 +1048,7 @@ static bool is_probe_measurement_type_for_hubbub(enum 
dc_probe_type type)
 {
        switch (type) {
        case DC_PROBE_PEAK_MEM_BW:
+       case DC_PROBE_PEAK_MEM_BW_STRESSED:
        case DC_PROBE_AVG_MEM_BW:
        case DC_PROBE_MEM_LATENCY:
        case DC_PROBE_URGENT_ASSERTION_COUNT:
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h 
b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
index 3f9eda74fb1b..f75e34a09dca 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -142,6 +142,14 @@ struct send_dmcub_cmd_params {
        enum dm_dmub_wait_type wait_type;
 };
 
+struct lsdma_send_pio_copy_params {
+       struct dc_dmub_srv *dc_dmub_srv;
+       uint64_t src_addr;
+       uint64_t dst_addr;
+       uint32_t byte_count;
+       uint32_t overlap_disable;
+};
+
 struct setup_dpp_params {
        struct pipe_ctx *pipe_ctx;
 };
@@ -1058,6 +1066,7 @@ union block_sequence_params {
        struct update_info_frame_params update_info_frame_params;
        struct program_manual_trigger_params program_manual_trigger_params;
        struct send_dmcub_cmd_params send_dmcub_cmd_params;
+       struct lsdma_send_pio_copy_params lsdma_send_pio_copy_params;
        struct setup_dpp_params setup_dpp_params;
        struct program_bias_and_scale_params program_bias_and_scale_params;
        struct set_output_transfer_func_params set_output_transfer_func_params;
@@ -1236,6 +1245,7 @@ enum block_sequence_func {
        HUBP_SET_DMDATA_ATTRIBUTES,
        OPTC_PROGRAM_MANUAL_TRIGGER,
        DMUB_SEND_DMCUB_CMD,
+       LSDMA_SEND_PIO_COPY,
        DPP_SETUP_DPP,
        DPP_PROGRAM_BIAS_AND_SCALE,
        DPP_SET_OUTPUT_TRANSFER_FUNC,
@@ -1847,6 +1857,7 @@ void hwss_process_outstanding_hw_updates(struct dc *dc,
                struct dc_state *dc_context);
 
 void hwss_send_dmcub_cmd(union block_sequence_params *params);
+void hwss_lsdma_send_pio_copy(union block_sequence_params *params);
 
 void hwss_program_manual_trigger(union block_sequence_params *params);
 
@@ -2205,6 +2216,9 @@ void hwss_add_dmub_send_dmcub_cmd(struct 
block_sequence_state *seq_state,
 void hwss_add_dmub_subvp_save_surf_addr(struct block_sequence_state *seq_state,
                struct dc_dmub_srv *dc_dmub_srv, struct dc_plane_address *addr, 
uint8_t subvp_index);
 
+void hwss_add_lsdma_send_pio_copy(struct block_sequence_state *seq_state,
+               struct dc_dmub_srv *dc_dmub_srv, uint64_t src_addr, uint64_t 
dst_addr,
+               uint32_t byte_count, uint32_t overlap_disable);
 void hwss_add_hubp_wait_pipe_read_start(struct block_sequence_state *seq_state,
                struct hubp *hubp);
 
diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_status.h 
b/drivers/gpu/drm/amd/display/dc/inc/core_status.h
index 1a17e727ed04..69215d0e8f46 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/core_status.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_status.h
@@ -72,6 +72,8 @@ enum dc_status {
        /// Handshake failed, programming aborted, DCN may be in inconsistent 
state.
        DC_DPMS_FAILED_INCOMPLETE = 33,
 
+       DC_NO_DRAM_BUFFER_RESOURCE = 34,
+
        DC_ERROR_UNEXPECTED = -1
 };
 
diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h 
b/drivers/gpu/drm/amd/display/dc/inc/core_types.h
index b18d607fb222..d2cca2c97a41 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h
@@ -247,6 +247,8 @@ struct audio_support{
 #define NO_UNDERLAY_PIPE -1
 
 struct resource_pool {
+       struct dc_context *ctx;
+
        struct mem_input *mis[MAX_PIPES];
        struct hubp *hubps[MAX_PIPES];
        struct input_pixel_processor *ipps[MAX_PIPES];
@@ -264,6 +266,13 @@ struct resource_pool {
        struct dce_i2c_sw *sw_i2cs[MAX_PIPES];
        bool i2c_hw_buffer_in_use;
 
+       /* LSDMA scratch memory. buffer is NULL when not allocated. */
+       struct {
+               void *buffer;
+               long long pa;
+               unsigned int size;
+       } lsdma_scratch;
+
        struct dwbc *dwbc[MAX_DWB_PIPES];
        struct mcif_wb *mcif_wb[MAX_DWB_PIPES];
        struct {
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c 
b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
index b52d2179532e..534adb1e0ba6 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
@@ -3,6 +3,7 @@
 // Copyright 2024 Advanced Micro Devices, Inc.
 
 #include "dm_services.h"
+#include "dm_helpers.h"
 #include "dc.h"
 
 #include "dcn32/dcn32_init.h"
@@ -84,6 +85,8 @@
 #include "dml2_wrapper/dml2_wrapper.h"
 #include "dml2_wrapper/dml21_wrapper/dml21_wrapper.h"
 
+#define LSDMA_CONTENTION_BUFFER_SIZE (64 * 1024 * 1024)
+
 #define DC_LOGGER_INIT(logger)
 
 /* begin *********************
@@ -1627,6 +1630,12 @@ static void dcn60_resource_destruct(struct 
dcn60_resource_pool *pool)
 {
        unsigned int i;
 
+       if (pool->base.lsdma_scratch.buffer) {
+               dm_helpers_free_gpu_mem(pool->base.ctx,
+                               DC_MEM_ALLOC_TYPE_GART, 
pool->base.lsdma_scratch.buffer);
+               pool->base.lsdma_scratch.buffer = NULL;
+       }
+
        for (i = 0; i < pool->base.stream_enc_count; i++) {
                if (pool->base.stream_enc[i] != NULL) {
                        if (pool->base.stream_enc[i]->vpg != NULL) {
@@ -2043,6 +2052,16 @@ static bool dcn60_resource_construct(
        dc->caps.utm_support = true;
        dc->caps.max_v_total = (1 << 15) - 1;
 
+       pool->base.ctx = ctx;
+
+       if (dc->config.lsdma_peak_bw_contention_support) {
+               pool->base.lsdma_scratch.buffer = 
dm_helpers_allocate_gpu_mem(ctx,
+                               DC_MEM_ALLOC_TYPE_GART, 
LSDMA_CONTENTION_BUFFER_SIZE,
+                               &pool->base.lsdma_scratch.pa);
+               if (pool->base.lsdma_scratch.buffer)
+                       pool->base.lsdma_scratch.size = 
LSDMA_CONTENTION_BUFFER_SIZE;
+       }
+
        if (ASICREV_IS_GC_12_0_1_A0(dc->ctx->asic_id.hw_internal_rev))
                dc->caps.dcc_plane_width_limit = 7680;
 
-- 
2.43.0

Reply via email to