···265265 RPM_SUSPENDED, which means that each device is initially regarded by the266266 PM core as 'suspended', regardless of its real hardware status267267268268+ `enum rpm_status last_status;`269269+ - the last runtime PM status of the device captured before disabling runtime270270+ PM for it (invalid initially and when disable_depth is 0)271271+268272 `unsigned int runtime_auto;`269273 - if set, indicates that the user space has allowed the device driver to270274 power manage the device at run time via the /sys/devices/.../power/control···337333338334 `int pm_runtime_resume(struct device *dev);`339335 - execute the subsystem-level resume callback for the device; returns 0 on340340- success, 1 if the device's runtime PM status was already 'active' or341341- error code on failure, where -EAGAIN means it may be safe to attempt to342342- resume the device again in future, but 'power.runtime_error' should be343343- checked additionally, and -EACCES means that 'power.disable_depth' is336336+ success, 1 if the device's runtime PM status is already 'active' (also if337337+ 'power.disable_depth' is nonzero, but the status was 'active' when it was338338+ changing from 0 to 1) or error code on failure, where -EAGAIN means it may339339+ be safe to attempt to resume the device again in future, but340340+ 'power.runtime_error' should be checked additionally, and -EACCES means341341+ that the callback could not be run, because 'power.disable_depth' was344342 different from 0345343346344 `int pm_runtime_resume_and_get(struct device *dev);`
+1-2
drivers/base/core.c
···485485 /* Ensure that all references to the link object have been dropped. */486486 device_link_synchronize_removal();487487488488- while (refcount_dec_not_one(&link->rpm_active))489489- pm_runtime_put(link->supplier);488488+ pm_runtime_release_supplier(link, true);490489491490 put_device(link->consumer);492491 put_device(link->supplier);
+55-31
drivers/base/power/runtime.c
···305305 return 0;306306}307307308308+/**309309+ * pm_runtime_release_supplier - Drop references to device link's supplier.310310+ * @link: Target device link.311311+ * @check_idle: Whether or not to check if the supplier device is idle.312312+ *313313+ * Drop all runtime PM references associated with @link to its supplier device314314+ * and if @check_idle is set, check if that device is idle (and so it can be315315+ * suspended).316316+ */317317+void pm_runtime_release_supplier(struct device_link *link, bool check_idle)318318+{319319+ struct device *supplier = link->supplier;320320+321321+ /*322322+ * The additional power.usage_count check is a safety net in case323323+ * the rpm_active refcount becomes saturated, in which case324324+ * refcount_dec_not_one() would return true forever, but it is not325325+ * strictly necessary.326326+ */327327+ while (refcount_dec_not_one(&link->rpm_active) &&328328+ atomic_read(&supplier->power.usage_count) > 0)329329+ pm_runtime_put_noidle(supplier);330330+331331+ if (check_idle)332332+ pm_request_idle(supplier);333333+}334334+308335static void __rpm_put_suppliers(struct device *dev, bool try_to_suspend)309336{310337 struct device_link *link;311338312339 list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,313313- device_links_read_lock_held()) {314314-315315- while (refcount_dec_not_one(&link->rpm_active))316316- pm_runtime_put_noidle(link->supplier);317317-318318- if (try_to_suspend)319319- pm_request_idle(link->supplier);320320- }340340+ device_links_read_lock_held())341341+ pm_runtime_release_supplier(link, try_to_suspend);321342}322343323344static void rpm_put_suppliers(struct device *dev)···763742 trace_rpm_resume_rcuidle(dev, rpmflags);764743765744 repeat:766766- if (dev->power.runtime_error)745745+ if (dev->power.runtime_error) {767746 retval = -EINVAL;768768- else if (dev->power.disable_depth == 1 && dev->power.is_suspended769769- && dev->power.runtime_status == RPM_ACTIVE)770770- retval = 1;771771- else if (dev->power.disable_depth > 0)772772- retval = -EACCES;747747+ } else if (dev->power.disable_depth > 0) {748748+ if (dev->power.runtime_status == RPM_ACTIVE &&749749+ dev->power.last_status == RPM_ACTIVE)750750+ retval = 1;751751+ else752752+ retval = -EACCES;753753+ }773754 if (retval)774755 goto out;775756···14331410 /* Update time accounting before disabling PM-runtime. */14341411 update_pm_runtime_accounting(dev);1435141214361436- if (!dev->power.disable_depth++)14131413+ if (!dev->power.disable_depth++) {14371414 __pm_runtime_barrier(dev);14151415+ dev->power.last_status = dev->power.runtime_status;14161416+ }1438141714391418 out:14401419 spin_unlock_irq(&dev->power.lock);···1453142814541429 spin_lock_irqsave(&dev->power.lock, flags);1455143014561456- if (dev->power.disable_depth > 0) {14571457- dev->power.disable_depth--;14581458-14591459- /* About to enable runtime pm, set accounting_timestamp to now */14601460- if (!dev->power.disable_depth)14611461- dev->power.accounting_timestamp = ktime_get_mono_fast_ns();14621462- } else {14311431+ if (!dev->power.disable_depth) {14631432 dev_warn(dev, "Unbalanced %s!\n", __func__);14331433+ goto out;14641434 }1465143514661466- WARN(!dev->power.disable_depth &&14671467- dev->power.runtime_status == RPM_SUSPENDED &&14681468- !dev->power.ignore_children &&14691469- atomic_read(&dev->power.child_count) > 0,14701470- "Enabling runtime PM for inactive device (%s) with active children\n",14711471- dev_name(dev));14361436+ if (--dev->power.disable_depth > 0)14371437+ goto out;1472143814391439+ dev->power.last_status = RPM_INVALID;14401440+ dev->power.accounting_timestamp = ktime_get_mono_fast_ns();14411441+14421442+ if (dev->power.runtime_status == RPM_SUSPENDED &&14431443+ !dev->power.ignore_children &&14441444+ atomic_read(&dev->power.child_count) > 0)14451445+ dev_warn(dev, "Enabling runtime PM for inactive device with active children\n");14461446+14471447+out:14731448 spin_unlock_irqrestore(&dev->power.lock, flags);14741449}14751450EXPORT_SYMBOL_GPL(pm_runtime_enable);···16651640void pm_runtime_init(struct device *dev)16661641{16671642 dev->power.runtime_status = RPM_SUSPENDED;16431643+ dev->power.last_status = RPM_INVALID;16681644 dev->power.idle_notification = false;1669164516701646 dev->power.disable_depth = 1;···17981772 return;1799177318001774 pm_runtime_drop_link_count(link->consumer);18011801-18021802- while (refcount_dec_not_one(&link->rpm_active))18031803- pm_runtime_put(link->supplier);17751775+ pm_runtime_release_supplier(link, true);18041776}1805177718061778static bool pm_runtime_need_not_resume(struct device *dev)