From patchwork Mon Jan 20 17:05:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ezequiel Garcia X-Patchwork-Id: 11342595 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 065E5139A for ; Mon, 20 Jan 2020 17:06:59 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D32B021D7E for ; Mon, 20 Jan 2020 17:06:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="QGkCxpNU" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D32B021D7E Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=collabora.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-rockchip-bounces+patchwork-linux-rockchip=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=OAM2B5Iv7+OeBLJZj8uPZ6TB9UaQy5UQT33RR1F98J0=; b=QGkCxpNUrmU6FN X+FfTyhUHqc+u6vDo1KNtCtHjlkitIc3iq2P8gaHUM1UOlL71RJrN4lOglFGzK0Xl5yl2YA236QcP BcPFwYag6iwV9BaFCOmnn5cqysL103wLkmGZdQPEUN2+8DK2dkWvw1Bk6OzfYyRg4jn0KrvdE+YVj 7GdJsL2o45VlfZ3Z+5YAq1ewxmrHeDIfgOU89CMb20yHP69333mR3QTCZzgKiocsA0yqVJkmmMfxh V5DWexXBFmMNvCWLCIKxHV16h34arqNAVVR1I4WkRDBi1YwYv2Dq7duxh4tspXbBk+eJbcDKAs03f eMQ3hbkoNAlIXj751NjA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1itaVo-0007cs-TT; Mon, 20 Jan 2020 17:06:52 +0000 Received: from bhuna.collabora.co.uk ([2a00:1098:0:82:1000:25:2eeb:e3e3]) by bombadil.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1itaVH-0007AH-Bz; Mon, 20 Jan 2020 17:06:25 +0000 Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: ezequiel) with ESMTPSA id 588B62912EE From: Ezequiel Garcia To: Greg Kroah-Hartman , "Rafael J . Wysocki" , Sandy Huang , =?utf-8?q?Heiko_St=C3=BCbner?= , David Airlie , Daniel Vetter Subject: [PATCH 1/5] component: Add an API to cleanup before unbind Date: Mon, 20 Jan 2020 14:05:58 -0300 Message-Id: <20200120170602.3832-2-ezequiel@collabora.com> X-Mailer: git-send-email 2.25.0 In-Reply-To: <20200120170602.3832-1-ezequiel@collabora.com> References: <20200120170602.3832-1-ezequiel@collabora.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200120_090619_662040_B93CB61B X-CRM114-Status: GOOD ( 14.09 ) X-Spam-Score: -0.0 (/) X-Spam-Report: SpamAssassin version 3.4.3 on bombadil.infradead.org summary: Content analysis details: (-0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record 0.0 UNPARSEABLE_RELAY Informational: message has unparseable relay lines X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-rockchip@lists.infradead.org, kernel@collabora.com, Ezequiel Garcia , linux-arm-kernel@lists.infradead.org Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+patchwork-linux-rockchip=patchwork.kernel.org@lists.infradead.org Some users of the component API have a special model for the allocation and release of its resources: resources are allocated by an API but then released by other means. This contrasts with the current component API assumption: .unbind must undo everything that .bind did. An example of this is the DRM framework, which expects registered objects (connectors, planes, CRTCs, etc) to be released by respective drm_xxx_funcs.destroy hooks. The drm_xxx_funcs.destroy call is done either directly by drm_mode_config_cleanup() or in a refcounted fashion, depending on the type of object. For example, a DRM CRTC object is registered by drm_crtc_init_with_planes(), and then released by drm_crtc_cleanup(), which is normally called from the drm_crtc_funcs.destroy hook. Now, in this model, drm_mode_config_cleanup() should always be called before component_unbind() to avoid use-after-free situations (because each component has a devres group). However, component_bind_all() calls component_unbind on binded components, if any component in the chain fails to bind. In order to allow this special case, and following Alan Kay: "simple things should be simple, complex things should be possible" introduce an extension to component_bind_all, which takes an extra cleanup callback, to be called when binding fails to perform extra cleanup steps. This new API allows the following simple pattern: void unbind_cleanup(...) { drm_mode_config_cleanup(drm_dev); } int foo_bind() { component_bind_all_or_cleanup(dev, drm_dev, unbind_cleanup); } void foo_unbind() { drm_mode_config_cleanup(drm_dev); component_unbind_all(dev, drm_dev); } Each DRM component then uses the respective .destroy hooks to destroy DRM resources, and the .unbind hooks to release non-DRM resources. Arguably, this could be viewed as Very Ugly. However, it handles this complex case, making it possible to fix the current unbind crashes that some DRM drivers suffer from, in a non-invasive way, keeping the DRM resource handling model. Signed-off-by: Ezequiel Garcia --- drivers/base/component.c | 9 +++++++-- include/linux/component.h | 10 +++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index 532a3a5d8f63..371cff9208cf 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -622,12 +622,14 @@ static int component_bind(struct component *component, struct master *master, * component_bind_all - bind all components of an aggregate driver * @master_dev: device with the aggregate driver * @data: opaque pointer, passed to all components + * @cleanup: optional cleanup callback. * * Binds all components of the aggregate @dev by passing @data to their * &component_ops.bind functions. Should be called from * &component_master_ops.bind. */ -int component_bind_all(struct device *master_dev, void *data) +int component_bind_all_or_cleanup(struct device *master_dev, + void *data, void (*cleanup)(void *data)) { struct master *master; struct component *c; @@ -650,6 +652,9 @@ int component_bind_all(struct device *master_dev, void *data) } if (ret != 0) { + if (cleanup) + cleanup(data); + for (; i > 0; i--) if (!master->match->compare[i - 1].duplicate) { c = master->match->compare[i - 1].component; @@ -659,7 +664,7 @@ int component_bind_all(struct device *master_dev, void *data) return ret; } -EXPORT_SYMBOL_GPL(component_bind_all); +EXPORT_SYMBOL_GPL(component_bind_all_or_cleanup); static int __component_add(struct device *dev, const struct component_ops *ops, int subcomponent) diff --git a/include/linux/component.h b/include/linux/component.h index 16de18f473d7..1a5c7b772de3 100644 --- a/include/linux/component.h +++ b/include/linux/component.h @@ -38,7 +38,15 @@ int component_add_typed(struct device *dev, const struct component_ops *ops, int subcomponent); void component_del(struct device *, const struct component_ops *); -int component_bind_all(struct device *master, void *master_data); +int component_bind_all_or_cleanup(struct device *master, + void *master_data, + void (*cleanup)(void *data)); + +static inline int component_bind_all(struct device *master, void *master_data) +{ + return component_bind_all_or_cleanup(master, master_data, NULL); +} + void component_unbind_all(struct device *master, void *master_data); struct master;