diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index bbefde319f95..cee6be23acc8 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -655,6 +655,7 @@ static inline bool iso_enabled(void)
 int mgmt_init(void);
 void mgmt_exit(void);
 void mgmt_cleanup(struct sock *sk);
+void mgmt_pending_cleanup(struct hci_dev *hdev);
 
 void bt_sock_reclassify_lock(struct sock *sk, int proto);
 
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 54bfeeaa0995..a9449b9e86fc 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1683,6 +1683,7 @@ static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
 
 #define hci_dev_lock(d)		mutex_lock(&d->lock)
 #define hci_dev_unlock(d)	mutex_unlock(&d->lock)
+#define hci_dev_is_locked(d)	mutex_is_locked(&d->lock)
 
 #define to_hci_dev(d) container_of(d, struct hci_dev, dev)
 #define to_hci_conn(c) container_of(c, struct hci_conn, dev)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 5eb0600bbd03..d7d2e17d2aec 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2685,10 +2685,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
 		hci_dev_unlock(hdev);
 	}
 
-	/* mgmt_index_removed should take care of emptying the
-	 * pending list */
-	BUG_ON(!list_empty(&hdev->mgmt_pending));
-
+	mgmt_pending_cleanup(hdev);
 	hci_sock_dev_event(hdev, HCI_DEV_UNREG);
 
 	if (hdev->rfkill) {
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 46b22708dfbd..9126235a1a82 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1447,14 +1447,12 @@ static void settings_rsp(struct mgmt_pending_cmd *cmd, void *data)
 
 	send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
 
-	list_del(&cmd->list);
-
 	if (match->sk == NULL) {
 		match->sk = cmd->sk;
 		sock_hold(match->sk);
 	}
 
-	mgmt_pending_free(cmd);
+	mgmt_pending_remove(cmd);
 }
 
 static void cmd_status_rsp(struct mgmt_pending_cmd *cmd, void *data)
@@ -2598,18 +2596,25 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
 static bool pending_eir_or_class(struct hci_dev *hdev)
 {
 	struct mgmt_pending_cmd *cmd;
+	bool found = false;
 
-	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+	rcu_read_lock();
+
+	list_for_each_entry_rcu(cmd, &hdev->mgmt_pending, list) {
+		if (atomic_read(&cmd->deleted))
+		    continue;
 		switch (cmd->opcode) {
 		case MGMT_OP_ADD_UUID:
 		case MGMT_OP_REMOVE_UUID:
 		case MGMT_OP_SET_DEV_CLASS:
 		case MGMT_OP_SET_POWERED:
-			return true;
+			found = true;
+			break;
 		}
 	}
 
-	return false;
+	rcu_read_unlock();
+	return found;
 }
 
 static const u8 bluetooth_base_uuid[] = {
@@ -3401,19 +3406,23 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
 static struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn)
 {
 	struct hci_dev *hdev = conn->hdev;
-	struct mgmt_pending_cmd *cmd;
+	struct mgmt_pending_cmd *tmp, *cmd = NULL;
 
-	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-		if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
-			continue;
+	rcu_read_lock();
 
-		if (cmd->user_data != conn)
+	list_for_each_entry_rcu(tmp, &hdev->mgmt_pending, list) {
+		if (atomic_read(&tmp->deleted))
 			continue;
-
-		return cmd;
+		if (tmp->opcode != MGMT_OP_PAIR_DEVICE)
+			continue;
+		if (tmp->user_data != conn)
+			continue;
+		cmd = tmp;
+		break;
 	}
 
-	return NULL;
+	rcu_read_unlock();
+	return cmd;
 }
 
 static int pairing_complete(struct mgmt_pending_cmd *cmd, u8 status)
@@ -10476,3 +10485,14 @@ void mgmt_cleanup(struct sock *sk)
 
 	read_unlock(&hci_dev_list_lock);
 }
+
+void mgmt_pending_cleanup(struct hci_dev *hdev)
+{
+	struct mgmt_pending_cmd *cmd, *tmp;
+
+	list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
+		BUG_ON(atomic_read(&cmd->deleted) == 0);
+		list_del_rcu(&cmd->list);
+		mgmt_pending_free(cmd);
+	}
+}
diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c
index e5ff65e424b5..978e12db969c 100644
--- a/net/bluetooth/mgmt_util.c
+++ b/net/bluetooth/mgmt_util.c
@@ -217,30 +217,44 @@ int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
 struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
 					   struct hci_dev *hdev)
 {
-	struct mgmt_pending_cmd *cmd;
+	struct mgmt_pending_cmd *tmp, *cmd = NULL;
+
+	rcu_read_lock();
 
-	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
-		if (hci_sock_get_channel(cmd->sk) != channel)
+	list_for_each_entry_rcu(tmp, &hdev->mgmt_pending, list) {
+		if (atomic_read(&tmp->deleted))
+			continue;
+		if (hci_sock_get_channel(tmp->sk) != channel)
 			continue;
-		if (cmd->opcode == opcode)
-			return cmd;
+		if (tmp->opcode == opcode) {
+			cmd = tmp;
+			break;
+		}
 	}
 
-	return NULL;
+	rcu_read_unlock();
+	return cmd;
 }
 
 void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
 			  void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
 			  void *data)
 {
-	struct mgmt_pending_cmd *cmd, *tmp;
+	struct mgmt_pending_cmd *cmd;
+
+	rcu_read_lock();
 
-	list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
+	list_for_each_entry_rcu(cmd, &hdev->mgmt_pending, list) {
+		if (atomic_read(&cmd->deleted))
+			continue;
 		if (opcode > 0 && cmd->opcode != opcode)
 			continue;
-
+		rcu_read_unlock();
 		cb(cmd, data);
+		rcu_read_lock();
 	}
+
+	rcu_read_unlock();
 }
 
 struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
@@ -270,17 +284,34 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
 	return cmd;
 }
 
+static void mgmt_pending_delayed_free(struct rcu_head *rcu)
+{
+	struct mgmt_pending_cmd *cmd =
+		container_of(rcu, struct mgmt_pending_cmd, head);
+	kfree(cmd->param);
+	kfree(cmd);
+}
+
 struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
 					  struct hci_dev *hdev,
 					  void *data, u16 len)
 {
-	struct mgmt_pending_cmd *cmd;
+	struct mgmt_pending_cmd *cmd, *old, *tmp;
 
 	cmd = mgmt_pending_new(sk, opcode, hdev, data, len);
 	if (!cmd)
 		return NULL;
 
-	list_add_tail(&cmd->list, &hdev->mgmt_pending);
+	BUG_ON(!hci_dev_is_locked(hdev));
+
+	list_for_each_entry_safe(old, tmp, &hdev->mgmt_pending, list)
+		if (atomic_read(&old->deleted)) {
+			list_del_rcu(&old->list);
+			sock_put(old->sk);
+			call_rcu(&old->head, mgmt_pending_delayed_free);
+		}
+
+	list_add_tail_rcu(&cmd->list, &hdev->mgmt_pending);
 
 	return cmd;
 }
@@ -294,8 +325,7 @@ void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
 
 void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
 {
-	list_del(&cmd->list);
-	mgmt_pending_free(cmd);
+	atomic_set(&cmd->deleted, 1);
 }
 
 void mgmt_mesh_foreach(struct hci_dev *hdev,
diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h
index f2ba994ab1d8..5e681bc74220 100644
--- a/net/bluetooth/mgmt_util.h
+++ b/net/bluetooth/mgmt_util.h
@@ -32,6 +32,8 @@ struct mgmt_mesh_tx {
 
 struct mgmt_pending_cmd {
 	struct list_head list;
+	struct rcu_head head;
+	atomic_t deleted;
 	u16 opcode;
 	int index;
 	void *param;
@@ -65,6 +67,7 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
 					  void *data, u16 len);
 void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
 void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
+void mgmt_pending_cleanup(struct hci_dev *hdev);
 void mgmt_mesh_foreach(struct hci_dev *hdev,
 		       void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
 		       void *data, struct sock *sk);