diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 08:47:44 -0700 | 
| commit | bf61c8840efe60fd8f91446860b63338fb424158 (patch) | |
| tree | 7a71832407a4f0d6346db773343f4c3ae2257b19 /drivers/base/power/runtime.c | |
| parent | 5846115b30f3a881e542c8bfde59a699c1c13740 (diff) | |
| parent | 0c6a61657da78098472fd0eb71cc01f2387fa1bb (diff) | |
| download | olio-linux-3.10-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz olio-linux-3.10-bf61c8840efe60fd8f91446860b63338fb424158.zip  | |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'drivers/base/power/runtime.c')
| -rw-r--r-- | drivers/base/power/runtime.c | 89 | 
1 files changed, 88 insertions, 1 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 3148b10dc2e..1244930e3d7 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -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. @@ -278,7 +348,24 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)  	if (!cb)  		return -ENOSYS; -	retval = __rpm_callback(cb, dev); +	if (dev->power.memalloc_noio) { +		unsigned int noio_flag; + +		/* +		 * Deadlock might be caused if memory allocation with +		 * GFP_KERNEL happens inside runtime_suspend and +		 * runtime_resume callbacks of one block device's +		 * ancestor or the block device itself. Network +		 * device might be thought as part of iSCSI block +		 * device, so network device and its ancestor should +		 * be marked as memalloc_noio too. +		 */ +		noio_flag = memalloc_noio_save(); +		retval = __rpm_callback(cb, dev); +		memalloc_noio_restore(noio_flag); +	} else { +		retval = __rpm_callback(cb, dev); +	}  	dev->power.runtime_error = retval;  	return retval != -EACCES ? retval : -EIO;  |