Skip to content

Commit

Permalink
Bluetooth: Restore running state if suspend fails
Browse files Browse the repository at this point in the history
If Bluetooth fails to enter the suspended state correctly, restore the
state to running (re-enabling scans). PM_POST_SUSPEND is only sent to
notifiers that successfully return from PM_PREPARE_SUSPEND notification
so we should recover gracefully if it fails.

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
  • Loading branch information
Abhishek Pandit-Subedi authored and Marcel Holtmann committed Mar 23, 2020
1 parent 0d7043f commit 8731840
Showing 1 changed file with 20 additions and 19 deletions.
39 changes: 20 additions & 19 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -3305,6 +3305,15 @@ static void hci_prepare_suspend(struct work_struct *work)
hci_dev_unlock(hdev);
}

static int hci_change_suspend_state(struct hci_dev *hdev,
enum suspended_state next)
{
hdev->suspend_state_next = next;
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
return hci_suspend_wait_event(hdev);
}

static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
void *data)
{
Expand All @@ -3330,32 +3339,24 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
* connectable (disabling scanning)
* - Second, program event filter/whitelist and enable scan
*/
hdev->suspend_state_next = BT_SUSPEND_DISCONNECT;
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
ret = hci_suspend_wait_event(hdev);
ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT);

/* If the disconnect portion failed, don't attempt to complete
* by configuring the whitelist. The suspend notifier will
* follow a cancelled suspend with a PM_POST_SUSPEND
* notification.
*/
if (!ret) {
hdev->suspend_state_next = BT_SUSPEND_COMPLETE;
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
ret = hci_suspend_wait_event(hdev);
}
/* Only configure whitelist if disconnect succeeded */
if (!ret)
ret = hci_change_suspend_state(hdev,
BT_SUSPEND_COMPLETE);
} else if (action == PM_POST_SUSPEND) {
hdev->suspend_state_next = BT_RUNNING;
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
ret = hci_suspend_wait_event(hdev);
ret = hci_change_suspend_state(hdev, BT_RUNNING);
}

/* If suspend failed, restore it to running */
if (ret && action == PM_SUSPEND_PREPARE)
hci_change_suspend_state(hdev, BT_RUNNING);

done:
return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP;
}

/* Alloc HCI device */
struct hci_dev *hci_alloc_dev(void)
{
Expand Down

0 comments on commit 8731840

Please sign in to comment.