Message ID | 20231121203954.173364-2-rdbabiera@google.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2b07:b0:403:3b70:6f57 with SMTP id io7csp902673vqb; Tue, 21 Nov 2023 12:41:00 -0800 (PST) X-Google-Smtp-Source: AGHT+IFeA8Rn+7cQNJ0DGttvkbNtHJNzp1fkHp9jdi8848yRK8pxOjyh3J7D7i0/yW58Nb3ZLUbn X-Received: by 2002:a05:6808:1155:b0:3ae:2024:837b with SMTP id u21-20020a056808115500b003ae2024837bmr532699oiu.34.1700599260714; Tue, 21 Nov 2023 12:41:00 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1700599260; cv=none; d=google.com; s=arc-20160816; b=uyaP8rsjK5RAdMBQVlNMBRlQq+bOtuD5gbrJWJ3gg0SgVWWXv24JzCRe4Ntjme6EB+ XnpJO/dWoBaKqNqqNWwx2tqL7jTqM/B8z/WmMKLiuO1qE43i6bhvSSP69PKfQPwkVYJR 7snv/LTguq3KMaOFal82Z0Fi0BWytWQYJfjJ+Um2P8LT/KzvrLrCJiBPOy5K3/ea1pc5 c4LaChwFFR/LwFYex1H280gMJ9bdMQWgsjCHWs0TgZeHJd/G0Gns9kHKpqWxgeox09j3 VnZf7IXQ7IVRFBRt/WQfzqqvTAK31068l5MARX1Bd5LY48GMiYHXg2WnmAnxctR+OP+J uImg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:from:subject:message-id:mime-version:date :dkim-signature; bh=gX/jHSjVMvQMuoAzLeVpINA8vcf/XXhZAAMF7Dr1Htw=; fh=CSEqckJMu7XshNeVh3G0BzLl3AZGEZpJ40gEsxT0FMI=; b=M8zYMGvIrR8a20/n6pzoKibChcNSmN/xdrUeTOCVc+UVFsEtfm3WkFAwnGtaz6ZdQZ /pcmAkJcfAu00ERU4OTbSjQtp01uKVz9+YakA5u24xWikbN+P5WYZdkJ+v9KSsRHiPHZ sCVcHuDZbT41mMs3pnyQ9Zy8KUG3kbVPmPnpNCo9agCqO4VIFPw00rMtusK4CdVgIAyc vHwDAHWB3GVTU2NfzjSegueSqQ5LvidvIt9xtQgrXcGFQjhMeLIKtSrCG4y64yWokcVR Z2JTbA01W50Xo57ATfJ6yDtH73yp1GZUYoslUArhtkAoKF+pMVphvuTT3sso8zqho/CF D8Bg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=aOCGaQgV; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from groat.vger.email (groat.vger.email. [2620:137:e000::3:5]) by mx.google.com with ESMTPS id m5-20020a632605000000b005b909e93e2dsi11321216pgm.522.2023.11.21.12.41.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Nov 2023 12:41:00 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) client-ip=2620:137:e000::3:5; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=aOCGaQgV; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:5 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by groat.vger.email (Postfix) with ESMTP id B803F8075016; Tue, 21 Nov 2023 12:40:13 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at groat.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229514AbjKUUkF (ORCPT <rfc822;ouuuleilei@gmail.com> + 99 others); Tue, 21 Nov 2023 15:40:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40788 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229498AbjKUUkD (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Tue, 21 Nov 2023 15:40:03 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C03CD1BB for <linux-kernel@vger.kernel.org>; Tue, 21 Nov 2023 12:39:58 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5cc3f5a9e96so1968207b3.1 for <linux-kernel@vger.kernel.org>; Tue, 21 Nov 2023 12:39:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1700599198; x=1701203998; darn=vger.kernel.org; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=gX/jHSjVMvQMuoAzLeVpINA8vcf/XXhZAAMF7Dr1Htw=; b=aOCGaQgVUQo1071NiL6+IclGxNl0CzP6bVNYSy4hLK8RetBCFFhT472KqD5kyB05VX teqbiboqAbslRIkKRe+R67k/7raJQBzgGb+tZYP0W2v8xTpsTpHNhtBDTtcovBhdsac/ +tc2iJ1hGq6k+7iVNorThplXqANAJfJmI7a7PpUMHdeLQtqI9sm6cOhAYk9tu6JvklMT LH2n/Hc5yfyHQakYqNgMKHkxDR/z7B1Z7IBgUAsHftg8G9Pft3ESmAb4M1rjUbMgoKuS ZZYYdOKZzvx8OVAkVnnde8A/7YnKt4AuhgUKHeTU3bpTqyhyjdN08S5QsNkQqEMV2IkL 6MQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700599198; x=1701203998; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=gX/jHSjVMvQMuoAzLeVpINA8vcf/XXhZAAMF7Dr1Htw=; b=DtI3lkHux54ACg0KLEq6P1/fxgpWkdaFoSmmWc7Ak9v2g5fTuiaynxuDMArfVLH76y Uk5uRj3uh7X5XyxoQbr6mICe7DR2rSaZdOtdVozxEG9u8CJJyuHx9L+Vy2+Sl9TPLyQP RNzTIMYIaiyQtca21u/UyyManYbuOXWiwLYC//nMNZoWt+JT2syiavHxlDi47PBg+NcH Ux7zP7ZjHUdLGQUxIhQ23QUrLiQoogh0Q2UqPV6nSHVCjMbsm6WhouiQR3BFZu2XCSHC A9gs3j5634sk8yRM1cImnirLGUdzio/nGXIbbjSURYVfPn4pUUpPGdoja7r0YmXwJ6sX HJpw== X-Gm-Message-State: AOJu0Yw4nsmwxs0DhNhorUq+b6TXQUWIYHTFvcj2HGGn0jz+i8t+jY0S TK5o8yEphaSgRC3KlgZzDmTMH5lpe8qnDwo= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a81:4f84:0:b0:5ca:4a99:7008 with SMTP id d126-20020a814f84000000b005ca4a997008mr2821ywb.10.1700599198097; Tue, 21 Nov 2023 12:39:58 -0800 (PST) Date: Tue, 21 Nov 2023 20:39:55 +0000 Mime-Version: 1.0 X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=2637; i=rdbabiera@google.com; h=from:subject; bh=g3cATdDr31eccLe03DHQgAdZzg53ANBo3tzI1LoDz8E=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmxorMqPUL6W6ov9tm+8mlYddhft0n+7cGy60HTA+QE7 85IsPjYUcrCIMbBICumyKLrn2dw40rqljmcNcYwc1iZQIYwcHEKwETihRkZei2t3rbxzJb1zuep fqkQnLblpNvN43v2m3JyJufs+BmwnJHh796Q47Xak7i22gczPVD9yffhSs+uPzFrvBLtF3Lyzc1 iBQA= X-Mailer: git-send-email 2.43.0.rc1.413.gea7ed67945-goog Message-ID: <20231121203954.173364-2-rdbabiera@google.com> Subject: [PATCH v1] usb: typec: class: fix typec_altmode_put_partner to put plugs From: RD Babiera <rdbabiera@google.com> To: heikki.krogerus@linux.intel.com, gregkh@linuxfoundation.org Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, badhri@google.com, RD Babiera <rdbabiera@google.com>, stable@vger.kernel.org Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-8.4 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on groat.vger.email Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (groat.vger.email [0.0.0.0]); Tue, 21 Nov 2023 12:40:13 -0800 (PST) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1783207570134605823 X-GMAIL-MSGID: 1783207570134605823 |
Series |
[v1] usb: typec: class: fix typec_altmode_put_partner to put plugs
|
|
Commit Message
RD Babiera
Nov. 21, 2023, 8:39 p.m. UTC
When releasing an Alt Mode, typec_altmode_release called by a plug device will not release the plug Alt Mode, meaning that a port will hold a reference to a plug Alt Mode even if the port partner is unregistered. As a result, typec_altmode_get_plug() can return an old plug altmode. Currently, typec_altmode_put_partner does not raise issues when unregistering a partner altmode. Looking at the current implementation: > static void typec_altmode_put_partner(struct altmode *altmode) > { > struct altmode *partner = altmode->partner; When called by the partner Alt Mode, then partner evaluates to the port's Alt Mode. When called by the plug Alt Mode, this also evaluates to the port's Alt Mode. > struct typec_altmode *adev; > > if (!partner) > return; > > adev = &partner->adev; This always evaluates to the port's typec_altmode > if (is_typec_plug(adev->dev.parent)) { > struct typec_plug *plug = to_typec_plug(adev->dev.parent); > > partner->plug[plug->index] = NULL; If the routine is called to put the plug's Alt mode and altmode refers to the plug, then adev referring to the port can never be a typec_plug. If altmode refers to the port, adev will always refer to the port partner, which runs the block below. > } else { > partner->partner = NULL; > } > put_device(&adev->dev); > } When calling typec_altmode_set_partner, a registration always calls get_device() on the port partner or the plug being registered, therefore typec_altmode_put_partner should put_device() the same device. By changing adev to altmode->adev, we make sure to put the correct device and properly unregister plugs. The reason port partners are always properly unregistered is because even when adev refers to the port, the port partner gets nullified in the else block. The port device currently gets put(). Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes") Cc: stable@vger.kernel.org Signed-off-by: RD Babiera <rdbabiera@google.com> --- drivers/usb/typec/class.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) base-commit: b85ea95d086471afb4ad062012a4d73cd328fa86
Comments
On Tue, Nov 21, 2023 at 08:39:55PM +0000, RD Babiera wrote: > When releasing an Alt Mode, typec_altmode_release called by a plug device > will not release the plug Alt Mode, meaning that a port will hold a > reference to a plug Alt Mode even if the port partner is unregistered. > As a result, typec_altmode_get_plug() can return an old plug altmode. > > Currently, typec_altmode_put_partner does not raise issues > when unregistering a partner altmode. Looking at the current > implementation: > > > static void typec_altmode_put_partner(struct altmode *altmode) > > { > > struct altmode *partner = altmode->partner; > > When called by the partner Alt Mode, then partner evaluates to the port's > Alt Mode. When called by the plug Alt Mode, this also evaluates to the > port's Alt Mode. > > > struct typec_altmode *adev; > > > > if (!partner) > > return; > > > > adev = &partner->adev; > > This always evaluates to the port's typec_altmode > > > if (is_typec_plug(adev->dev.parent)) { > > struct typec_plug *plug = to_typec_plug(adev->dev.parent); > > > > partner->plug[plug->index] = NULL; > > If the routine is called to put the plug's Alt mode and altmode refers to > the plug, then adev referring to the port can never be a typec_plug. If > altmode refers to the port, adev will always refer to the port partner, > which runs the block below. > > > } else { > > partner->partner = NULL; > > } > > put_device(&adev->dev); > > } > > When calling typec_altmode_set_partner, a registration always calls > get_device() on the port partner or the plug being registered, therefore > typec_altmode_put_partner should put_device() the same device. By changing > adev to altmode->adev, we make sure to put the correct device and properly > unregister plugs. The reason port partners are always properly > unregistered is because even when adev refers to the port, the port > partner gets nullified in the else block. The port device currently gets > put(). > > Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes") > Cc: stable@vger.kernel.org > Signed-off-by: RD Babiera <rdbabiera@google.com> > --- > drivers/usb/typec/class.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c > index 2e0451bd336e..803be1943445 100644 > --- a/drivers/usb/typec/class.c > +++ b/drivers/usb/typec/class.c > @@ -267,7 +267,7 @@ static void typec_altmode_put_partner(struct altmode *altmode) > if (!partner) > return; > > - adev = &partner->adev; > + adev = &altmode->adev; > > if (is_typec_plug(adev->dev.parent)) { > struct typec_plug *plug = to_typec_plug(adev->dev.parent); Sorry, I may have missed something, but do we need to call this function with ports at all? static void typec_altmode_release(struct device *dev) { struct altmode *alt = to_altmode(to_typec_altmode(dev)); - typec_altmode_put_partner(alt); + if (!is_typec_port(dev->parent)) + typec_altmode_put_partner(alt); altmode_id_remove(alt->adev.dev.parent, alt->id); kfree(alt); ... thanks,
Hi, I found this mail in the archives after looking at a bug report that was bisected to the change that resulted from the following analysis: https://lore.kernel.org/all/CAP-bSRb3SXpgo_BEdqZB-p1K5625fMegRZ17ZkPE1J8ZYgEHDg@mail.gmail.com/ AFAICS the analysis below is partially flawed On Tue, Nov 21, 2023 at 08:39:55PM +0000, RD Babiera wrote: > When releasing an Alt Mode, typec_altmode_release called by a plug device > will not release the plug Alt Mode, meaning that a port will hold a > reference to a plug Alt Mode even if the port partner is unregistered. > As a result, typec_altmode_get_plug() can return an old plug altmode. > > Currently, typec_altmode_put_partner does not raise issues > when unregistering a partner altmode. Looking at the current > implementation: > > > static void typec_altmode_put_partner(struct altmode *altmode) > > { > > struct altmode *partner = altmode->partner; > > When called by the partner Alt Mode, then partner evaluates to the port's > Alt Mode. When called by the plug Alt Mode, this also evaluates to the > port's Alt Mode. > > > struct typec_altmode *adev; > > > > if (!partner) > > return; > > > > adev = &partner->adev; > > This always evaluates to the port's typec_altmode > > > if (is_typec_plug(adev->dev.parent)) { > > struct typec_plug *plug = to_typec_plug(adev->dev.parent); > > > > partner->plug[plug->index] = NULL; > > If the routine is called to put the plug's Alt mode and altmode refers to > the plug, then adev referring to the port can never be a typec_plug. If > altmode refers to the port, adev will always refer to the port partner, > which runs the block below. > > > } else { > > partner->partner = NULL; > > } > > put_device(&adev->dev); > > } So far everything is fine. > When calling typec_altmode_set_partner, a registration always calls > get_device() on the port partner or the plug being registered, This is wrong. It is the altmode of the plug or partner that holds a reference to the altmode of the port not the other way around. The port's altmode has (back) pointers to the altmodes of its partner and the cable plugs but these are weak references that do not contribute to the refcount. > therefore > typec_altmode_put_partner should put_device() the same device. By changing Thus this conclusion is wrong. The put_device() used to be correct. > adev to altmode->adev, we make sure to put the correct device and properly > unregister plugs. The reason port partners are always properly > unregistered is because even when adev refers to the port, the port > partner gets nullified in the else block. The port device currently gets > put(). Please correct me if I missed something. regards Christian
Hi Christian, On Fri, Dec 29, 2023 at 04:32:16PM +0100, Christian A. Ehrhardt wrote: > > Hi, > > I found this mail in the archives after looking at a bug report > that was bisected to the change that resulted from the following > analysis: > > https://lore.kernel.org/all/CAP-bSRb3SXpgo_BEdqZB-p1K5625fMegRZ17ZkPE1J8ZYgEHDg@mail.gmail.com/ > > AFAICS the analysis below is partially flawed > > On Tue, Nov 21, 2023 at 08:39:55PM +0000, RD Babiera wrote: > > When releasing an Alt Mode, typec_altmode_release called by a plug device > > will not release the plug Alt Mode, meaning that a port will hold a > > reference to a plug Alt Mode even if the port partner is unregistered. > > As a result, typec_altmode_get_plug() can return an old plug altmode. > > > > Currently, typec_altmode_put_partner does not raise issues > > when unregistering a partner altmode. Looking at the current > > implementation: > > > > > static void typec_altmode_put_partner(struct altmode *altmode) > > > { > > > struct altmode *partner = altmode->partner; > > > > When called by the partner Alt Mode, then partner evaluates to the port's > > Alt Mode. When called by the plug Alt Mode, this also evaluates to the > > port's Alt Mode. > > > > > struct typec_altmode *adev; > > > > > > if (!partner) > > > return; > > > > > > adev = &partner->adev; > > > > This always evaluates to the port's typec_altmode > > > > > if (is_typec_plug(adev->dev.parent)) { > > > struct typec_plug *plug = to_typec_plug(adev->dev.parent); > > > > > > partner->plug[plug->index] = NULL; > > > > If the routine is called to put the plug's Alt mode and altmode refers to > > the plug, then adev referring to the port can never be a typec_plug. If > > altmode refers to the port, adev will always refer to the port partner, > > which runs the block below. > > > > > } else { > > > partner->partner = NULL; > > > } > > > put_device(&adev->dev); > > > } > > So far everything is fine. > > > When calling typec_altmode_set_partner, a registration always calls > > get_device() on the port partner or the plug being registered, > > This is wrong. It is the altmode of the plug or partner > that holds a reference to the altmode of the port not the other > way around. The port's altmode has (back) pointers to the altmodes > of its partner and the cable plugs but these are weak references that > do not contribute to the refcount. > > > therefore > > typec_altmode_put_partner should put_device() the same device. By changing > > Thus this conclusion is wrong. The put_device() used to be correct. > > > adev to altmode->adev, we make sure to put the correct device and properly > > unregister plugs. The reason port partners are always properly > > unregistered is because even when adev refers to the port, the port > > partner gets nullified in the else block. The port device currently gets > > put(). > > Please correct me if I missed something. Thanks for checking this. Your analysis sounds correct to me. RD, I think we need to revert the commmit. If you still see the original problem, please prepare a new patch. thanks,
Hi Heikki, That sounds good to me. Christian had proposed a fix in another email thread, so I can post the patch after testing on my end. Thanks again Christian for the analysis and fix. Best, --- RD
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 2e0451bd336e..803be1943445 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -267,7 +267,7 @@ static void typec_altmode_put_partner(struct altmode *altmode) if (!partner) return; - adev = &partner->adev; + adev = &altmode->adev; if (is_typec_plug(adev->dev.parent)) { struct typec_plug *plug = to_typec_plug(adev->dev.parent);