Received: by 2002:a05:6358:3188:b0:123:57c1:9b43 with SMTP id q8csp13077399rwd; Fri, 23 Jun 2023 15:29:23 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5Y2lrlsh98NHLSBSVVHmRfxsKs10iHbqA1ALZzHF93UfgGHnKGVE6P5NX980whP9v+NKbo X-Received: by 2002:a05:6870:32cf:b0:1aa:f3:5b3b with SMTP id r15-20020a05687032cf00b001aa00f35b3bmr25323276oac.17.1687559363290; Fri, 23 Jun 2023 15:29:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1687559363; cv=none; d=google.com; s=arc-20160816; b=FMc1JTE1AhmO7Ng1yxZthMySvfDR4Hx2o7BgDr28Z4w4SJH5X+P47YOKaK08FiDySd V/QPduvMLHTYMc4CEyoBX7VDXoV1LOTQFmebCBujcLNhmBFAEM7iY1yyhJGhaX5aYlKE PGvzqZlCuTH0Qv8f88EGjMqW/MW9Syg8CRnQETdy9RQ1tEdJgiyrmfFfUOEYAiPAwfpN Wx1GKpuju83nHn5ZI8JkffZQpL+2CBm5RX4cwjjCqLC51lrDyV/OFaYuBEqpwRJ8Ibp1 GynVarM3BbcI5GwfCiKESRiGKhrVZI7ePrvAvIXc2Tpletw7jkMrv28wLWLwCx03OT43 PnAg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:content-transfer-encoding :references:in-reply-to:date:cc:to:from:subject:message-id; bh=md/764jHAzNuAzcrADnWFYKSfywFXTJBmBk4tDVvI04=; fh=v4Z3N2Htbr8NtGqMLkQjkIE/EbeUcf6lhEWEAm07n3g=; b=r80WCde6anaXCrv3rOSezN4RgSzwIeTU8kHR7ieMQqD8OVmNxdxjAjL/MjKobx0z0k 8pbPRTRJOxJo4AewyhKI28cgCyl/Dg+Fe6KMesT7Hg5icznT7VFE8zwL/X/1u7ri2AXa NU5//58qtuy//qFEKzLoNHg7XviCLrwACjw49IGxGTjFKJ86bKUyLVB2+cUHfVZigYeC TShf8mRMsUHmohHIhwco+cQJtdy+3Vriu2iyYP/JGzvNgqQsFRat/SZd7giU7QQ7nY+/ Fv8oAMjMPg4uQlRZFWLGv8txtaK3NQ2ku8/V7y3aAPeaXeqpw72ZbrsEYNLpApXS/D5D 2plg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-bluetooth-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-bluetooth-owner@vger.kernel.org Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id s4-20020a63d044000000b0052c73367c13si262007pgi.871.2023.06.23.15.29.03; Fri, 23 Jun 2023 15:29:23 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-bluetooth-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-bluetooth-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-bluetooth-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232442AbjFWWVf convert rfc822-to-8bit (ORCPT + 99 others); Fri, 23 Jun 2023 18:21:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53000 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232478AbjFWWV3 (ORCPT ); Fri, 23 Jun 2023 18:21:29 -0400 Received: from mout02.posteo.de (mout02.posteo.de [185.67.36.142]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 577532D65 for ; Fri, 23 Jun 2023 15:21:11 -0700 (PDT) Received: from submission (posteo.de [185.67.36.169]) by mout02.posteo.de (Postfix) with ESMTPS id 4634D240101 for ; Sat, 24 Jun 2023 00:21:09 +0200 (CEST) Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4QnsC83Xvbz9rxG; Sat, 24 Jun 2023 00:21:08 +0200 (CEST) Message-ID: <5930c316c248f6326bd47078ebbc289798c249ce.camel@iki.fi> Subject: Re: [PATCH RFC 1/5] Bluetooth: hci_conn: add hci_conn_is_alive From: Pauli Virtanen To: Luiz Augusto von Dentz Cc: linux-bluetooth@vger.kernel.org Date: Fri, 23 Jun 2023 22:21:08 +0000 In-Reply-To: References: <45455ee45ccb3313618a48c01be714e14d372257.1687525956.git.pav@iki.fi> Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8BIT MIME-Version: 1.0 X-Spam-Status: No, score=-3.4 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_NEUTRAL,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-bluetooth@vger.kernel.org Hi Luiz, pe, 2023-06-23 kello 12:39 -0700, Luiz Augusto von Dentz kirjoitti: > On Fri, Jun 23, 2023 at 10:37 AM Pauli Virtanen wrote: > > > > A delayed operation such as hci_sync on a given hci_conn needs to take > > hci_conn_get, so that the hci_conn doesn't get freed in the meantime. > > This does not guarantee the conn is still alive in a valid state, as it > > may be cleaned up in the meantime, so one needs to check if it is still > > in conn_hash to know if it's still alive. > > > > Simplify this alive check, using HCI_CONN_DELETED flag. This is also > > meaningful with RCU lock only, but with slightly different semantics. > > > > If hci_conn_is_alive(conn) returns true inside rcu_read_lock, conn was > > in conn_hash from the point of view of the current task when the flag > > was read. Then its deletion cannot complete before rcu_read_unlock. > > > > Signed-off-by: Pauli Virtanen > > --- > > > > Notes: > > This probably can be done with RCU primitives setting list.prev, but > > that's maybe more magical... > > > > include/net/bluetooth/hci_core.h | 18 ++++++++++++++++++ > > net/bluetooth/hci_conn.c | 10 +--------- > > 2 files changed, 19 insertions(+), 9 deletions(-) > > > > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h > > index 05a9b3ab3f56..cab39bdd0592 100644 > > --- a/include/net/bluetooth/hci_core.h > > +++ b/include/net/bluetooth/hci_core.h > > @@ -978,6 +978,7 @@ enum { > > HCI_CONN_PER_ADV, > > HCI_CONN_BIG_CREATED, > > HCI_CONN_CREATE_CIS, > > + HCI_CONN_DELETED, > > }; > > > > static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) > > @@ -997,6 +998,7 @@ static inline bool hci_conn_sc_enabled(struct hci_conn *conn) > > static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) > > { > > struct hci_conn_hash *h = &hdev->conn_hash; > > + WARN_ON(test_bit(HCI_CONN_DELETED, &c->flags)); > > list_add_tail_rcu(&c->list, &h->list); > > switch (c->type) { > > case ACL_LINK: > > @@ -1023,6 +1025,10 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) > > static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) > > { > > struct hci_conn_hash *h = &hdev->conn_hash; > > + bool deleted; > > + > > + deleted = test_and_set_bit(HCI_CONN_DELETED, &c->flags); > > + WARN_ON(deleted); > > > > list_del_rcu(&c->list); > > synchronize_rcu(); > > @@ -1049,6 +1055,18 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) > > } > > } > > > > +/* With hdev->lock: whether hci_conn is in conn_hash. > > + * With RCU: if true, the hci_conn is valid conn_hash iteration cursor and > > + * hci_conn_hash_del has not completed. (Note that if hci_conn was obtained in > > + * this critical section it is always valid, but this may return false!) > > + */ > > +static inline bool hci_conn_is_alive(struct hci_dev *hdev, struct hci_conn *c) > > +{ > > + RCU_LOCKDEP_WARN(lockdep_is_held(&hdev->lock) || rcu_read_lock_held(), > > + "suspicious locking"); > > + return !test_bit(HCI_CONN_DELETED, &c->flags); > > +} > > I think we are better off doing something like > hci_conn_hold_unless_zero like we do in l2cap_chan_hold_unless_zero, > that said we need to check if the hci_conn_drop can still set the ref > below zero, anyway that is probably a bug in itself and we should > probably WARN_ON if that happens. The problem here is that we'd like to have both (1) to have hci_conn_del/cleanup delete the item from conn_hash immediately (2) be able to continue iteration from the conn we held, after releasing and reacquiring RCU or hdev->lock If conn is removed from the list, conn->list.next won't be updated any more, so it is not safe to access after we have left the critical section. So it seems we'd need some marker on whether it is still in the list. Maybe (1) could be given up instead, something like: hci_conn_cleanup sets HCI_CONN_DELETED instead of deleting from the list if refcount is positive, and lookup functions skip items with this flag.  Something along these lines could work, need to think a bit. > > static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) > > { > > struct hci_conn_hash *h = &hdev->conn_hash; > > diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c > > index 62a7ccfdfe63..d489a4829be7 100644 > > --- a/net/bluetooth/hci_conn.c > > +++ b/net/bluetooth/hci_conn.c > > @@ -183,21 +183,13 @@ static void le_scan_cleanup(struct work_struct *work) > > struct hci_conn *conn = container_of(work, struct hci_conn, > > le_scan_cleanup); > > struct hci_dev *hdev = conn->hdev; > > - struct hci_conn *c = NULL; > > > > BT_DBG("%s hcon %p", hdev->name, conn); > > > > hci_dev_lock(hdev); > > > > /* Check that the hci_conn is still around */ > > - rcu_read_lock(); > > - list_for_each_entry_rcu(c, &hdev->conn_hash.list, list) { > > - if (c == conn) > > - break; > > - } > > - rcu_read_unlock(); > > - > > - if (c == conn) { > > + if (hci_conn_is_alive(hdev, conn)) { > > Hmm, I don't think this is safe, except if we are doing hci_conn_get > we can't really access the conn pointer since it may be freed already, > anyway this is sort of broken already given that we do access > conn->hdev already. hci_conn_get is held here, there's a hci_conn_put at the end of this function. > > > hci_connect_le_scan_cleanup(conn, 0x00); > > hci_conn_cleanup(conn); > > } > > -- > > 2.41.0 > > > >