mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 17:42:03 +02:00
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:
parent
5622564fc6
commit
8da4fedd5d
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user