aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKrzysztof Kozlowski <k.kozlowski@samsung.com>2015-03-12 03:44:03 -0400
committerSebastian Reichel <sre@kernel.org>2015-03-13 18:15:48 -0400
commitbc1540561c9ede1efb6d7bf44804676d3d02a3cc (patch)
tree9ca0a0051c6a5382e00ec8ced1c56e93378ad9fa
parent2dc9215d7c94f7f9f34ccf8b1710ad73d82f6216 (diff)
power_supply: Add API for safe access of power supply function attrs
Add simple wrappers for accessing power supply's function attributes: - get_property -> power_supply_get_property - set_property -> power_supply_set_property - property_is_writeable -> power_supply_property_is_writeable - external_power_changed -> power_supply_external_power_changed This API along with atomic usage counter adds a safe way of accessing a power supply from another driver. If power supply is unregistered after obtaining reference to it by some driver, then the API wrappers won't be executed in invalid (freed) context. Next patch changing the ownership of power supply class is still needed to fully fix race conditions in accessing freed power supply. Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Reviewed-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Reviewed-by: Sebastian Reichel <sre@kernel.org> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r--drivers/power/power_supply_core.c47
-rw-r--r--include/linux/power_supply.h16
2 files changed, 62 insertions, 1 deletions
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index a21e36ed8d41..583dece8845b 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -314,7 +314,9 @@ EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
314 314
315int power_supply_set_battery_charged(struct power_supply *psy) 315int power_supply_set_battery_charged(struct power_supply *psy)
316{ 316{
317 if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) { 317 if (atomic_read(&psy->use_cnt) >= 0 &&
318 psy->type == POWER_SUPPLY_TYPE_BATTERY &&
319 psy->set_charged) {
318 psy->set_charged(psy); 320 psy->set_charged(psy);
319 return 0; 321 return 0;
320 } 322 }
@@ -366,6 +368,47 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np,
366EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); 368EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
367#endif /* CONFIG_OF */ 369#endif /* CONFIG_OF */
368 370
371int power_supply_get_property(struct power_supply *psy,
372 enum power_supply_property psp,
373 union power_supply_propval *val)
374{
375 if (atomic_read(&psy->use_cnt) <= 0)
376 return -ENODEV;
377
378 return psy->get_property(psy, psp, val);
379}
380EXPORT_SYMBOL_GPL(power_supply_get_property);
381
382int power_supply_set_property(struct power_supply *psy,
383 enum power_supply_property psp,
384 const union power_supply_propval *val)
385{
386 if (atomic_read(&psy->use_cnt) <= 0 || !psy->set_property)
387 return -ENODEV;
388
389 return psy->set_property(psy, psp, val);
390}
391EXPORT_SYMBOL_GPL(power_supply_set_property);
392
393int power_supply_property_is_writeable(struct power_supply *psy,
394 enum power_supply_property psp)
395{
396 if (atomic_read(&psy->use_cnt) <= 0 || !psy->property_is_writeable)
397 return -ENODEV;
398
399 return psy->property_is_writeable(psy, psp);
400}
401EXPORT_SYMBOL_GPL(power_supply_property_is_writeable);
402
403void power_supply_external_power_changed(struct power_supply *psy)
404{
405 if (atomic_read(&psy->use_cnt) <= 0 || !psy->external_power_changed)
406 return;
407
408 psy->external_power_changed(psy);
409}
410EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
411
369int power_supply_powers(struct power_supply *psy, struct device *dev) 412int power_supply_powers(struct power_supply *psy, struct device *dev)
370{ 413{
371 return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); 414 return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
@@ -555,6 +598,7 @@ static int __power_supply_register(struct device *parent,
555 dev->release = power_supply_dev_release; 598 dev->release = power_supply_dev_release;
556 dev_set_drvdata(dev, psy); 599 dev_set_drvdata(dev, psy);
557 psy->dev = dev; 600 psy->dev = dev;
601 atomic_inc(&psy->use_cnt);
558 if (cfg) { 602 if (cfg) {
559 psy->drv_data = cfg->drv_data; 603 psy->drv_data = cfg->drv_data;
560 psy->of_node = cfg->of_node; 604 psy->of_node = cfg->of_node;
@@ -676,6 +720,7 @@ EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws);
676 720
677void power_supply_unregister(struct power_supply *psy) 721void power_supply_unregister(struct power_supply *psy)
678{ 722{
723 WARN_ON(atomic_dec_return(&psy->use_cnt));
679 cancel_work_sync(&psy->changed_work); 724 cancel_work_sync(&psy->changed_work);
680 sysfs_remove_link(&psy->dev->kobj, "powers"); 725 sysfs_remove_link(&psy->dev->kobj, "powers");
681 power_supply_remove_triggers(psy); 726 power_supply_remove_triggers(psy);
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 0d7c95f634a5..7ae60346465f 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -199,6 +199,12 @@ struct power_supply {
199 size_t num_supplies; 199 size_t num_supplies;
200 struct device_node *of_node; 200 struct device_node *of_node;
201 201
202 /*
203 * Functions for drivers implementing power supply class.
204 * These shouldn't be called directly by other drivers for accessing
205 * this power supply. Instead use power_supply_*() functions (for
206 * example power_supply_get_property()).
207 */
202 int (*get_property)(struct power_supply *psy, 208 int (*get_property)(struct power_supply *psy,
203 enum power_supply_property psp, 209 enum power_supply_property psp,
204 union power_supply_propval *val); 210 union power_supply_propval *val);
@@ -227,6 +233,7 @@ struct power_supply {
227 struct work_struct changed_work; 233 struct work_struct changed_work;
228 spinlock_t changed_lock; 234 spinlock_t changed_lock;
229 bool changed; 235 bool changed;
236 atomic_t use_cnt;
230#ifdef CONFIG_THERMAL 237#ifdef CONFIG_THERMAL
231 struct thermal_zone_device *tzd; 238 struct thermal_zone_device *tzd;
232 struct thermal_cooling_device *tcd; 239 struct thermal_cooling_device *tcd;
@@ -287,6 +294,15 @@ extern int power_supply_is_system_supplied(void);
287static inline int power_supply_is_system_supplied(void) { return -ENOSYS; } 294static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
288#endif 295#endif
289 296
297extern int power_supply_get_property(struct power_supply *psy,
298 enum power_supply_property psp,
299 union power_supply_propval *val);
300extern int power_supply_set_property(struct power_supply *psy,
301 enum power_supply_property psp,
302 const union power_supply_propval *val);
303extern int power_supply_property_is_writeable(struct power_supply *psy,
304 enum power_supply_property psp);
305extern void power_supply_external_power_changed(struct power_supply *psy);
290extern int power_supply_register(struct device *parent, 306extern int power_supply_register(struct device *parent,
291 struct power_supply *psy, 307 struct power_supply *psy,
292 const struct power_supply_config *cfg); 308 const struct power_supply_config *cfg);