platform/x86/amd/hsmp: Serialize per-socket metric table reads with a mutex

HSMP_GET_METRIC_TABLE makes the firmware refill a shared per-socket metric
DRAM region, which hsmp_metric_tbl_read() then copies out with
memcpy_fromio(). Two concurrent readers of the metrics_bin sysfs attribute
on the same socket can race: one can trigger a fresh fill while the other
is mid-copy and return a torn snapshot. (The hwmon path does not touch
this region; it only issues power messages via hsmp_send_message().)

Embed a struct mutex metric_read_lock in each hsmp_socket and hold it
across the fill-and-copy in hsmp_metric_tbl_read(). Add
hsmp_init_metric_read_locks() and hsmp_destroy_metric_read_locks(), which
take only struct hsmp_plat_device and iterate pdev->sock[] over
pdev->num_sockets so the caller cannot pass a count that disagrees with the
array.

Wire them into both front-ends' probe and teardown paths so the mutex is
always initialized before metrics_bin is exposed: the platform driver and
the ACPI driver both drive hsmp_metric_tbl_read() through the same 0444
metrics_bin attribute. Doing this in one patch avoids a bisection point
where an ACPI read would lock an uninitialized mutex.

Signed-off-by: Muralidhara M K <muralidhara.mk@amd.com>
Link: https://patch.msgid.link/20260723094656.3806028-4-muralidhara.mk@amd.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Muralidhara M K 2026-07-23 15:16:53 +05:30 committed by Ilpo Järvinen
parent 5622564fc6
commit 8da4fedd5d
No known key found for this signature in database
GPG Key ID: 59AC4F6153E5CE31
4 changed files with 39 additions and 0 deletions

View File

@ -639,6 +639,8 @@ static int hsmp_acpi_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!hsmp_pdev->sock)
return -ENOMEM;
hsmp_init_metric_read_locks(hsmp_pdev);
}
ret = init_acpi(&pdev->dev);
@ -670,6 +672,7 @@ static void hsmp_acpi_remove(struct platform_device *pdev)
*/
if (hsmp_pdev->is_probed) {
hsmp_misc_deregister();
hsmp_destroy_metric_read_locks(hsmp_pdev);
hsmp_pdev->is_probed = false;
}
}

View File

@ -10,9 +10,11 @@
#include <asm/amd/hsmp.h>
#include <linux/acpi.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/semaphore.h>
#include <linux/sysfs.h>
@ -415,6 +417,14 @@ ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size)
msg.msg_id = HSMP_GET_METRIC_TABLE;
msg.sock_ind = sock->sock_ind;
/*
* HSMP_GET_METRIC_TABLE makes firmware refill this socket's shared
* metric DRAM region, which is then copied out below. Hold the
* per-socket lock across the fill-and-copy so concurrent readers of the
* same socket cannot return a torn snapshot.
*/
guard(mutex)(&sock->metric_read_lock);
ret = hsmp_send_message(&msg);
if (ret)
return ret;
@ -424,6 +434,24 @@ ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size)
}
EXPORT_SYMBOL_NS_GPL(hsmp_metric_tbl_read, "AMD_HSMP");
void hsmp_init_metric_read_locks(struct hsmp_plat_device *pdev)
{
u16 i;
for (i = 0; i < pdev->num_sockets; i++)
mutex_init(&pdev->sock[i].metric_read_lock);
}
EXPORT_SYMBOL_NS_GPL(hsmp_init_metric_read_locks, "AMD_HSMP");
void hsmp_destroy_metric_read_locks(struct hsmp_plat_device *pdev)
{
u16 i;
for (i = 0; i < pdev->num_sockets; i++)
mutex_destroy(&pdev->sock[i].metric_read_lock);
}
EXPORT_SYMBOL_NS_GPL(hsmp_destroy_metric_read_locks, "AMD_HSMP");
void hsmp_unmap_metric_tbls(struct hsmp_plat_device *pdev)
{
struct hsmp_socket *sock;

View File

@ -15,6 +15,7 @@
#include <linux/hwmon.h>
#include <linux/kconfig.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/rwsem.h>
#include <linux/semaphore.h>
@ -44,6 +45,8 @@ struct hsmp_socket {
void __iomem *metric_tbl_addr;
void __iomem *virt_base_addr;
struct semaphore hsmp_sem;
/* Serializes HSMP_GET_METRIC_TABLE fill-and-copy for this socket */
struct mutex metric_read_lock;
char name[HSMP_ATTR_GRP_NAME_SIZE];
struct device *dev;
u16 sock_ind;
@ -65,6 +68,8 @@ void hsmp_misc_deregister(void);
int hsmp_misc_register(struct device *dev);
int hsmp_get_tbl_dram_base(u16 sock_ind);
void hsmp_unmap_metric_tbls(struct hsmp_plat_device *pdev);
void hsmp_init_metric_read_locks(struct hsmp_plat_device *pdev);
void hsmp_destroy_metric_read_locks(struct hsmp_plat_device *pdev);
ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size);
struct hsmp_plat_device *get_hsmp_pdev(void);
#if IS_ENABLED(CONFIG_HWMON)

View File

@ -212,6 +212,7 @@ static int init_platform_device(struct device *dev)
static void hsmp_pltdrv_release(void *data)
{
hsmp_unmap_metric_tbls(hsmp_pdev);
hsmp_destroy_metric_read_locks(hsmp_pdev);
}
static int hsmp_pltdrv_probe(struct platform_device *pdev)
@ -224,6 +225,8 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev)
if (!hsmp_pdev->sock)
return -ENOMEM;
hsmp_init_metric_read_locks(hsmp_pdev);
ret = devm_add_action_or_reset(&pdev->dev, hsmp_pltdrv_release, NULL);
if (ret)
return ret;