1403 lines · 38.8 KB · C
RawDownload
/* * drivers/base/power/runtime.c - Helper functions for device runtime PM * * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. * Copyright (C) 2010 Alan Stern <stern@rowland.harvard.edu> * * This file is released under the GPLv2. */ #include <linux/sched.h>#include <linux/export.h>#include <linux/pm_runtime.h>#include <trace/events/rpm.h>#include "power.h" static int rpm_resume(struct device *dev, int rpmflags);static int rpm_suspend(struct device *dev, int rpmflags); /** * update_pm_runtime_accounting - Update the time accounting of power states * @dev: Device to update the accounting for * * In order to be able to have time accounting of the various power states * (as used by programs such as PowerTOP to show the effectiveness of runtime * PM), we need to track the time spent in each state. * update_pm_runtime_accounting must be called each time before the * runtime_status field is updated, to account the time in the old state * correctly. */void update_pm_runtime_accounting(struct device *dev){	unsigned long now = jiffies;	unsigned long delta; 	delta = now - dev->power.accounting_timestamp; 	dev->power.accounting_timestamp = now; 	if (dev->power.disable_depth > 0)		return; 	if (dev->power.runtime_status == RPM_SUSPENDED)		dev->power.suspended_jiffies += delta;	else		dev->power.active_jiffies += delta;} static void __update_runtime_status(struct device *dev, enum rpm_status status){	update_pm_runtime_accounting(dev);	dev->power.runtime_status = status;} /** * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. * @dev: Device to handle. */static void pm_runtime_deactivate_timer(struct device *dev){	if (dev->power.timer_expires > 0) {		del_timer(&dev->power.suspend_timer);		dev->power.timer_expires = 0;	}} /** * pm_runtime_cancel_pending - Deactivate suspend timer and cancel requests. * @dev: Device to handle. */static void pm_runtime_cancel_pending(struct device *dev){	pm_runtime_deactivate_timer(dev);	/*	 * In case there's a request pending, make sure its work function will	 * return without doing anything.	 */	dev->power.request = RPM_REQ_NONE;} /* * pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time. * @dev: Device to handle. * * Compute the autosuspend-delay expiration time based on the device's * power.last_busy time.  If the delay has already expired or is disabled * (negative) or the power.use_autosuspend flag isn't set, return 0. * Otherwise return the expiration time in jiffies (adjusted to be nonzero). * * This function may be called either with or without dev->power.lock held. * Either way it can be racy, since power.last_busy may be updated at any time. */unsigned long pm_runtime_autosuspend_expiration(struct device *dev){	int autosuspend_delay;	long elapsed;	unsigned long last_busy;	unsigned long expires = 0; 	if (!dev->power.use_autosuspend)		goto out; 	autosuspend_delay = ACCESS_ONCE(dev->power.autosuspend_delay);	if (autosuspend_delay < 0)		goto out; 	last_busy = ACCESS_ONCE(dev->power.last_busy);	elapsed = jiffies - last_busy;	if (elapsed < 0)		goto out;	/* jiffies has wrapped around. */ 	/*	 * If the autosuspend_delay is >= 1 second, align the timer by rounding	 * up to the nearest second.	 */	expires = last_busy + msecs_to_jiffies(autosuspend_delay);	if (autosuspend_delay >= 1000)		expires = round_jiffies(expires);	expires += !expires;	if (elapsed >= expires - last_busy)		expires = 0;	/* Already expired. */  out:	return expires;}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. */static int rpm_check_suspend_allowed(struct device *dev){	int retval = 0; 	if (dev->power.runtime_error)		retval = -EINVAL;	else if (dev->power.disable_depth > 0)		retval = -EACCES;	else if (atomic_read(&dev->power.usage_count) > 0)		retval = -EAGAIN;	else if (!pm_children_suspended(dev))		retval = -EBUSY; 	/* Pending resume requests take precedence over suspends. */	else if ((dev->power.deferred_resume			&& dev->power.runtime_status == RPM_SUSPENDING)	    || (dev->power.request_pending			&& dev->power.request == RPM_REQ_RESUME))		retval = -EAGAIN;	else if (__dev_pm_qos_read_value(dev) < 0)		retval = -EPERM;	else if (dev->power.runtime_status == RPM_SUSPENDED)		retval = 1; 	return retval;} /** * __rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */static int __rpm_callback(int (*cb)(struct device *), struct device *dev)	__releases(&dev->power.lock) __acquires(&dev->power.lock){	int retval; 	if (dev->power.irq_safe)		spin_unlock(&dev->power.lock);	else		spin_unlock_irq(&dev->power.lock); 	retval = cb(dev); 	if (dev->power.irq_safe)		spin_lock(&dev->power.lock);	else		spin_lock_irq(&dev->power.lock); 	return retval;} /** * rpm_idle - Notify device bus type if the device can be suspended. * @dev: Device to notify the bus type about. * @rpmflags: Flag bits. * * Check if the device's runtime PM status allows it to be suspended.  If * another idle notification has been started earlier, return immediately.  If * the RPM_ASYNC flag is set then queue an idle-notification request; otherwise * run the ->runtime_idle() callback directly. If the ->runtime_idle callback * doesn't exist or if it returns 0, call rpm_suspend with the RPM_AUTO flag. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_idle(struct device *dev, int rpmflags){	int (*callback)(struct device *);	int retval; 	trace_rpm_idle(dev, rpmflags);	retval = rpm_check_suspend_allowed(dev);	if (retval < 0)		;	/* Conditions are wrong. */ 	/* Idle notifications are allowed only in the RPM_ACTIVE state. */	else if (dev->power.runtime_status != RPM_ACTIVE)		retval = -EAGAIN; 	/*	 * Any pending request other than an idle notification takes	 * precedence over us, except that the timer may be running.	 */	else if (dev->power.request_pending &&	    dev->power.request > RPM_REQ_IDLE)		retval = -EAGAIN; 	/* Act as though RPM_NOWAIT is always set. */	else if (dev->power.idle_notification)		retval = -EINPROGRESS;	if (retval)		goto out; 	/* Pending requests need to be canceled. */	dev->power.request = RPM_REQ_NONE; 	if (dev->power.no_callbacks)		goto out; 	/* Carry out an asynchronous or a synchronous idle notification. */	if (rpmflags & RPM_ASYNC) {		dev->power.request = RPM_REQ_IDLE;		if (!dev->power.request_pending) {			dev->power.request_pending = true;			queue_work(pm_wq, &dev->power.work);		}		trace_rpm_return_int(dev, _THIS_IP_, 0);		return 0;	} 	dev->power.idle_notification = true; 	if (dev->pm_domain)		callback = dev->pm_domain->ops.runtime_idle;	else if (dev->type && dev->type->pm)		callback = dev->type->pm->runtime_idle;	else if (dev->class && dev->class->pm)		callback = dev->class->pm->runtime_idle;	else if (dev->bus && dev->bus->pm)		callback = dev->bus->pm->runtime_idle;	else		callback = NULL; 	if (!callback && dev->driver && dev->driver->pm)		callback = dev->driver->pm->runtime_idle; 	if (callback)		retval = __rpm_callback(callback, dev); 	dev->power.idle_notification = false;	wake_up_all(&dev->power.wait_queue);  out:	trace_rpm_return_int(dev, _THIS_IP_, retval);	return retval ? retval : rpm_suspend(dev, rpmflags | RPM_AUTO);} /** * rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */static int rpm_callback(int (*cb)(struct device *), struct device *dev){	int retval; 	if (!cb)		return -ENOSYS; 	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;} /** * rpm_suspend - Carry out runtime suspend of given device. * @dev: Device to suspend. * @rpmflags: Flag bits. * * Check if the device's runtime PM status allows it to be suspended. * Cancel a pending idle notification, autosuspend or suspend. If * another suspend has been started earlier, either return immediately * or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC * flags. If the RPM_ASYNC flag is set then queue a suspend request; * otherwise run the ->runtime_suspend() callback directly. When * ->runtime_suspend succeeded, if a deferred resume was requested while * the callback was running then carry it out, otherwise send an idle * notification for its parent (if the suspend succeeded and both * ignore_children of parent->power and irq_safe of dev->power are not set). * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO * flag is set and the next autosuspend-delay expiration time is in the * future, schedule another autosuspend attempt. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_suspend(struct device *dev, int rpmflags)	__releases(&dev->power.lock) __acquires(&dev->power.lock){	int (*callback)(struct device *);	struct device *parent = NULL;	int retval; 	trace_rpm_suspend(dev, rpmflags);  repeat:	retval = rpm_check_suspend_allowed(dev); 	if (retval < 0)		;	/* Conditions are wrong. */ 	/* Synchronous suspends are not allowed in the RPM_RESUMING state. */	else if (dev->power.runtime_status == RPM_RESUMING &&	    !(rpmflags & RPM_ASYNC))		retval = -EAGAIN;	if (retval)		goto out; 	/* If the autosuspend_delay time hasn't expired yet, reschedule. */	if ((rpmflags & RPM_AUTO)	    && dev->power.runtime_status != RPM_SUSPENDING) {		unsigned long expires = pm_runtime_autosuspend_expiration(dev); 		if (expires != 0) {			/* Pending requests need to be canceled. */			dev->power.request = RPM_REQ_NONE; 			/*			 * Optimization: If the timer is already running and is			 * set to expire at or before the autosuspend delay,			 * avoid the overhead of resetting it.  Just let it			 * expire; pm_suspend_timer_fn() will take care of the			 * rest.			 */			if (!(dev->power.timer_expires && time_before_eq(			    dev->power.timer_expires, expires))) {				dev->power.timer_expires = expires;				mod_timer(&dev->power.suspend_timer, expires);			}			dev->power.timer_autosuspends = 1;			goto out;		}	} 	/* Other scheduled or pending requests need to be canceled. */	pm_runtime_cancel_pending(dev); 	if (dev->power.runtime_status == RPM_SUSPENDING) {		DEFINE_WAIT(wait); 		if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {			retval = -EINPROGRESS;			goto out;		} 		if (dev->power.irq_safe) {			spin_unlock(&dev->power.lock); 			cpu_relax(); 			spin_lock(&dev->power.lock);			goto repeat;		} 		/* Wait for the other suspend running in parallel with us. */		for (;;) {			prepare_to_wait(&dev->power.wait_queue, &wait,					TASK_UNINTERRUPTIBLE);			if (dev->power.runtime_status != RPM_SUSPENDING)				break; 			spin_unlock_irq(&dev->power.lock); 			schedule(); 			spin_lock_irq(&dev->power.lock);		}		finish_wait(&dev->power.wait_queue, &wait);		goto repeat;	} 	if (dev->power.no_callbacks)		goto no_callback;	/* Assume success. */ 	/* Carry out an asynchronous or a synchronous suspend. */	if (rpmflags & RPM_ASYNC) {		dev->power.request = (rpmflags & RPM_AUTO) ?		    RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;		if (!dev->power.request_pending) {			dev->power.request_pending = true;			queue_work(pm_wq, &dev->power.work);		}		goto out;	} 	__update_runtime_status(dev, RPM_SUSPENDING); 	if (dev->pm_domain)		callback = dev->pm_domain->ops.runtime_suspend;	else if (dev->type && dev->type->pm)		callback = dev->type->pm->runtime_suspend;	else if (dev->class && dev->class->pm)		callback = dev->class->pm->runtime_suspend;

Lines 1–500 of 1403.