|
@@ -124,6 +124,76 @@ unsigned long pm_runtime_autosuspend_expiration(struct device *dev)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration);
|
|
|
|
|
|
+static int dev_memalloc_noio(struct device *dev, void *data)
|
|
|
+{
|
|
|
+ return dev->power.memalloc_noio;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * pm_runtime_set_memalloc_noio - Set a device's memalloc_noio flag.
|
|
|
+ * @dev: Device to handle.
|
|
|
+ * @enable: True for setting the flag and False for clearing the flag.
|
|
|
+ *
|
|
|
+ * Set the flag for all devices in the path from the device to the
|
|
|
+ * root device in the device tree if @enable is true, otherwise clear
|
|
|
+ * the flag for devices in the path whose siblings don't set the flag.
|
|
|
+ *
|
|
|
+ * The function should only be called by block device, or network
|
|
|
+ * device driver for solving the deadlock problem during runtime
|
|
|
+ * resume/suspend:
|
|
|
+ *
|
|
|
+ * If memory allocation with GFP_KERNEL is called inside runtime
|
|
|
+ * resume/suspend callback of any one of its ancestors(or the
|
|
|
+ * block device itself), the deadlock may be triggered inside the
|
|
|
+ * memory allocation since it might not complete until the block
|
|
|
+ * device becomes active and the involed page I/O finishes. The
|
|
|
+ * situation is pointed out first by Alan Stern. Network device
|
|
|
+ * are involved in iSCSI kind of situation.
|
|
|
+ *
|
|
|
+ * The lock of dev_hotplug_mutex is held in the function for handling
|
|
|
+ * hotplug race because pm_runtime_set_memalloc_noio() may be called
|
|
|
+ * in async probe().
|
|
|
+ *
|
|
|
+ * The function should be called between device_add() and device_del()
|
|
|
+ * on the affected device(block/network device).
|
|
|
+ */
|
|
|
+void pm_runtime_set_memalloc_noio(struct device *dev, bool enable)
|
|
|
+{
|
|
|
+ static DEFINE_MUTEX(dev_hotplug_mutex);
|
|
|
+
|
|
|
+ mutex_lock(&dev_hotplug_mutex);
|
|
|
+ for (;;) {
|
|
|
+ bool enabled;
|
|
|
+
|
|
|
+ /* hold power lock since bitfield is not SMP-safe. */
|
|
|
+ spin_lock_irq(&dev->power.lock);
|
|
|
+ enabled = dev->power.memalloc_noio;
|
|
|
+ dev->power.memalloc_noio = enable;
|
|
|
+ spin_unlock_irq(&dev->power.lock);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * not need to enable ancestors any more if the device
|
|
|
+ * has been enabled.
|
|
|
+ */
|
|
|
+ if (enabled && enable)
|
|
|
+ break;
|
|
|
+
|
|
|
+ dev = dev->parent;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * clear flag of the parent device only if all the
|
|
|
+ * children don't set the flag because ancestor's
|
|
|
+ * flag was set by any one of the descendants.
|
|
|
+ */
|
|
|
+ if (!dev || (!enable &&
|
|
|
+ device_for_each_child(dev, NULL,
|
|
|
+ dev_memalloc_noio)))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mutex_unlock(&dev_hotplug_mutex);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(pm_runtime_set_memalloc_noio);
|
|
|
+
|
|
|
/**
|
|
|
* rpm_check_suspend_allowed - Test whether a device may be suspended.
|
|
|
* @dev: Device to test.
|