From patchwork Tue Mar 10 08:27:09 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Kozlowski X-Patchwork-Id: 5975421 Return-Path: X-Original-To: patchwork-linux-acpi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id BC476BF440 for ; Tue, 10 Mar 2015 08:38:14 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B8E2E20251 for ; Tue, 10 Mar 2015 08:38:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 909102024D for ; Tue, 10 Mar 2015 08:38:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752582AbbCJIh3 (ORCPT ); Tue, 10 Mar 2015 04:37:29 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:36817 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751452AbbCJI1y (ORCPT ); Tue, 10 Mar 2015 04:27:54 -0400 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 <0NKZ00528MDAEZ10@mailout1.w1.samsung.com>; Tue, 10 Mar 2015 08:31:58 +0000 (GMT) X-AuditID: cbfec7f4-b7f126d000001e9a-fc-54feaa76046d Received: from eusync4.samsung.com ( [203.254.199.214]) by eucpsbgm1.samsung.com (EUCPMTA) with SMTP id F2.CC.07834.67AAEF45; Tue, 10 Mar 2015 08:25:26 +0000 (GMT) Received: from AMDC1943.digital.local ([106.116.151.171]) by eusync4.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0NKZ0033KM64IB70@eusync4.samsung.com>; Tue, 10 Mar 2015 08:27:51 +0000 (GMT) From: Krzysztof Kozlowski To: Sebastian Reichel , Dmitry Eremin-Solenikov , David Woodhouse , "Rafael J. Wysocki" , Len Brown , Jiri Kosina , David Herrmann , Cezary Jackiewicz , Darren Hart , Support Opensource , Milo Kim , 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 , linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, 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 Cc: Thomas Gleixner , Pavel Machek , Kyungmin Park , Marek Szyprowski , Krzysztof Kozlowski Subject: [PATCH v6 05/22] power_supply: Add API for safe access of power supply function attrs Date: Tue, 10 Mar 2015 09:27:09 +0100 Message-id: <1425976046-20973-6-git-send-email-k.kozlowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1425976046-20973-1-git-send-email-k.kozlowski@samsung.com> References: <1425976046-20973-1-git-send-email-k.kozlowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAAy2RWzCUYRiAffv/++9S2/yW+HNhGuVGcihT74ioC76imUxu0kxs7GCyS7uY dFGSJudDyjQyDWPlNMSiyGEKK1rEbks2y5jVjMpGdlNtIkt3z/vOM+9z8XIJfiPpxI0XJwsl YkGCC2VDKjfeTB5Ord8I99LeOQIFoysskA3lkpDx9wsJ9xeWCegeMXOgSu0AOZVeUFxXQsB4 03EobXGErvIqEr4uesNoxhIH5n4oEHTOGyh4sF7DgpreQgRy/SQbplfX2KB+WU6BMX8AQbap jILGAR0HasxNJKheVWzJhk0KdG8VFDR0LxIwolSxoWzJDpS5bFB2JUPP3QkCWuUPCfjVrGcH HsC1q554trefxIVNRjbuLNNx8KeiEg5urXXD8vpsCs9MdlP429gYBz8ZDsOtsltYkTdD4s/F fyi83KuhcJvmHokL2urReYcIG78YYUJ8qlDieTLKJs74Liyp5dD1+fZKIh3pD+Ygay5D+zBF 7TK0ww7M+OwzysJ8uhox2gn3HS5gMWY528IUfZRprZFtOTZce1rBZczLixzLQNAqxOgXhjkW y46OYqZm1KSFSdqVWXmv3i7w6GDmsTrrf82ZGR4s2b5qTWPGZNKSO7VgRqf6jooQrwJZ1aO9 wpToJOmVWJG3h1QgkqaIYz2iE0VytPNiUweqGvTtQzQXuezmTV/aCOezBanSNFEfYriEiz1P Ubi14sUI0m4IJYmRkpQEobQPsbjWTunIqpN3+sVK076eR/57HH01oYQh8kODMTNI6M+nTzl3 JNWd0F3+eE78W7T/dkizq6lxavrnqlK5GVMQwSq9GNQXGeTIry6v0+Y8f3ohS9arHcssogYN aa/9dNfOBgYMXRXPd60HtPk4hRTejFWt2Z7ZVV6XV9uvCfUInNMfc8/PtnUhpXECbzdCIhX8 A6VmZFvAAgAA Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Acked-by: Pavel Machek --- 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 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); 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; @@ -676,6 +720,7 @@ EXPORT_SYMBOL_GPL(devm_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 0d7c95f634a5..7ae60346465f 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);