The power_supply_get_by_phandle() and power_supply_get_by_name() use function class_find_device() for obtaining the reference to power supply. Each use of class_find_device() increases the power supply's device reference counter.
However the reference counter was not decreased by users of this API. Thus final device_unregister() call from power_supply_unregister() could not release the device and clean up its resources. This lead to memory leak if one pf power_supply_get_*() was called between registering and unregistering the power supply. Add and document new API power_supply_put() for decrementing the reference counter. Signed-off-by: Krzysztof Kozlowski <k.kozlow...@samsung.com> --- drivers/power/power_supply_core.c | 41 +++++++++++++++++++++++++++++++++++++++ include/linux/power_supply.h | 1 + 2 files changed, 42 insertions(+) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 8e36986a73e1..ca2b6442e0b3 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -331,6 +331,17 @@ static int power_supply_match_device_by_name(struct device *dev, const void *dat return strcmp(psy->name, name) == 0; } +/** + * power_supply_get_by_name() - Search for a power supply and returns its ref + * @name: Power supply name to fetch + * + * If power supply was found, it increases reference count for the + * internal power supply's device. The user should power_supply_put() + * after usage. + * + * Return: On success returns a reference to a power supply with + * matching name equals to @name, a NULL otherwise. + */ struct power_supply *power_supply_get_by_name(const char *name) { struct device *dev = class_find_device(power_supply_class, NULL, name, @@ -340,12 +351,42 @@ struct power_supply *power_supply_get_by_name(const char *name) } EXPORT_SYMBOL_GPL(power_supply_get_by_name); +/** + * power_supply_put() - Drop reference obtained with power_supply_get_by_name + * @psy: Reference to put + * + * The reference to power supply should be put before unregistering + * the power supply. + */ +void power_supply_put(struct power_supply *psy) +{ + might_sleep(); + + if (!psy) + return; + + put_device(psy->dev); +} +EXPORT_SYMBOL_GPL(power_supply_put); + #ifdef CONFIG_OF static int power_supply_match_device_node(struct device *dev, const void *data) { return dev->parent && dev->parent->of_node == data; } +/** + * power_supply_get_by_phandle() - Search for a power supply and returns its ref + * @np: Pointer to device node holding phandle property + * @phandle_name: Name of property holding a power supply name + * + * If power supply was found, it increases reference count for the + * internal power supply's device. The user should power_supply_put() + * after usage. + * + * Return: On success returns a reference to a power supply with + * matching name equals to value under @property, NULL or ERR_PTR otherwise. + */ struct power_supply *power_supply_get_by_phandle(struct device_node *np, const char *property) { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 44e749c65505..1b6db883e090 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -256,6 +256,7 @@ extern struct atomic_notifier_head power_supply_notifier; extern int power_supply_reg_notifier(struct notifier_block *nb); extern void power_supply_unreg_notifier(struct notifier_block *nb); extern struct power_supply *power_supply_get_by_name(const char *name); +extern void power_supply_put(struct power_supply *psy); #ifdef CONFIG_OF extern struct power_supply *power_supply_get_by_phandle(struct device_node *np, const char *property); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/