Bluetooth: Restore running state if suspend fails

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>
This commit is contained in:
Abhishek Pandit-Subedi 2020-03-19 17:07:12 -07:00 committed by Marcel Holtmann
parent 0d7043f355
commit 8731840a34

View File

@ -3305,6 +3305,15 @@ static void hci_prepare_suspend(struct work_struct *work)
hci_dev_unlock(hdev); 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, static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
void *data) void *data)
{ {
@ -3330,32 +3339,24 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
* connectable (disabling scanning) * connectable (disabling scanning)
* - Second, program event filter/whitelist and enable scan * - Second, program event filter/whitelist and enable scan
*/ */
hdev->suspend_state_next = BT_SUSPEND_DISCONNECT; ret = hci_change_suspend_state(hdev, 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);
/* If the disconnect portion failed, don't attempt to complete /* Only configure whitelist if disconnect succeeded */
* by configuring the whitelist. The suspend notifier will if (!ret)
* follow a cancelled suspend with a PM_POST_SUSPEND ret = hci_change_suspend_state(hdev,
* notification. BT_SUSPEND_COMPLETE);
*/
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);
}
} else if (action == PM_POST_SUSPEND) { } else if (action == PM_POST_SUSPEND) {
hdev->suspend_state_next = BT_RUNNING; ret = hci_change_suspend_state(hdev, BT_RUNNING);
set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks);
queue_work(hdev->req_workqueue, &hdev->suspend_prepare);
ret = hci_suspend_wait_event(hdev);
} }
/* If suspend failed, restore it to running */
if (ret && action == PM_SUSPEND_PREPARE)
hci_change_suspend_state(hdev, BT_RUNNING);
done: done:
return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP; return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP;
} }
/* Alloc HCI device */ /* Alloc HCI device */
struct hci_dev *hci_alloc_dev(void) struct hci_dev *hci_alloc_dev(void)
{ {