Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Abhishek Pandit-Subedi and committed by
Marcel Holtmann
8731840a 0d7043f3

+20 -19
+20 -19
net/bluetooth/hci_core.c
··· 3305 3305 hci_dev_unlock(hdev); 3306 3306 } 3307 3307 3308 + static int hci_change_suspend_state(struct hci_dev *hdev, 3309 + enum suspended_state next) 3310 + { 3311 + hdev->suspend_state_next = next; 3312 + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3313 + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3314 + return hci_suspend_wait_event(hdev); 3315 + } 3316 + 3308 3317 static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action, 3309 3318 void *data) 3310 3319 { ··· 3339 3330 * connectable (disabling scanning) 3340 3331 * - Second, program event filter/whitelist and enable scan 3341 3332 */ 3342 - hdev->suspend_state_next = BT_SUSPEND_DISCONNECT; 3343 - set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3344 - queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3345 - ret = hci_suspend_wait_event(hdev); 3333 + ret = hci_change_suspend_state(hdev, BT_SUSPEND_DISCONNECT); 3346 3334 3347 - /* If the disconnect portion failed, don't attempt to complete 3348 - * by configuring the whitelist. The suspend notifier will 3349 - * follow a cancelled suspend with a PM_POST_SUSPEND 3350 - * notification. 3351 - */ 3352 - if (!ret) { 3353 - hdev->suspend_state_next = BT_SUSPEND_COMPLETE; 3354 - set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3355 - queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3356 - ret = hci_suspend_wait_event(hdev); 3357 - } 3335 + /* Only configure whitelist if disconnect succeeded */ 3336 + if (!ret) 3337 + ret = hci_change_suspend_state(hdev, 3338 + BT_SUSPEND_COMPLETE); 3358 3339 } else if (action == PM_POST_SUSPEND) { 3359 - hdev->suspend_state_next = BT_RUNNING; 3360 - set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); 3361 - queue_work(hdev->req_workqueue, &hdev->suspend_prepare); 3362 - ret = hci_suspend_wait_event(hdev); 3340 + ret = hci_change_suspend_state(hdev, BT_RUNNING); 3363 3341 } 3342 + 3343 + /* If suspend failed, restore it to running */ 3344 + if (ret && action == PM_SUSPEND_PREPARE) 3345 + hci_change_suspend_state(hdev, BT_RUNNING); 3364 3346 3365 3347 done: 3366 3348 return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP; 3367 3349 } 3350 + 3368 3351 /* Alloc HCI device */ 3369 3352 struct hci_dev *hci_alloc_dev(void) 3370 3353 {