Commit e5f84d1c authored by Thomas Weißschuh's avatar Thomas Weißschuh Committed by Sebastian Reichel
Browse files

power: supply: cros_charge-control: add mutex for driver data



Concurrent accesses through sysfs may lead to inconsistent state in the
priv data. Introduce a mutex to avoid this.

Fixes: c6ed48ef ("power: supply: add ChromeOS EC based charge control driver")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarThomas Weißschuh <linux@weissschuh.net>
Link: https://lore.kernel.org/r/20241208-cros_charge-control-v2-v1-1-8d168d0f08a3@weissschuh.net


Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
parent afc6e39e
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -7,8 +7,10 @@
#include <acpi/battery.h>
#include <linux/container_of.h>
#include <linux/dmi.h>
#include <linux/lockdep.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
@@ -49,6 +51,7 @@ struct cros_chctl_priv {
	struct attribute *attributes[_CROS_CHCTL_ATTR_COUNT];
	struct attribute_group group;

	struct mutex lock; /* protects fields below and cros_ec */
	enum power_supply_charge_behaviour current_behaviour;
	u8 current_start_threshold, current_end_threshold;
};
@@ -85,6 +88,8 @@ static int cros_chctl_configure_ec(struct cros_chctl_priv *priv)
{
	struct ec_params_charge_control req = {};

	lockdep_assert_held(&priv->lock);

	req.cmd = EC_CHARGE_CONTROL_CMD_SET;

	switch (priv->current_behaviour) {
@@ -159,6 +164,7 @@ static ssize_t charge_control_start_threshold_show(struct device *dev,
	struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
							       CROS_CHCTL_ATTR_START_THRESHOLD);

	guard(mutex)(&priv->lock);
	return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_start_threshold);
}

@@ -169,6 +175,7 @@ static ssize_t charge_control_start_threshold_store(struct device *dev,
	struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
							       CROS_CHCTL_ATTR_START_THRESHOLD);

	guard(mutex)(&priv->lock);
	return cros_chctl_store_threshold(dev, priv, 0, buf, count);
}

@@ -178,6 +185,7 @@ static ssize_t charge_control_end_threshold_show(struct device *dev, struct devi
	struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
							       CROS_CHCTL_ATTR_END_THRESHOLD);

	guard(mutex)(&priv->lock);
	return sysfs_emit(buf, "%u\n", (unsigned int)priv->current_end_threshold);
}

@@ -187,6 +195,7 @@ static ssize_t charge_control_end_threshold_store(struct device *dev, struct dev
	struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
							       CROS_CHCTL_ATTR_END_THRESHOLD);

	guard(mutex)(&priv->lock);
	return cros_chctl_store_threshold(dev, priv, 1, buf, count);
}

@@ -195,6 +204,7 @@ static ssize_t charge_behaviour_show(struct device *dev, struct device_attribute
	struct cros_chctl_priv *priv = cros_chctl_attr_to_priv(&attr->attr,
							       CROS_CHCTL_ATTR_CHARGE_BEHAVIOUR);

	guard(mutex)(&priv->lock);
	return power_supply_charge_behaviour_show(dev, EC_CHARGE_CONTROL_BEHAVIOURS,
						  priv->current_behaviour, buf);
}
@@ -210,6 +220,7 @@ static ssize_t charge_behaviour_store(struct device *dev, struct device_attribut
	if (ret < 0)
		return ret;

	guard(mutex)(&priv->lock);
	priv->current_behaviour = ret;

	ret = cros_chctl_configure_ec(priv);
@@ -290,6 +301,10 @@ static int cros_chctl_probe(struct platform_device *pdev)
	if (!priv)
		return -ENOMEM;

	ret = devm_mutex_init(dev, &priv->lock);
	if (ret)
		return ret;

	ret = cros_ec_get_cmd_versions(cros_ec, EC_CMD_CHARGE_CONTROL);
	if (ret < 0)
		return ret;
@@ -327,6 +342,7 @@ static int cros_chctl_probe(struct platform_device *pdev)
	priv->current_end_threshold = 100;

	/* Bring EC into well-known state */
	scoped_guard(mutex, &priv->lock)
		ret = cros_chctl_configure_ec(priv);
	if (ret < 0)
		return ret;