* [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex
@ 2026-06-08 6:20 Fang Wang
2026-06-08 6:28 ` sashiko-bot
2026-06-09 0:51 ` Sasha Levin
0 siblings, 2 replies; 3+ messages in thread
From: Fang Wang @ 2026-06-08 6:20 UTC (permalink / raw)
To: gregkh, stable, linux
Cc: patches, linux-kernel, jdelvare, atull, broonie, linux-hwmon,
psanman
From: Guenter Roeck <linux@roeck-us.net>
[ Upstream commit 754bd2b4a084b90b5e7b630e1f423061a9b9b761 ]
The regulator operations pmbus_regulator_get_voltage(),
pmbus_regulator_set_voltage(), and pmbus_regulator_list_voltage()
access PMBus registers and shared data but were not protected by
the update_lock mutex. This could lead to race conditions.
However, adding mutex protection directly to these functions causes
a deadlock because pmbus_regulator_notify() (which calls
regulator_notifier_call_chain()) is often called with the mutex
already held (e.g., from pmbus_fault_handler()). If a regulator
callback then calls one of the now-protected voltage functions,
it will attempt to acquire the same mutex.
Rework pmbus_regulator_notify() to utilize a worker function to
send notifications outside of the mutex protection. Events are
stored as atomics in a per-page bitmask and processed by the worker.
Initialize the worker and its associated data during regulator
registration, and ensure it is cancelled on device removal using
devm_add_action_or_reset().
While at it, remove the unnecessary include of linux/of.h.
Cc: Sanman Pradhan <psanman@juniper.net>
Fixes: ddbb4db4ced1b ("hwmon: (pmbus) Add regulator support")
Reviewed-by: Sanman Pradhan <psanman@juniper.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Fang Wang <32840572@qq.com>
---
drivers/hwmon/pmbus/pmbus_core.c | 117 ++++++++++++++++++++++++-------
1 file changed, 91 insertions(+), 26 deletions(-)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 019c5982ba56..a61e2fb176da 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -6,6 +6,7 @@
* Copyright (c) 2012 Guenter Roeck
*/
+#include <linux/atomic.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
#include <linux/math64.h>
@@ -19,8 +20,8 @@
#include <linux/pmbus.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
-#include <linux/of.h>
#include <linux/thermal.h>
+#include <linux/workqueue.h>
#include "pmbus.h"
/*
@@ -102,6 +103,11 @@ struct pmbus_data {
struct mutex update_lock;
+#if IS_ENABLED(CONFIG_REGULATOR)
+ atomic_t regulator_events[PMBUS_PAGES];
+ struct work_struct regulator_notify_work;
+#endif
+
bool has_status_word; /* device uses STATUS_WORD register */
int (*read_status)(struct i2c_client *client, int page);
@@ -3056,12 +3062,19 @@ static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
.class = PSC_VOLTAGE_OUT,
.convert = true,
};
+ int ret;
+ mutex_lock(&data->update_lock);
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
- if (s.data < 0)
- return s.data;
+ if (s.data < 0) {
+ ret = s.data;
+ goto unlock;
+ }
- return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
+ ret = (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
+unlock:
+ mutex_unlock(&data->update_lock);
+ return ret;
}
static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
@@ -3078,16 +3091,22 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
};
int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
int low, high;
+ int ret;
*selector = 0;
+ mutex_lock(&data->update_lock);
low = pmbus_regulator_get_low_margin(client, s.page);
- if (low < 0)
- return low;
+ if (low < 0) {
+ ret = low;
+ goto unlock;
+ }
high = pmbus_regulator_get_high_margin(client, s.page);
- if (high < 0)
- return high;
+ if (high < 0) {
+ ret = high;
+ goto unlock;
+ }
/* Make sure we are within margins */
if (low > val)
@@ -3097,7 +3116,10 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
val = pmbus_data2reg(data, &s, val);
- return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
+ ret = _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
+unlock:
+ mutex_unlock(&data->update_lock);
+ return ret;
}
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
@@ -3105,7 +3127,9 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
int val, low, high;
+ int ret;
if (selector >= rdev->desc->n_voltages ||
selector < rdev->desc->linear_min_sel)
@@ -3115,18 +3139,29 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
(rdev->desc->uV_step * selector), 1000); /* convert to mV */
+ mutex_lock(&data->update_lock);
+
low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev));
- if (low < 0)
- return low;
+ if (low < 0) {
+ ret = low;
+ goto unlock;
+ }
high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev));
- if (high < 0)
- return high;
+ if (high < 0) {
+ ret = high;
+ goto unlock;
+ }
- if (val >= low && val <= high)
- return val * 1000; /* unit is uV */
+ if (val >= low && val <= high) {
+ ret = val * 1000; /* unit is uV */
+ goto unlock;
+ }
- return 0;
+ ret = 0;
+unlock:
+ mutex_unlock(&data->update_lock);
+ return ret;
}
const struct regulator_ops pmbus_regulator_ops = {
@@ -3141,12 +3176,42 @@ const struct regulator_ops pmbus_regulator_ops = {
};
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
+static void pmbus_regulator_notify_work_cancel(void *data)
+{
+ struct pmbus_data *pdata = data;
+
+ cancel_work_sync(&pdata->regulator_notify_work);
+}
+
+static void pmbus_regulator_notify_worker(struct work_struct *work)
+{
+ struct pmbus_data *data =
+ container_of(work, struct pmbus_data, regulator_notify_work);
+ int i, j;
+
+ for (i = 0; i < data->info->pages; i++) {
+ int event;
+
+ event = atomic_xchg(&data->regulator_events[i], 0);
+ if (!event)
+ continue;
+
+ for (j = 0; j < data->info->num_regulators; j++) {
+ if (i == rdev_get_id(data->rdevs[j])) {
+ regulator_notifier_call_chain(data->rdevs[j],
+ event, NULL);
+ break;
+ }
+ }
+ }
+}
+
static int pmbus_regulator_register(struct pmbus_data *data)
{
struct device *dev = data->dev;
const struct pmbus_driver_info *info = data->info;
const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
- int i;
+ int i, ret;
data->rdevs = devm_kzalloc(dev, sizeof(struct regulator_dev *) * info->num_regulators,
GFP_KERNEL);
@@ -3170,20 +3235,20 @@ static int pmbus_regulator_register(struct pmbus_data *data)
info->reg_desc[i].name);
}
+ INIT_WORK(&data->regulator_notify_work, pmbus_regulator_notify_worker);
+
+ ret = devm_add_action_or_reset(dev, pmbus_regulator_notify_work_cancel, data);
+ if (ret)
+ return ret;
+
return 0;
}
static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
{
- int j;
-
- for (j = 0; j < data->info->num_regulators; j++) {
- if (page == rdev_get_id(data->rdevs[j])) {
- regulator_notifier_call_chain(data->rdevs[j], event, NULL);
- break;
- }
- }
- return 0;
+ atomic_or(event, &data->regulator_events[page]);
+ schedule_work(&data->regulator_notify_work);
+ return 0;
}
#else
static int pmbus_regulator_register(struct pmbus_data *data)
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex
2026-06-08 6:20 [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex Fang Wang
@ 2026-06-08 6:28 ` sashiko-bot
2026-06-09 0:51 ` Sasha Levin
1 sibling, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-06-08 6:28 UTC (permalink / raw)
To: Fang Wang; +Cc: linux-hwmon
> From: Guenter Roeck <linux@roeck-us.net>
>
> [ Upstream commit 754bd2b4a084b90b5e7b630e1f423061a9b9b761 ]
>
> The regulator operations pmbus_regulator_get_voltage(),
> pmbus_regulator_set_voltage(), and pmbus_regulator_list_voltage()
> access PMBus registers and shared data but were not protected by
> the update_lock mutex. This could lead to race conditions.
>
> However, adding mutex protection directly to these functions causes
> a deadlock because pmbus_regulator_notify() (which calls
> regulator_notifier_call_chain()) is often called with the mutex
> already held (e.g., from pmbus_fault_handler()). If a regulator
> callback then calls one of the now-protected voltage functions,
> it will attempt to acquire the same mutex.
> [ ... ]
> Cc: Sanman Pradhan <psanman@juniper.net>
> Fixes: ddbb4db4ced1b ("hwmon: (pmbus) Add regulator support")
> Reviewed-by: Sanman Pradhan <psanman@juniper.net>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> Signed-off-by: Fang Wang <32840572@qq.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/tencent_53D6CAB7A20BCE168EA9DF22F0E78EF14509@qq.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex
2026-06-08 6:20 [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex Fang Wang
2026-06-08 6:28 ` sashiko-bot
@ 2026-06-09 0:51 ` Sasha Levin
1 sibling, 0 replies; 3+ messages in thread
From: Sasha Levin @ 2026-06-09 0:51 UTC (permalink / raw)
To: gregkh, stable, linux
Cc: Sasha Levin, patches, linux-kernel, jdelvare, atull, broonie,
linux-hwmon, psanman, Fang Wang
> [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex
Queued for 6.6, thanks.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-09 0:52 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-08 6:20 [PATCH 6.6.y] hwmon: (pmbus/core) Protect regulator operations with mutex Fang Wang
2026-06-08 6:28 ` sashiko-bot
2026-06-09 0:51 ` Sasha Levin
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.