From patchwork Fri Jan 30 14:47:41 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Kozlowski X-Patchwork-Id: 434914 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 425A7140277 for ; Sat, 31 Jan 2015 01:58:08 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1423023AbbA3O5g (ORCPT ); Fri, 30 Jan 2015 09:57:36 -0500 Received: from mailout1.w1.samsung.com ([210.118.77.11]:56929 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932841AbbA3Os2 (ORCPT ); Fri, 30 Jan 2015 09:48:28 -0500 Received: from eucpsbgm1.samsung.com (unknown [203.254.199.244]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0NIZ00MP5VZJ6Y80@mailout1.w1.samsung.com>; Fri, 30 Jan 2015 14:52:32 +0000 (GMT) X-AuditID: cbfec7f4-b7f126d000001e9a-fe-54cb99275ef1 Received: from eusync2.samsung.com ( [203.254.199.212]) by eucpsbgm1.samsung.com (EUCPMTA) with SMTP id 20.19.07834.7299BC45; Fri, 30 Jan 2015 14:45:59 +0000 (GMT) Received: from AMDC1943.digital.local ([106.116.151.171]) by eusync2.samsung.com (Oracle Communications Messaging Server 7u4-23.01(7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0NIZ00EV0VSES470@eusync2.samsung.com>; Fri, 30 Jan 2015 14:48:24 +0000 (GMT) From: Krzysztof Kozlowski To: Sebastian Reichel , Dmitry Eremin-Solenikov , David Woodhouse , linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, "Rafael J. Wysocki" , Len Brown , Jiri Kosina , David Herrmann , Cezary Jackiewicz , Darren Hart , Support Opensource , Milo Kim , Julian Andres Klode , Marc Dietrich , Greg Kroah-Hartman , linux-acpi@vger.kernel.org, linux-input@vger.kernel.org, platform-driver-x86@vger.kernel.org, patches@opensource.wolfsonmicro.com, ac100@lists.launchpad.net, linux-tegra@vger.kernel.org, devel@driverdev.osuosl.org, Linus Walleij , Samuel Ortiz , Lee Jones , linux-arm-kernel@lists.infradead.org, Ingo Molnar , "H. Peter Anvin" , x86@kernel.org, Daniel Mack , Haojian Zhuang , Robert Jarzmik , Russell King Cc: Thomas Gleixner , Pavel Machek , Kyungmin Park , Bartlomiej Zolnierkiewicz , Krzysztof Kozlowski Subject: [PATCH v3 03/20] power_supply: Add API for safe access of power supply function attrs Date: Fri, 30 Jan 2015 15:47:41 +0100 Message-id: <1422629278-12202-4-git-send-email-k.kozlowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1422629278-12202-1-git-send-email-k.kozlowski@samsung.com> References: <1422629278-12202-1-git-send-email-k.kozlowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAAyWRaUjTcRjH/e1/7K+4+jdN/4hEDGNg5UFCT2LiO39QL4rshQbl0qWG89hU NIhkauSRM+fV0tRmzQvvyuXUMrV0aqYphUd5BMvUnFchpTl993me53u8eBhC+IZ0YiKi4qTy KEmkiLYhjdvvPp0UPzQGeGxUHITswRUeNBbVU1DxPpME5b8FEnLnfxFgGNjig3bUATLKPeBB lZqAFG09DcN1p6Gg0RGemUtpaCvWkvDT5AmDykU+fN3oQaCfWaIh76+OB7oOFYKmuXEKvqxu UjD6qpiGtfvdCNLXNTRMjAog/UM5At1WHQkjr8t2DUs7NEz199BQYzARMGAcoUCzaAfGTAqM bXHQnvaRgOamfAL+NMxRfmKcmpJF48pVdzzd8ZbEqro1Cus1U3y8Yr6Kv+eo+bi50hXPL+go 3FSdTuPJcQONl4eG+Phx30XcWVK7K6i4g3uyJskLTkE2PqHSyIgEqdzdN9gmPCV/mBeTdjyx 2rCCkpHBJQNZMxzrxU0WFPL22YEbnq6nM5ANI2SfIm584zdhOQjZbB73IsfZwjR7imvWVeyJ 7Nkla64320BaBoKdQJzeULUXZccGc+Yf48jCJHuMG21RkxYWsP7czKyW2q87wvX1qvfYmsVc g2mW2m/z51RjRXQOEpQhq2p0WBofEqO4HibzdFNIZIr4qDC3kGhZE9p/+3or0vZ6dyGWQSJb wWeJMUBISRIUSbIuxDGEyF6Ql7u7EoRKkm5J5dHX5PGRUkUX4jHWTsmoRisJer7TSZo1mQx9 b9Pr6ZQt30W50Goeijqr71Uj/XJB4Y441cj2f5M3K/3STDdFtuVl3o0ll1+qJoKE7YHnt7vK rviuL820xGqsavWX5Np6MvD2jbuBZ7onsc+5UueOmDH5oWRHaehU0oET4rSIvqMjC4xPoio2 9ol45ZGIVIRLPF0JuULyH50Bv7HUAgAA Sender: linux-tegra-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-tegra@vger.kernel.org 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 Reviewed-by: Bartlomiej Zolnierkiewicz Reviewed-by: Sebastian Reichel --- drivers/power/power_supply_core.c | 47 ++++++++++++++++++++++++++++++++++++++- include/linux/power_supply.h | 16 +++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index b748391c3e17..0521681b3674 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); int power_supply_set_battery_charged(struct power_supply *psy) { - if (psy->type == POWER_SUPPLY_TYPE_BATTERY && psy->set_charged) { + if (atomic_read(&psy->use_cnt) >= 0 && + psy->type == POWER_SUPPLY_TYPE_BATTERY && + psy->set_charged) { psy->set_charged(psy); return 0; } @@ -366,6 +368,47 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np, EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); #endif /* CONFIG_OF */ +int power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + if (atomic_read(&psy->use_cnt) <= 0) + return -ENODEV; + + return psy->get_property(psy, psp, val); +} +EXPORT_SYMBOL_GPL(power_supply_get_property); + +int power_supply_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + if (atomic_read(&psy->use_cnt) <= 0 || !psy->set_property) + return -ENODEV; + + return psy->set_property(psy, psp, val); +} +EXPORT_SYMBOL_GPL(power_supply_set_property); + +int power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + if (atomic_read(&psy->use_cnt) <= 0 || !psy->property_is_writeable) + return -ENODEV; + + return psy->property_is_writeable(psy, psp); +} +EXPORT_SYMBOL_GPL(power_supply_property_is_writeable); + +void power_supply_external_power_changed(struct power_supply *psy) +{ + if (atomic_read(&psy->use_cnt) <= 0 || !psy->external_power_changed) + return; + + psy->external_power_changed(psy); +} +EXPORT_SYMBOL_GPL(power_supply_external_power_changed); + int power_supply_powers(struct power_supply *psy, struct device *dev) { return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); @@ -555,6 +598,7 @@ static int __power_supply_register(struct device *parent, dev->release = power_supply_dev_release; dev_set_drvdata(dev, psy); psy->dev = dev; + atomic_inc(&psy->use_cnt); if (cfg) { psy->drv_data = cfg->drv_data; psy->of_node = cfg->of_node; @@ -629,6 +673,7 @@ EXPORT_SYMBOL_GPL(power_supply_register_no_ws); void power_supply_unregister(struct power_supply *psy) { + WARN_ON(atomic_dec_return(&psy->use_cnt)); cancel_work_sync(&psy->changed_work); sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index b203a26d5c54..5d460995f38d 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -199,6 +199,12 @@ struct power_supply { size_t num_supplies; struct device_node *of_node; + /* + * Functions for drivers implementing power supply class. + * These shouldn't be called directly by other drivers for accessing + * this power supply. Instead use power_supply_*() functions (for + * example power_supply_get_property()). + */ int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); @@ -227,6 +233,7 @@ struct power_supply { struct work_struct changed_work; spinlock_t changed_lock; bool changed; + atomic_t use_cnt; #ifdef CONFIG_THERMAL struct thermal_zone_device *tzd; struct thermal_cooling_device *tcd; @@ -287,6 +294,15 @@ extern int power_supply_is_system_supplied(void); static inline int power_supply_is_system_supplied(void) { return -ENOSYS; } #endif +extern int power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); +extern int power_supply_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val); +extern int power_supply_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp); +extern void power_supply_external_power_changed(struct power_supply *psy); extern int power_supply_register(struct device *parent, struct power_supply *psy, const struct power_supply_config *cfg);