Commit e271ed52 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull power management fixes from Rafael Wysocki:
 "Fix three issues introduced into device suspend/resume error paths in
  the PM core by some of the recent updates.

  First off, replace list_splice() with list_splice_init() in three
  places in device suspend error paths to avoid attempting to use an
  uninitialized list head going forward.

  Second, rearrange device_resume() to avoid leaking the
  power.is_suspended device PM flag to the next system suspend/resume
  cycle where it can confuse rolling back after an error or early
  wakeup.

  Finally, add synchronization to dpm_async_resume_children() to avoid
  resetting the async state mistakenly for devices whose resume
  callbacks have already been queued up for asynchronous execution in
  the given device resume phase, which fortunately can happen only if
  the preceding system suspend transition has been aborted"

* tag 'pm-6.16-rc1-3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  PM: sleep: Add locking to dpm_async_resume_children()
  PM: sleep: Fix power.is_suspended cleanup for direct-complete devices
  PM: sleep: Fix list splicing in device suspend error paths
parents 2c7e4a26 8887abcc
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -637,6 +637,13 @@ static int dpm_async_with_cleanup(struct device *dev, void *fn)

static void dpm_async_resume_children(struct device *dev, async_func_t func)
{
	/*
	 * Prevent racing with dpm_clear_async_state() during initial list
	 * walks in dpm_noirq_resume_devices(), dpm_resume_early(), and
	 * dpm_resume().
	 */
	guard(mutex)(&dpm_list_mtx);

	/*
	 * Start processing "async" children of the device unless it's been
	 * started already for them.
@@ -985,6 +992,8 @@ static void device_resume(struct device *dev, pm_message_t state, bool async)
	if (!dev->power.is_suspended)
		goto Complete;

	dev->power.is_suspended = false;

	if (dev->power.direct_complete) {
		/*
		 * Allow new children to be added under the device after this
@@ -1047,7 +1056,6 @@ static void device_resume(struct device *dev, pm_message_t state, bool async)

 End:
	error = dpm_run_callback(callback, dev, state, info);
	dev->power.is_suspended = false;

	device_unlock(dev);
	dpm_watchdog_clear(&wd);
@@ -1451,7 +1459,7 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
			 * Move all devices to the target list to resume them
			 * properly.
			 */
			list_splice(&dpm_late_early_list, &dpm_noirq_list);
			list_splice_init(&dpm_late_early_list, &dpm_noirq_list);
			break;
		}
	}
@@ -1653,7 +1661,7 @@ int dpm_suspend_late(pm_message_t state)
			 * Move all devices to the target list to resume them
			 * properly.
			 */
			list_splice(&dpm_suspended_list, &dpm_late_early_list);
			list_splice_init(&dpm_suspended_list, &dpm_late_early_list);
			break;
		}
	}
@@ -1946,7 +1954,7 @@ int dpm_suspend(pm_message_t state)
			 * Move all devices to the target list to resume them
			 * properly.
			 */
			list_splice(&dpm_prepared_list, &dpm_suspended_list);
			list_splice_init(&dpm_prepared_list, &dpm_suspended_list);
			break;
		}
	}