Re: [PATCH v6 4/4] hwmon: pmbus: mpq8646: gate the writes

Guenter Roeck <[email protected]> Wed, 29 Jul 2026 21:14:36 -0700
Newsgroups org.kernel.vger.linux-hwmon,dev.linux.lists.sashiko-reviews,org.kernel.vger.linux-devicetree
Message-ID <[email protected]>
On 7/29/26 15:07, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] Debugfs attributes perform I2C accesses without acquiring necessary locks, risking race conditions and bypassing hardware write protection.
> - [Medium] Mixing goto-based error handling with guard() declarations in the same function.
> - [Medium] Debugfs write functions mask negative error codes by returning 0 (success) on hardware failure.
> --
> 

Feedback seems valid. Please address.

Thanks,
Guenter

> --- Patch [4]: [PATCH v6 4/4] hwmon: pmbus: mpq8646: gate the writes ---
> commit ec4599677b214a57f0796000d18d9f3b6e5d4fb5
> Author: Vincent Jardin <[email protected]>
> 
> hwmon: pmbus: mpq8646: gate the writes
> 
> This commit adds unsafe debugfs provisioning and benchmarking helpers
> to write specific registers. These operations are restricted due to the
> risk of browning out the board or persisting bad setpoints.
> 
>> diff --git a/drivers/hwmon/pmbus/mpq8646.c b/drivers/hwmon/pmbus/mpq8646.c
>> index 1fd41d89e5bef..0034cf5919345 100644
>> --- a/drivers/hwmon/pmbus/mpq8646.c
>> +++ b/drivers/hwmon/pmbus/mpq8646.c
> [ ... ]
>> +static int mpq8646_dbg_clear_protection_last(void *data, u64 val)
>> +{
> [ ... ]
>> +	scoped_guard(mutex, &priv->mps_lock)
>> +		rc = i2c_smbus_write_byte(priv->client, MPS_CLEAR_LAST_FAULT);
>> +	if (rc < 0)
>> +		dev_warn(&priv->client->dev,
>> +			 "clear_protection_last: CLEAR_LAST_FAULT write failed (%d)\n",
>> +			 rc);
>> +	return 0;
>> +}
> 
> [Severity: Medium]
> Should this propagate the negative error code from rc instead of returning 0?
> Returning 0 masks hardware faults and might lead userspace provisioning
> scripts to assume the write succeeded.
> 
> [ ... ]
>> +static int mpq8646_dbg_clear_protection_last_force(void *data, u64 val)
>> +{
> [ ... ]
>> +	guard(pmbus_lock)(priv->client);
>> +	guard(mutex)(&priv->mps_lock);
>> +
>> +	wp_orig = i2c_smbus_read_byte_data(priv->client, PMBUS_WRITE_PROTECT);
>> +	if (wp_orig < 0) {
>> +		dev_warn(&priv->client->dev,
>> +			 "clear_protection_last_force: WRITE_PROTECT read failed (%d), aborting\n",
>> +			 wp_orig);
>> +		return 0;
>> +	}
> 
> [Severity: Medium]
> Like above, should this return the negative error code in wp_orig so the I2C
> read failure is reported to the caller?
> 
> [ ... ]
>> +	rc = i2c_smbus_write_word_data(priv->client, MPS_MFR_CFG_EXT,
>> +				       (u16)cfg_orig | MPS_MFR_CFG_EXT_CLR_LAST_EN);
>> +	if (rc < 0) {
>> +		dev_warn(&priv->client->dev,
>> +			 "clear_protection_last_force: gate open failed (%d)\n",
>> +			 rc);
>> +		goto restore_wp;
>> +	}
> 
> [Severity: Medium]
> Does this mix goto-based error handling with guard() declarations in the
> same function?
> 
> The cleanup guidelines specify that functions should either use scope-based
> cleanup exclusively or not at all, to avoid confusing ownership semantics.
> 
> [ ... ]
>> +static int mpq8646_dbg_store_all(void *data, u64 val)
>> +{
> [ ... ]
>> +	mpq8646_unsafe_warn_once(priv);
>> +	scoped_guard(mutex, &priv->mps_lock)
>> +		rc = i2c_smbus_write_byte(priv->client, PMBUS_STORE_USER_ALL);
> 
> [Severity: High]
> Should this also acquire the global pmbus_lock?
> 
> Issuing STORE_USER_ALL without the pmbus_lock might expose an unprotected
> NVM-busy window to PMBus core background polling.
> 
>> +	if (rc < 0)
>> +		dev_warn(&priv->client->dev,
>> +			 "store_all: STORE_USER_ALL (0x15) write failed (%d)\n",
>> +			 rc);
>> +	return 0;
>> +}
> 
> [Severity: Medium]
> Does this need to return the negative error code in rc instead of 0 on
> failure?
> 
> [ ... ]
>> +static int mpq8646_dbg_restore_all(void *data, u64 val)
>> +{
> [ ... ]
>> +	mpq8646_unsafe_warn_once(priv);
>> +	scoped_guard(mutex, &priv->mps_lock)
>> +		rc = i2c_smbus_write_byte(priv->client, PMBUS_RESTORE_USER_ALL);
> 
> [Severity: High]
> As with mpq8646_dbg_store_all(), should this also hold pmbus_lock to
> prevent NVM-busy window races?
> 
>> +	if (rc < 0)
>> +		dev_warn(&priv->client->dev,
>> +			 "restore_all: RESTORE_USER_ALL (0x16) write failed (%d)\n",
>> +			 rc);
>> +	return 0;
>> +}
> 
> [Severity: Medium]
> Should this return the negative error code in rc?
> 
> [ ... ]
>> +static int mpq8646_dbg_reg_set(void *data, u64 val)
>> +{
>> +	struct mpq8646_dbg_reg_ctx *ctx = data;
>> +	int rc;
>> +
>> +	mpq8646_unsafe_warn_once(ctx->priv);
>> +	if (ctx->desc->is_word)
>> +		rc = i2c_smbus_write_word_data(ctx->priv->client,
>> +					       ctx->desc->reg, (u16)val);
>> +	else
>> +		rc = i2c_smbus_write_byte_data(ctx->priv->client,
>> +					       ctx->desc->reg, (u8)val);
> 
> [Severity: High]
> Does this need to acquire pmbus_lock or mps_lock before performing the I2C
> write?
> 
> If another thread concurrently calls mpq8646_dbg_clear_protection_last_force()
> which temporarily lowers the PMBUS_WRITE_PROTECT register, could a direct
> write here slip through and bypass the hardware write protection?
>