From nobody Sat Jun 20 20:54:49 2026 Received: from mail-lj1-f180.google.com (mail-lj1-f180.google.com [209.85.208.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F5B3371D11 for ; Fri, 10 Apr 2026 06:30:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775802610; cv=none; b=Cqxn6+E2q55XPvboZBx/GGvPnijYRM7Xc9u5YkhUvvc3hNaCBkfdSwxYahAgueU3hF1eCtMyBznj2x5AacEsMW6n5JOqiGf18ni/ptX/jtTgM1lJiurgLYtOoDij1ZinXYKL9/veMMIX4ZzpL1ELaH/kiCcT4ctvD9Hl2HT1Wxs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775802610; c=relaxed/simple; bh=Q3YuX6ojbP6s1IWwxDjoZs3SIKMzF867hEvEWZx9y8A=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=SYEBiCasklZY9yOCnvWGCFkY6XsIApl05cC8+Mqq2NhucgMcE4TL50Szr7pyxGzSJc0RP2ihMSH+WlbtDpEfznkIswLuq5opDQ9MB3kXg8xHsHUITWrzgcz6EtxmlzvU26PrVP1bd6EAZTZeVX+orscolHcPRg0EiRaZChBxdaU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sfZvw5Rn; arc=none smtp.client-ip=209.85.208.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sfZvw5Rn" Received: by mail-lj1-f180.google.com with SMTP id 38308e7fff4ca-38de18126e7so18260751fa.1 for ; Thu, 09 Apr 2026 23:30:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1775802606; x=1776407406; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=cgAfu309cYrD/5ag9qQuWbjQY22q9LkbBL15U/YGYGU=; b=sfZvw5RnFxsmtEsR6PuwK8JJufyvcnvoILKI+PrR15AGivVyPuuAix154RULvXCdst uIrjrHF4Qvzb/aK8IVTZdmfI2Sc0yYiXzBLP2bOyqZar+Mgm7qHVwXUIRGgpH29vtP4E Jx82xYRArPnvTKzloKuaw6aNg5VontY4Z4Vmecqj1lO8SMBED/MXarVZOkWqxz8fWR1t rZe1e5r0rRxiBubYACBbezSWiSghB2kAP1FSLBChJLmawJcFVkqqBTI4l/EojitK3djK mdt+6nEyKIIg2TcK/6YBniXkRg4V496+0K6lGqCog4WJK0sm++1vH479jU07HKDVnxkM jokA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775802606; x=1776407406; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=cgAfu309cYrD/5ag9qQuWbjQY22q9LkbBL15U/YGYGU=; b=ENVomtSnYbImzzQrCIQuXpUHfi17tyqr64WLLmmrZPhoEzR1Z43KUmMEae3Q54XDH2 RuuJgszIcypVhcBeaAHtmbjHsxUPeYlfexAGGukJnkzEPdNyOgouQ1EkctcejfbyiweL RfRE0gh/djcuMebDtMgkfQkNx+BcjNEgmvlTUnfo3A+Y6Llc1bXuYJp5K7QvzV+/btuP NpbC8elMhinC6r25Sv4/gndaeCRQfVdHPIfRXB/ueLHHy+YPsQF3cjTtL+B6N9r4iQrc jhlYgKYuoIdR/viPLpeDyTYRq6x9yaHHyPgCIkxxMsCKOwDNgzi4tXsEa3v1aUegB74p oyFg== X-Forwarded-Encrypted: i=1; AJvYcCXtmvosR+xsF6zFV454y/kPM9YXfsW6X7Srtlm6csjOjss0SLnf1Tki0dZePIK0O75MDGMRc+klAJwEc/U=@vger.kernel.org X-Gm-Message-State: AOJu0YwsILFl8mBaZpqzx1V8ufGSMc8InYjCp4lieAfg1TSGXjdO0aJG 84Vo2HCt8KuDMX/6Co+obAjKwM+fff3/USNEbNrhDMxtH2PmoKozXYyr X-Gm-Gg: AeBDievM9W4spGALL4VM9/z8x24DgfMJXLrHPk+RC+Xg5303GTnkkaX+nLUNWFii4C9 hf5Ly4R90tAExKyvLpJc528dXHcgNW7fWsvqfigTPTu12oH4gTydwkgaE1EaBcs65MYnVVMZuYF IxIPdoYeoOlfSdwtcg/ctnd5I0w49GOBjvKELPMXMX12IZTu+P7s/aSLybFepeaOLQ1rdxWAkkD Jl0SmXJELg4w8j87zRuYGCT3c78kPw6TmVyVmHE2PHwqBh9JanH0NHmU7cQja4upV3nfq4aIrfa Fv+uF7wWEz18vRcIW31Va5flWVNBPLaclh7Kc++TxxSmHRKISEa78x9JifaCtm//r7CJZKOiEyL M4Em3mPmEUVjDLs2YPFizTy9SeIjSFVWStlcS4knL1d5rVtyCwqbgO8iceL4viiZBLd+EyAOHXB kC8uOzk3SLvsOmH+c5ChA98afPYfxE4hz9mbG1o/qY6z0W X-Received: by 2002:a05:651c:146b:b0:38e:23c9:2f5f with SMTP id 38308e7fff4ca-38e4bf7413emr4611021fa.26.1775802606003; Thu, 09 Apr 2026 23:30:06 -0700 (PDT) Received: from localhost ([188.234.148.119]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-38e495aed16sm4141311fa.40.2026.04.09.23.30.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 09 Apr 2026 23:30:05 -0700 (PDT) From: Mikhail Gavrilov To: marcel@holtmann.org, luiz.dentz@gmail.com Cc: pmenzel@molgen.mpg.de, linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org, Mikhail Gavrilov Subject: [PATCH v2] Bluetooth: l2cap: defer conn param update to avoid conn->lock/hdev->lock inversion Date: Fri, 10 Apr 2026 11:30:01 +0500 Message-ID: <20260410063001.94728-1-mikhail.v.gavrilov@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When a BLE peripheral sends an L2CAP Connection Parameter Update Request the processing path is: process_pending_rx() [takes conn->lock] l2cap_le_sig_channel() l2cap_conn_param_update_req() hci_le_conn_update() [takes hdev->lock] Meanwhile other code paths take the locks in the opposite order: l2cap_chan_connect() [takes hdev->lock] ... mutex_lock(&conn->lock) l2cap_conn_ready() [hdev->lock via hci_cb_list_lock] ... mutex_lock(&conn->lock) This is a classic AB/BA deadlock which lockdep reports as a circular locking dependency when connecting a BLE MIDI keyboard (Carry-On FC-49). Fix this by introducing l2cap_conn_param_update_sync() which is scheduled via hci_cmd_sync_queue() to run on the hci_cmd_sync workqueue, outside any of the involved locks. The necessary connection parameters are captured into a heap-allocated struct conn_param_update_data and the sync callback performs the hci_conn_params update, sends the HCI LE Connection Update command, and notifies mgmt of the new parameters. To guard against a theoretical handle-reuse race (the connection could drop and its handle be reassigned between queuing and execution), the sync callback verifies the connection still exists and its destination address matches the original request before proceeding. The allocation is performed before sending the L2CAP_CONN_PARAM_ACCEPTED response so that on OOM the peer receives a REJECTED response instead of an inconsistent state where the peer applies new parameters but the local controller does not. Fixes: f044eb0524a0 ("Bluetooth: Store latency and supervision timeout in c= onnection params") Signed-off-by: Mikhail Gavrilov Reviewed-by: Paul Menzel --- Changes in v2 (Paul Menzel, Sashiko/Gemini AI review): - Allocate before sending ACCEPTED response to avoid state mismatch on OOM - Verify connection handle and address in sync callback against reuse race - Expand commit message with implementation details net/bluetooth/l2cap_core.c | 93 ++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 95c65fece39b..94dfef9df498 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4670,6 +4670,70 @@ static inline int l2cap_information_rsp(struct l2cap= _conn *conn, return 0; } =20 +struct conn_param_update_data { + u16 handle; + bdaddr_t dst; + u8 dst_type; + u16 min; + u16 max; + u16 latency; + u16 to_multiplier; +}; + +static int l2cap_conn_param_update_sync(struct hci_dev *hdev, void *data) +{ + struct conn_param_update_data *d =3D data; + struct hci_conn *conn; + struct hci_conn_params *params; + struct hci_cp_le_conn_update cp; + u8 store_hint =3D 0x00; + + /* Verify the connection is still alive and matches the original + * request, since the handle could theoretically be reused after + * a disconnect/reconnect cycle. + */ + hci_dev_lock(hdev); + + conn =3D hci_conn_hash_lookup_handle(hdev, d->handle); + if (!conn || bacmp(&conn->dst, &d->dst)) { + hci_dev_unlock(hdev); + return 0; + } + + params =3D hci_conn_params_lookup(hdev, &d->dst, d->dst_type); + if (params) { + params->conn_min_interval =3D d->min; + params->conn_max_interval =3D d->max; + params->conn_latency =3D d->latency; + params->supervision_timeout =3D d->to_multiplier; + store_hint =3D 0x01; + } + + hci_dev_unlock(hdev); + + memset(&cp, 0, sizeof(cp)); + cp.handle =3D cpu_to_le16(d->handle); + cp.conn_interval_min =3D cpu_to_le16(d->min); + cp.conn_interval_max =3D cpu_to_le16(d->max); + cp.conn_latency =3D cpu_to_le16(d->latency); + cp.supervision_timeout =3D cpu_to_le16(d->to_multiplier); + cp.min_ce_len =3D cpu_to_le16(0x0000); + cp.max_ce_len =3D cpu_to_le16(0x0000); + + hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); + + mgmt_new_conn_param(hdev, &d->dst, d->dst_type, store_hint, + d->min, d->max, d->latency, d->to_multiplier); + + return 0; +} + +static void l2cap_conn_param_update_destroy(struct hci_dev *hdev, void *da= ta, + int err) +{ + kfree(data); +} + static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -4677,6 +4741,7 @@ static inline int l2cap_conn_param_update_req(struct = l2cap_conn *conn, struct hci_conn *hcon =3D conn->hcon; struct l2cap_conn_param_update_req *req; struct l2cap_conn_param_update_rsp rsp; + struct conn_param_update_data *d; u16 min, max, latency, to_multiplier; int err; =20 @@ -4703,18 +4768,30 @@ static inline int l2cap_conn_param_update_req(struc= t l2cap_conn *conn, else rsp.result =3D cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); =20 + if (!err) { + d =3D kmalloc(sizeof(*d), GFP_KERNEL); + if (!d) { + rsp.result =3D cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); + err =3D -ENOMEM; + } + } + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); =20 if (!err) { - u8 store_hint; - - store_hint =3D hci_le_conn_update(hcon, min, max, latency, - to_multiplier); - mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, - store_hint, min, max, latency, - to_multiplier); - + d->handle =3D hcon->handle; + bacpy(&d->dst, &hcon->dst); + d->dst_type =3D hcon->dst_type; + d->min =3D min; + d->max =3D max; + d->latency =3D latency; + d->to_multiplier =3D to_multiplier; + + if (hci_cmd_sync_queue(hcon->hdev, + l2cap_conn_param_update_sync, d, + l2cap_conn_param_update_destroy) < 0) + kfree(d); } =20 return 0; --=20 2.53.0