Commit 1857c199 authored by Zhengping Jiang's avatar Zhengping Jiang Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: hci_sync: add lock to protect HCI_UNREGISTER



When the HCI_UNREGISTER flag is set, no jobs should be scheduled. Fix
potential race when HCI_UNREGISTER is set after the flag is tested in
hci_cmd_sync_queue.

Fixes: 0b94f265 ("Bluetooth: hci_sync: Fix queuing commands when HCI_UNREGISTER is set")
Signed-off-by: default avatarZhengping Jiang <jiangzp@google.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent c5d2b6fa
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -515,6 +515,7 @@ struct hci_dev {
	struct work_struct	cmd_sync_work;
	struct list_head	cmd_sync_work_list;
	struct mutex		cmd_sync_work_lock;
	struct mutex		unregister_lock;
	struct work_struct	cmd_sync_cancel_work;
	struct work_struct	reenable_adv_work;

+2 −0
Original line number Diff line number Diff line
@@ -2686,7 +2686,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
{
	BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);

	mutex_lock(&hdev->unregister_lock);
	hci_dev_set_flag(hdev, HCI_UNREGISTER);
	mutex_unlock(&hdev->unregister_lock);

	write_lock(&hci_dev_list_lock);
	list_del(&hdev->list);
+14 −6
Original line number Diff line number Diff line
@@ -629,6 +629,7 @@ void hci_cmd_sync_init(struct hci_dev *hdev)
	INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work);
	INIT_LIST_HEAD(&hdev->cmd_sync_work_list);
	mutex_init(&hdev->cmd_sync_work_lock);
	mutex_init(&hdev->unregister_lock);

	INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
	INIT_WORK(&hdev->reenable_adv_work, reenable_adv);
@@ -692,14 +693,19 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
			void *data, hci_cmd_sync_work_destroy_t destroy)
{
	struct hci_cmd_sync_work_entry *entry;
	int err = 0;

	if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
		return -ENODEV;
	mutex_lock(&hdev->unregister_lock);
	if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
		err = -ENODEV;
		goto unlock;
	}

	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
	if (!entry)
		return -ENOMEM;

	if (!entry) {
		err = -ENOMEM;
		goto unlock;
	}
	entry->func = func;
	entry->data = data;
	entry->destroy = destroy;
@@ -710,7 +716,9 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,

	queue_work(hdev->req_workqueue, &hdev->cmd_sync_work);

	return 0;
unlock:
	mutex_unlock(&hdev->unregister_lock);
	return err;
}
EXPORT_SYMBOL(hci_cmd_sync_submit);