From patchwork Mon Jul 27 14:30:50 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Zapolskiy X-Patchwork-Id: 500451 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 796771402E8 for ; Tue, 28 Jul 2015 00:33:11 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752207AbbG0Ocs (ORCPT ); Mon, 27 Jul 2015 10:32:48 -0400 Received: from relay1.mentorg.com ([192.94.38.131]:61204 "EHLO relay1.mentorg.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753286AbbG0OcS (ORCPT ); Mon, 27 Jul 2015 10:32:18 -0400 Received: from nat-ies.mentorg.com ([192.94.31.2] helo=SVR-IES-FEM-01.mgc.mentorg.com) by relay1.mentorg.com with esmtp id 1ZJjQg-0004x2-0I from Vladimir_Zapolskiy@mentor.com ; Mon, 27 Jul 2015 07:30:58 -0700 Received: from eyas.fin.mentorg.com (137.202.0.76) by SVR-IES-FEM-01.mgc.mentorg.com (137.202.0.104) with Microsoft SMTP Server (TLS) id 14.3.224.2; Mon, 27 Jul 2015 15:30:56 +0100 From: Vladimir Zapolskiy To: Wolfram Sang , Thierry Reding CC: Subject: [PATCH v2 3/4] i2c: core: add and export of_get_i2c_adapter_by_node() interface Date: Mon, 27 Jul 2015 17:30:50 +0300 Message-ID: <1438007451-8553-4-git-send-email-vladimir_zapolskiy@mentor.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1438007451-8553-1-git-send-email-vladimir_zapolskiy@mentor.com> References: <1438007451-8553-1-git-send-email-vladimir_zapolskiy@mentor.com> MIME-Version: 1.0 X-Originating-IP: [137.202.0.76] Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org of_find_i2c_adapter_by_node() call requires quite often missing put_device(), and i2c_put_adapter() releases a device locked by i2c_get_adapter() only. In general module_put(adapter->owner) and put_device(dev) are not interchangeable. This is a common error reproduction scenario as a result of the misusage described above (for clearness this is run on iMX6 platform with HDMI and I2C bus drivers compiled as kernel modules): root@mx6q:~# lsmod | grep i2c i2c_imx 10213 0 root@mx6q:~# lsmod | grep dw_hdmi_imx dw_hdmi_imx 3631 0 dw_hdmi 11846 1 dw_hdmi_imx imxdrm 8674 3 dw_hdmi_imx,imx_ipuv3_crtc,imx_ldb drm_kms_helper 113765 5 dw_hdmi,imxdrm,imx_ipuv3_crtc,imx_ldb root@mx6q:~# rmmod dw_hdmi_imx root@mx6q:~# lsmod | grep i2c i2c_imx 10213 -1 ^^^^^ root@mx6q:~# rmmod i2c_imx rmmod: ERROR: Module i2c_imx is in use To fix existing users of these interfaces and to avoid any further confusion and misusage in future, add one more interface of_get_i2c_adapter_by_node(), it is similar to i2c_get_adapter() in sense that an I2C bus device driver found and locked by user can be correctly unlocked by i2c_put_adapter(). Signed-off-by: Vladimir Zapolskiy --- Changes from v1 to v2: * rebased v1 1/10 on top of v2 1/4 and v2 2/4, removed put_device() from of_get_i2c_adapter_by_node() as suggested by Thierry * reuse of_find_i2c_adapter_by_node() to replace common code * fix potential device refcount leakage on error path Changes from RFC to v1: * added new exported function declaration in include/linux/i2c.h * added put_device(dev) call right inside of_get_i2c_adapter_by_node() * corrected authorship of the change drivers/i2c/i2c-core.c | 18 ++++++++++++++++++ include/linux/i2c.h | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index a7054ac..f3de2e1 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1369,6 +1369,24 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) return adapter; } EXPORT_SYMBOL(of_find_i2c_adapter_by_node); + +/* must call i2c_put_adapter() when done with returned i2c_adapter device */ +struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) +{ + struct i2c_adapter *adapter; + + adapter = of_find_i2c_adapter_by_node(node); + if (!adapter) + return NULL; + + if (!try_module_get(adapter->owner)) { + put_device(&adapter->dev); + adapter = NULL; + } + + return adapter; +} +EXPORT_SYMBOL(of_get_i2c_adapter_by_node); #else static void of_i2c_register_devices(struct i2c_adapter *adap) { } #endif /* CONFIG_OF */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e83a738..e2c859b 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -638,6 +638,8 @@ extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); /* must call put_device() when done with returned i2c_adapter device */ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node); +/* must call i2c_put_adapter() when done with returned i2c_adapter device */ +struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node); #else static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) @@ -649,6 +651,11 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node { return NULL; } + +static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node) +{ + return NULL; +} #endif /* CONFIG_OF */ #endif /* _LINUX_I2C_H */