2020-03-20 00:08:09

by Abhishek Pandit-Subedi

[permalink] [raw]
Subject: [PATCH 1/2] 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 <[email protected]>
---

net/bluetooth/hci_core.c | 39 ++++++++++++++++++++-------------------
1 file changed, 20 insertions(+), 19 deletions(-)

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index dbd2ad3a26ed..2e7bc2da8371 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -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)
{
@@ -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)
{
--
2.25.1.696.g5e7596f4ac-goog