diff mbox series

[v3,2/2] Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT

Message ID 20240827140529.635522-2-luiz.dentz@gmail.com
State New
Headers show
Series [v3,1/2] Bluetooth: hci_sync: Introduce hci_cmd_sync_run/hci_cmd_sync_run_once | expand

Commit Message

Luiz Augusto von Dentz Aug. 27, 2024, 2:05 p.m. UTC
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

MGMT_OP_DISCONNECT can be called while mgmt_device_connected has not
been called yet, which will cause the connection procedure to be
aborted, so mgmt_device_disconnected shall still respond with command
complete to MGMT_OP_DISCONNECT and just not emit
MGMT_EV_DEVICE_DISCONNECTED since MGMT_EV_DEVICE_CONNECTED was never
sent.

To fix this MGMT_OP_DISCONNECT is changed to work similarly to other
command which do use hci_cmd_sync_queue and then use hci_conn_abort to
disconnect and returns the result, in order for hci_conn_abort to be
used from hci_cmd_sync context it now uses hci_cmd_sync_run_once.

Link: https://github.com/bluez/bluez/issues/932
Fixes: 12d4a3b ("Bluetooth: Move check for MGMT_CONNECTED flag into mgmt.c")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
---
 net/bluetooth/hci_conn.c |  6 ++-
 net/bluetooth/mgmt.c     | 84 ++++++++++++++++++++--------------------
 2 files changed, 47 insertions(+), 43 deletions(-)

Comments

Paul Menzel Aug. 27, 2024, 4:40 p.m. UTC | #1
Dear Luiz,


Thank you for posting version 3. I found one more nit.


Am 27.08.24 um 16:05 schrieb Luiz Augusto von Dentz:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> MGMT_OP_DISCONNECT can be called while mgmt_device_connected has not
> been called yet, which will cause the connection procedure to be
> aborted, so mgmt_device_disconnected shall still respond with command
> complete to MGMT_OP_DISCONNECT and just not emit
> MGMT_EV_DEVICE_DISCONNECTED since MGMT_EV_DEVICE_CONNECTED was never
> sent.
> 
> To fix this MGMT_OP_DISCONNECT is changed to work similarly to other
> command which do use hci_cmd_sync_queue and then use hci_conn_abort to
> disconnect and returns the result, in order for hci_conn_abort to be
> used from hci_cmd_sync context it now uses hci_cmd_sync_run_once.
> 
> Link: https://github.com/bluez/bluez/issues/932
> Fixes: 12d4a3b ("Bluetooth: Move check for MGMT_CONNECTED flag into mgmt.c")
> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> ---
>   net/bluetooth/hci_conn.c |  6 ++-
>   net/bluetooth/mgmt.c     | 84 ++++++++++++++++++++--------------------
>   2 files changed, 47 insertions(+), 43 deletions(-)
> 
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 8f0c9322eadb..d51d8dbea631 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -2951,5 +2951,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
>   		return 0;
>   	}
>   
> -	return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL);
> +	/* Run immediately if on cmd_sync_work since this maybe called

s/maybe/may be/


Kind regards,

Paul


> +	 * as a result to MGMT_OP_DISCONNECT and MGMT_OP_UNPAIR which does
> +	 * already queue its callback on cmd_sync_work.
> +	 */
> +	return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
>   }
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 25979f4283a6..59f9d457ca31 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -2921,7 +2921,12 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
>   	if (!conn)
>   		return 0;
>   
> -	return hci_abort_conn_sync(hdev, conn, HCI_ERROR_REMOTE_USER_TERM);
> +	/* Disregard any possible error since the likes of hci_abort_conn_sync
> +	 * will cleanup the connection no matter the error.
> +	 */
> +	hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
> +
> +	return 0;
>   }
>   
>   static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
> @@ -3053,13 +3058,44 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
>   	return err;
>   }
>   
> +static void disconnect_complete(struct hci_dev *hdev, void *data, int err)
> +{
> +	struct mgmt_pending_cmd *cmd = data;
> +
> +	cmd->cmd_complete(cmd, mgmt_status(err));
> +	mgmt_pending_free(cmd);
> +}
> +
> +static int disconnect_sync(struct hci_dev *hdev, void *data)
> +{
> +	struct mgmt_pending_cmd *cmd = data;
> +	struct mgmt_cp_disconnect *cp = cmd->param;
> +	struct hci_conn *conn;
> +
> +	if (cp->addr.type == BDADDR_BREDR)
> +		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
> +					       &cp->addr.bdaddr);
> +	else
> +		conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
> +					       le_addr_type(cp->addr.type));
> +
> +	if (!conn)
> +		return -ENOTCONN;
> +
> +	/* Disregard any possible error since the likes of hci_abort_conn_sync
> +	 * will cleanup the connection no matter the error.
> +	 */
> +	hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
> +
> +	return 0;
> +}
> +
>   static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
>   		      u16 len)
>   {
>   	struct mgmt_cp_disconnect *cp = data;
>   	struct mgmt_rp_disconnect rp;
>   	struct mgmt_pending_cmd *cmd;
> -	struct hci_conn *conn;
>   	int err;
>   
>   	bt_dev_dbg(hdev, "sock %p", sk);
> @@ -3082,27 +3118,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
>   		goto failed;
>   	}
>   
> -	if (pending_find(MGMT_OP_DISCONNECT, hdev)) {
> -		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
> -					MGMT_STATUS_BUSY, &rp, sizeof(rp));
> -		goto failed;
> -	}
> -
> -	if (cp->addr.type == BDADDR_BREDR)
> -		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
> -					       &cp->addr.bdaddr);
> -	else
> -		conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
> -					       le_addr_type(cp->addr.type));
> -
> -	if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
> -		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
> -					MGMT_STATUS_NOT_CONNECTED, &rp,
> -					sizeof(rp));
> -		goto failed;
> -	}
> -
> -	cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
> +	cmd = mgmt_pending_new(sk, MGMT_OP_DISCONNECT, hdev, data, len);
>   	if (!cmd) {
>   		err = -ENOMEM;
>   		goto failed;
> @@ -3110,9 +3126,10 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
>   
>   	cmd->cmd_complete = generic_cmd_complete;
>   
> -	err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
> +	err = hci_cmd_sync_queue(hdev, disconnect_sync, cmd,
> +				 disconnect_complete);
>   	if (err < 0)
> -		mgmt_pending_remove(cmd);
> +		mgmt_pending_free(cmd);
>   
>   failed:
>   	hci_dev_unlock(hdev);
> @@ -9689,18 +9706,6 @@ void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
>   	mgmt_event_skb(skb, NULL);
>   }
>   
> -static void disconnect_rsp(struct mgmt_pending_cmd *cmd, void *data)
> -{
> -	struct sock **sk = data;
> -
> -	cmd->cmd_complete(cmd, 0);
> -
> -	*sk = cmd->sk;
> -	sock_hold(*sk);
> -
> -	mgmt_pending_remove(cmd);
> -}
> -
>   static void unpair_device_rsp(struct mgmt_pending_cmd *cmd, void *data)
>   {
>   	struct hci_dev *hdev = data;
> @@ -9744,8 +9749,6 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
>   	if (link_type != ACL_LINK && link_type != LE_LINK)
>   		return;
>   
> -	mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
> -
>   	bacpy(&ev.addr.bdaddr, bdaddr);
>   	ev.addr.type = link_to_bdaddr(link_type, addr_type);
>   	ev.reason = reason;
> @@ -9758,9 +9761,6 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
>   
>   	if (sk)
>   		sock_put(sk);
> -
> -	mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
> -			     hdev);
>   }
>   
>   void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
diff mbox series

Patch

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 8f0c9322eadb..d51d8dbea631 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2951,5 +2951,9 @@  int hci_abort_conn(struct hci_conn *conn, u8 reason)
 		return 0;
 	}
 
-	return hci_cmd_sync_queue_once(hdev, abort_conn_sync, conn, NULL);
+	/* Run immediately if on cmd_sync_work since this maybe called
+	 * as a result to MGMT_OP_DISCONNECT and MGMT_OP_UNPAIR which does
+	 * already queue its callback on cmd_sync_work.
+	 */
+	return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
 }
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 25979f4283a6..59f9d457ca31 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2921,7 +2921,12 @@  static int unpair_device_sync(struct hci_dev *hdev, void *data)
 	if (!conn)
 		return 0;
 
-	return hci_abort_conn_sync(hdev, conn, HCI_ERROR_REMOTE_USER_TERM);
+	/* Disregard any possible error since the likes of hci_abort_conn_sync
+	 * will cleanup the connection no matter the error.
+	 */
+	hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+
+	return 0;
 }
 
 static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3053,13 +3058,44 @@  static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
 	return err;
 }
 
+static void disconnect_complete(struct hci_dev *hdev, void *data, int err)
+{
+	struct mgmt_pending_cmd *cmd = data;
+
+	cmd->cmd_complete(cmd, mgmt_status(err));
+	mgmt_pending_free(cmd);
+}
+
+static int disconnect_sync(struct hci_dev *hdev, void *data)
+{
+	struct mgmt_pending_cmd *cmd = data;
+	struct mgmt_cp_disconnect *cp = cmd->param;
+	struct hci_conn *conn;
+
+	if (cp->addr.type == BDADDR_BREDR)
+		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+					       &cp->addr.bdaddr);
+	else
+		conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
+					       le_addr_type(cp->addr.type));
+
+	if (!conn)
+		return -ENOTCONN;
+
+	/* Disregard any possible error since the likes of hci_abort_conn_sync
+	 * will cleanup the connection no matter the error.
+	 */
+	hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+
+	return 0;
+}
+
 static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
 		      u16 len)
 {
 	struct mgmt_cp_disconnect *cp = data;
 	struct mgmt_rp_disconnect rp;
 	struct mgmt_pending_cmd *cmd;
-	struct hci_conn *conn;
 	int err;
 
 	bt_dev_dbg(hdev, "sock %p", sk);
@@ -3082,27 +3118,7 @@  static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
 		goto failed;
 	}
 
-	if (pending_find(MGMT_OP_DISCONNECT, hdev)) {
-		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-					MGMT_STATUS_BUSY, &rp, sizeof(rp));
-		goto failed;
-	}
-
-	if (cp->addr.type == BDADDR_BREDR)
-		conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
-					       &cp->addr.bdaddr);
-	else
-		conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
-					       le_addr_type(cp->addr.type));
-
-	if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
-		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
-					MGMT_STATUS_NOT_CONNECTED, &rp,
-					sizeof(rp));
-		goto failed;
-	}
-
-	cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
+	cmd = mgmt_pending_new(sk, MGMT_OP_DISCONNECT, hdev, data, len);
 	if (!cmd) {
 		err = -ENOMEM;
 		goto failed;
@@ -3110,9 +3126,10 @@  static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
 
 	cmd->cmd_complete = generic_cmd_complete;
 
-	err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
+	err = hci_cmd_sync_queue(hdev, disconnect_sync, cmd,
+				 disconnect_complete);
 	if (err < 0)
-		mgmt_pending_remove(cmd);
+		mgmt_pending_free(cmd);
 
 failed:
 	hci_dev_unlock(hdev);
@@ -9689,18 +9706,6 @@  void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
 	mgmt_event_skb(skb, NULL);
 }
 
-static void disconnect_rsp(struct mgmt_pending_cmd *cmd, void *data)
-{
-	struct sock **sk = data;
-
-	cmd->cmd_complete(cmd, 0);
-
-	*sk = cmd->sk;
-	sock_hold(*sk);
-
-	mgmt_pending_remove(cmd);
-}
-
 static void unpair_device_rsp(struct mgmt_pending_cmd *cmd, void *data)
 {
 	struct hci_dev *hdev = data;
@@ -9744,8 +9749,6 @@  void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
 	if (link_type != ACL_LINK && link_type != LE_LINK)
 		return;
 
-	mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
-
 	bacpy(&ev.addr.bdaddr, bdaddr);
 	ev.addr.type = link_to_bdaddr(link_type, addr_type);
 	ev.reason = reason;
@@ -9758,9 +9761,6 @@  void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
 
 	if (sk)
 		sock_put(sk);
-
-	mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
-			     hdev);
 }
 
 void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,