From: Shyam Sundar S K Date: Tue, 2 Dec 2025 04:22:19 +0000 (+0530) Subject: platform/x86/amd/pmf: Use ring buffer to store custom BIOS input values X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2a2c085de1f3f54a6222fbef5b45f1d3c40e98e3;p=thirdparty%2Flinux.git platform/x86/amd/pmf: Use ring buffer to store custom BIOS input values Custom BIOS input values can be updated by multiple sources, such as power mode changes and sensor events, each triggering a custom BIOS input event. When these events occur in rapid succession, new data may overwrite previous values before they are processed, resulting in lost updates. To address this, introduce a fixed-size, power-of-two ring buffer to capture every custom BIOS input event, storing both the pending request and its associated input values. Access to the ring buffer is synchronized using a mutex. The previous use of memset() to clear the pending request structure after each event is removed, as each BIOS input value is now copied into the buffer as a snapshot. Consumers now process entries directly from the ring buffer, making explicit clearing of the pending request structure unnecessary. Reviewed-by: Mario Limonciello (AMD) Tested-by: Yijun Shen Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/20251202042219.245173-1-Shyam-sundar.S-k@amd.com Signed-off-by: Ilpo Järvinen --- diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 13c4fec2c7ef9..3d94b03cf7947 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -9,6 +9,9 @@ */ #include +#include +#include +#include #include "pmf.h" #define APMF_CQL_NOTIFICATION 2 @@ -331,6 +334,39 @@ int apmf_get_sbios_requests(struct amd_pmf_dev *pdev, struct apmf_sbios_req *req req, sizeof(*req)); } +/* Store custom BIOS inputs data in ring buffer */ +static void amd_pmf_custom_bios_inputs_rb(struct amd_pmf_dev *pmf_dev) +{ + struct pmf_cbi_ring_buffer *rb = &pmf_dev->cbi_buf; + int i; + + guard(mutex)(&pmf_dev->cbi_mutex); + + switch (pmf_dev->cpu_id) { + case AMD_CPU_ID_PS: + for (i = 0; i < ARRAY_SIZE(custom_bios_inputs_v1); i++) + rb->data[rb->head].val[i] = pmf_dev->req1.custom_policy[i]; + rb->data[rb->head].preq = pmf_dev->req1.pending_req; + break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: + for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) + rb->data[rb->head].val[i] = pmf_dev->req.custom_policy[i]; + rb->data[rb->head].preq = pmf_dev->req.pending_req; + break; + default: + return; + } + + if (CIRC_SPACE(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0) { + /* Rare case: ensures the newest BIOS input value is kept */ + dev_warn(pmf_dev->dev, "Overwriting BIOS input value, data may be lost\n"); + rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1); + } + + rb->head = (rb->head + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1); +} + static void amd_pmf_handle_early_preq(struct amd_pmf_dev *pdev) { if (!pdev->cb_flag) @@ -356,6 +392,8 @@ static void apmf_event_handler_v2(acpi_handle handle, u32 event, void *data) dev_dbg(pmf_dev->dev, "Pending request (preq): 0x%x\n", pmf_dev->req.pending_req); amd_pmf_handle_early_preq(pmf_dev); + + amd_pmf_custom_bios_inputs_rb(pmf_dev); } static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data) @@ -374,6 +412,8 @@ static void apmf_event_handler_v1(acpi_handle handle, u32 event, void *data) dev_dbg(pmf_dev->dev, "Pending request (preq1): 0x%x\n", pmf_dev->req1.pending_req); amd_pmf_handle_early_preq(pmf_dev); + + amd_pmf_custom_bios_inputs_rb(pmf_dev); } static void apmf_event_handler(acpi_handle handle, u32 event, void *data) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 8fc293c9c5380..9f4a1f79459a5 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -477,6 +478,10 @@ static int amd_pmf_probe(struct platform_device *pdev) if (err) return err; + err = devm_mutex_init(dev->dev, &dev->cbi_mutex); + if (err) + return err; + apmf_acpi_init(dev); platform_set_drvdata(pdev, dev); amd_pmf_dbgfs_register(dev); diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 9144c8c3bbaf2..e65a7eca05088 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -12,7 +12,9 @@ #define PMF_H #include +#include #include +#include #include #include @@ -120,6 +122,7 @@ struct cookie_header { #define APTS_MAX_STATES 16 #define CUSTOM_BIOS_INPUT_BITS GENMASK(16, 7) #define BIOS_INPUTS_MAX 10 +#define CUSTOM_BIOS_INPUT_RING_ENTRIES 64 /* Must be power of two for CIRC_* macros */ /* amd_pmf_send_cmd() set/get */ #define SET_CMD false @@ -365,6 +368,22 @@ struct pmf_bios_inputs_prev { u32 custom_bios_inputs[BIOS_INPUTS_MAX]; }; +/** + * struct pmf_bios_input_entry - Snapshot of custom BIOS input event + * @val: Array of custom BIOS input values + * @preq: Pending request value associated with this event + */ +struct pmf_bios_input_entry { + u32 val[BIOS_INPUTS_MAX]; + u32 preq; +}; + +struct pmf_cbi_ring_buffer { + struct pmf_bios_input_entry data[CUSTOM_BIOS_INPUT_RING_ENTRIES]; + int head; + int tail; +}; + struct amd_pmf_dev { void __iomem *regbase; void __iomem *smu_virt_addr; @@ -413,6 +432,8 @@ struct amd_pmf_dev { struct apmf_sbios_req_v1 req1; struct pmf_bios_inputs_prev cb_prev; /* To preserve custom BIOS inputs */ bool cb_flag; /* To handle first custom BIOS input */ + struct pmf_cbi_ring_buffer cbi_buf; + struct mutex cbi_mutex; /* Protects ring buffer access */ }; struct apmf_sps_prop_granular_v2 { diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index 0a37dc6a79500..f48678a23cc71 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include "pmf.h" @@ -132,32 +133,39 @@ static void amd_pmf_set_ta_custom_bios_input(struct ta_pmf_enact_table *in, int } } -static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, u32 pending_req, +static void amd_pmf_update_bios_inputs(struct amd_pmf_dev *pdev, struct pmf_bios_input_entry *data, const struct amd_pmf_pb_bitmap *inputs, - const u32 *custom_policy, struct ta_pmf_enact_table *in) + struct ta_pmf_enact_table *in) { unsigned int i; for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) { - if (!(pending_req & inputs[i].bit_mask)) + if (!(data->preq & inputs[i].bit_mask)) continue; - amd_pmf_set_ta_custom_bios_input(in, i, custom_policy[i]); - pdev->cb_prev.custom_bios_inputs[i] = custom_policy[i]; - dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, custom_policy[i]); + amd_pmf_set_ta_custom_bios_input(in, i, data->val[i]); + pdev->cb_prev.custom_bios_inputs[i] = data->val[i]; + dev_dbg(pdev->dev, "Custom BIOS Input[%d]: %u\n", i, data->val[i]); } } static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev, struct ta_pmf_enact_table *in) { + struct pmf_cbi_ring_buffer *rb = &pdev->cbi_buf; unsigned int i; + guard(mutex)(&pdev->cbi_mutex); + for (i = 0; i < ARRAY_SIZE(custom_bios_inputs); i++) amd_pmf_set_ta_custom_bios_input(in, i, pdev->cb_prev.custom_bios_inputs[i]); - if (!(pdev->req.pending_req || pdev->req1.pending_req)) + if (CIRC_CNT(rb->head, rb->tail, CUSTOM_BIOS_INPUT_RING_ENTRIES) == 0) return; + /* If no active custom BIOS input pending request, do not consume further work */ + if (!rb->data[rb->tail].preq) + goto out_rbadvance; + if (!pdev->smart_pc_enabled) return; @@ -165,20 +173,17 @@ static void amd_pmf_get_custom_bios_inputs(struct amd_pmf_dev *pdev, case PMF_IF_V1: if (!is_apmf_bios_input_notifications_supported(pdev)) return; - amd_pmf_update_bios_inputs(pdev, pdev->req1.pending_req, custom_bios_inputs_v1, - pdev->req1.custom_policy, in); + amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs_v1, in); break; case PMF_IF_V2: - amd_pmf_update_bios_inputs(pdev, pdev->req.pending_req, custom_bios_inputs, - pdev->req.custom_policy, in); + amd_pmf_update_bios_inputs(pdev, &rb->data[rb->tail], custom_bios_inputs, in); break; default: break; } - /* Clear pending requests after handling */ - memset(&pdev->req, 0, sizeof(pdev->req)); - memset(&pdev->req1, 0, sizeof(pdev->req1)); +out_rbadvance: + rb->tail = (rb->tail + 1) & (CUSTOM_BIOS_INPUT_RING_ENTRIES - 1); } static void amd_pmf_get_c0_residency(u16 *core_res, size_t size, struct ta_pmf_enact_table *in) diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 0abce76f89ffe..cec8b38c1afed 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -591,6 +591,8 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) status = ret == TA_PMF_TYPE_SUCCESS; if (status) { dev->cb_flag = true; + dev->cbi_buf.head = 0; + dev->cbi_buf.tail = 0; break; } amd_pmf_tee_deinit(dev);