Loading drivers/base/power/qos.c +41 −26 Original line number Original line Diff line number Diff line Loading @@ -24,26 +24,32 @@ * . a system-wide notification callback using the dev_pm_qos_*_global_notifier * . a system-wide notification callback using the dev_pm_qos_*_global_notifier * API. The notification chain data is stored in a static variable. * API. The notification chain data is stored in a static variable. * * * Note about the per-device constraint data struct allocation: * Notes about the per-device constraint data struct allocation: * . The per-device constraints data struct ptr is tored into the device * . The per-device constraints data struct ptr is stored into the device * dev_pm_info. * dev_pm_info. * . To minimize the data usage by the per-device constraints, the data struct * . To minimize the data usage by the per-device constraints, the data struct * is only allocated at the first call to dev_pm_qos_add_request. * is only allocated at the first call to dev_pm_qos_add_request. * . The data is later free'd when the device is removed from the system. * . The data is later free'd when the device is removed from the system. * . A global mutex protects the constraints users from the data being * * allocated and free'd. * Notes about locking: * . The dev->power.lock lock protects the constraints list * (dev->power.constraints) allocation and free, as triggered by the * driver core code at device insertion and removal, * . A global lock dev_pm_qos_lock protects the constraints list entries * from any modification and the notifiers registration and unregistration. * . For both locks a spinlock is needed since this code can be called from * interrupt context or spinlock protected context. */ */ #include <linux/pm_qos.h> #include <linux/pm_qos.h> #include <linux/spinlock.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/device.h> #include <linux/mutex.h> #include <linux/export.h> #include <linux/export.h> #include "power.h" #include "power.h" static DEFINE_MUTEX(dev_pm_qos_mtx); static DEFINE_SPINLOCK(dev_pm_qos_lock); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); Loading Loading @@ -110,18 +116,19 @@ static int apply_constraint(struct dev_pm_qos_request *req, * @dev: device to allocate data for * @dev: device to allocate data for * * * Called at the first call to add_request, for constraint data allocation * Called at the first call to add_request, for constraint data allocation * Must be called with the dev_pm_qos_mtx mutex held * Must be called with the dev_pm_qos_lock lock held */ */ static int dev_pm_qos_constraints_allocate(struct device *dev) static int dev_pm_qos_constraints_allocate(struct device *dev) { { struct pm_qos_constraints *c; struct pm_qos_constraints *c; struct blocking_notifier_head *n; struct blocking_notifier_head *n; unsigned long flags; c = kzalloc(sizeof(*c), GFP_KERNEL); c = kzalloc(sizeof(*c), GFP_ATOMIC); if (!c) if (!c) return -ENOMEM; return -ENOMEM; n = kzalloc(sizeof(*n), GFP_KERNEL); n = kzalloc(sizeof(*n), GFP_ATOMIC); if (!n) { if (!n) { kfree(c); kfree(c); return -ENOMEM; return -ENOMEM; Loading @@ -134,9 +141,9 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->type = PM_QOS_MIN; c->type = PM_QOS_MIN; c->notifiers = n; c->notifiers = n; spin_lock_irq(&dev->power.lock); spin_lock_irqsave(&dev->power.lock, flags); dev->power.constraints = c; dev->power.constraints = c; spin_unlock_irq(&dev->power.lock); spin_unlock_irqrestore(&dev->power.lock, flags); return 0; return 0; } } Loading @@ -150,10 +157,12 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) */ */ void dev_pm_qos_constraints_init(struct device *dev) void dev_pm_qos_constraints_init(struct device *dev) { { mutex_lock(&dev_pm_qos_mtx); unsigned long flags; spin_lock_irqsave(&dev_pm_qos_lock, flags); dev->power.constraints = NULL; dev->power.constraints = NULL; dev->power.power_state = PMSG_ON; dev->power.power_state = PMSG_ON; mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); } } /** /** Loading @@ -166,6 +175,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) { { struct dev_pm_qos_request *req, *tmp; struct dev_pm_qos_request *req, *tmp; struct pm_qos_constraints *c; struct pm_qos_constraints *c; unsigned long flags; /* /* * If the device's PM QoS resume latency limit has been exposed to user * If the device's PM QoS resume latency limit has been exposed to user Loading @@ -173,7 +183,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) */ */ dev_pm_qos_hide_latency_limit(dev); dev_pm_qos_hide_latency_limit(dev); mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); dev->power.power_state = PMSG_INVALID; dev->power.power_state = PMSG_INVALID; c = dev->power.constraints; c = dev->power.constraints; Loading @@ -198,7 +208,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) kfree(c); kfree(c); out: out: mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); } } /** /** Loading @@ -223,6 +233,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, s32 value) s32 value) { { int ret = 0; int ret = 0; unsigned long flags; if (!dev || !req) /*guard against callers passing in null */ if (!dev || !req) /*guard against callers passing in null */ return -EINVAL; return -EINVAL; Loading @@ -233,7 +244,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, req->dev = dev; req->dev = dev; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (!dev->power.constraints) { if (!dev->power.constraints) { if (dev->power.power_state.event == PM_EVENT_INVALID) { if (dev->power.power_state.event == PM_EVENT_INVALID) { Loading @@ -255,7 +266,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, ret = apply_constraint(req, PM_QOS_ADD_REQ, value); ret = apply_constraint(req, PM_QOS_ADD_REQ, value); out: out: mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } Loading @@ -280,6 +291,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) s32 new_value) { { int ret = 0; int ret = 0; unsigned long flags; if (!req) /*guard against callers passing in null */ if (!req) /*guard against callers passing in null */ return -EINVAL; return -EINVAL; Loading @@ -288,7 +300,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, "%s() called for unknown object\n", __func__)) "%s() called for unknown object\n", __func__)) return -EINVAL; return -EINVAL; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (req->dev->power.constraints) { if (req->dev->power.constraints) { if (new_value != req->node.prio) if (new_value != req->node.prio) Loading @@ -299,7 +311,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, ret = -ENODEV; ret = -ENODEV; } } mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); Loading @@ -319,6 +331,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) { { int ret = 0; int ret = 0; unsigned long flags; if (!req) /*guard against callers passing in null */ if (!req) /*guard against callers passing in null */ return -EINVAL; return -EINVAL; Loading @@ -327,7 +340,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) "%s() called for unknown object\n", __func__)) "%s() called for unknown object\n", __func__)) return -EINVAL; return -EINVAL; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (req->dev->power.constraints) { if (req->dev->power.constraints) { ret = apply_constraint(req, PM_QOS_REMOVE_REQ, ret = apply_constraint(req, PM_QOS_REMOVE_REQ, Loading @@ -338,7 +351,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) ret = -ENODEV; ret = -ENODEV; } } mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); Loading @@ -359,8 +372,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) { { int ret = 0; int ret = 0; unsigned long flags; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (!dev->power.constraints) if (!dev->power.constraints) ret = dev->power.power_state.event != PM_EVENT_INVALID ? ret = dev->power.power_state.event != PM_EVENT_INVALID ? Loading @@ -370,7 +384,7 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) ret = blocking_notifier_chain_register( ret = blocking_notifier_chain_register( dev->power.constraints->notifiers, notifier); dev->power.constraints->notifiers, notifier); mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); Loading @@ -389,8 +403,9 @@ int dev_pm_qos_remove_notifier(struct device *dev, struct notifier_block *notifier) struct notifier_block *notifier) { { int retval = 0; int retval = 0; unsigned long flags; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); /* Silently return if the constraints object is not present. */ /* Silently return if the constraints object is not present. */ if (dev->power.constraints) if (dev->power.constraints) Loading @@ -398,7 +413,7 @@ int dev_pm_qos_remove_notifier(struct device *dev, dev->power.constraints->notifiers, dev->power.constraints->notifiers, notifier); notifier); mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return retval; return retval; } } EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); Loading Loading
drivers/base/power/qos.c +41 −26 Original line number Original line Diff line number Diff line Loading @@ -24,26 +24,32 @@ * . a system-wide notification callback using the dev_pm_qos_*_global_notifier * . a system-wide notification callback using the dev_pm_qos_*_global_notifier * API. The notification chain data is stored in a static variable. * API. The notification chain data is stored in a static variable. * * * Note about the per-device constraint data struct allocation: * Notes about the per-device constraint data struct allocation: * . The per-device constraints data struct ptr is tored into the device * . The per-device constraints data struct ptr is stored into the device * dev_pm_info. * dev_pm_info. * . To minimize the data usage by the per-device constraints, the data struct * . To minimize the data usage by the per-device constraints, the data struct * is only allocated at the first call to dev_pm_qos_add_request. * is only allocated at the first call to dev_pm_qos_add_request. * . The data is later free'd when the device is removed from the system. * . The data is later free'd when the device is removed from the system. * . A global mutex protects the constraints users from the data being * * allocated and free'd. * Notes about locking: * . The dev->power.lock lock protects the constraints list * (dev->power.constraints) allocation and free, as triggered by the * driver core code at device insertion and removal, * . A global lock dev_pm_qos_lock protects the constraints list entries * from any modification and the notifiers registration and unregistration. * . For both locks a spinlock is needed since this code can be called from * interrupt context or spinlock protected context. */ */ #include <linux/pm_qos.h> #include <linux/pm_qos.h> #include <linux/spinlock.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/device.h> #include <linux/mutex.h> #include <linux/export.h> #include <linux/export.h> #include "power.h" #include "power.h" static DEFINE_MUTEX(dev_pm_qos_mtx); static DEFINE_SPINLOCK(dev_pm_qos_lock); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); Loading Loading @@ -110,18 +116,19 @@ static int apply_constraint(struct dev_pm_qos_request *req, * @dev: device to allocate data for * @dev: device to allocate data for * * * Called at the first call to add_request, for constraint data allocation * Called at the first call to add_request, for constraint data allocation * Must be called with the dev_pm_qos_mtx mutex held * Must be called with the dev_pm_qos_lock lock held */ */ static int dev_pm_qos_constraints_allocate(struct device *dev) static int dev_pm_qos_constraints_allocate(struct device *dev) { { struct pm_qos_constraints *c; struct pm_qos_constraints *c; struct blocking_notifier_head *n; struct blocking_notifier_head *n; unsigned long flags; c = kzalloc(sizeof(*c), GFP_KERNEL); c = kzalloc(sizeof(*c), GFP_ATOMIC); if (!c) if (!c) return -ENOMEM; return -ENOMEM; n = kzalloc(sizeof(*n), GFP_KERNEL); n = kzalloc(sizeof(*n), GFP_ATOMIC); if (!n) { if (!n) { kfree(c); kfree(c); return -ENOMEM; return -ENOMEM; Loading @@ -134,9 +141,9 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->type = PM_QOS_MIN; c->type = PM_QOS_MIN; c->notifiers = n; c->notifiers = n; spin_lock_irq(&dev->power.lock); spin_lock_irqsave(&dev->power.lock, flags); dev->power.constraints = c; dev->power.constraints = c; spin_unlock_irq(&dev->power.lock); spin_unlock_irqrestore(&dev->power.lock, flags); return 0; return 0; } } Loading @@ -150,10 +157,12 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) */ */ void dev_pm_qos_constraints_init(struct device *dev) void dev_pm_qos_constraints_init(struct device *dev) { { mutex_lock(&dev_pm_qos_mtx); unsigned long flags; spin_lock_irqsave(&dev_pm_qos_lock, flags); dev->power.constraints = NULL; dev->power.constraints = NULL; dev->power.power_state = PMSG_ON; dev->power.power_state = PMSG_ON; mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); } } /** /** Loading @@ -166,6 +175,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) { { struct dev_pm_qos_request *req, *tmp; struct dev_pm_qos_request *req, *tmp; struct pm_qos_constraints *c; struct pm_qos_constraints *c; unsigned long flags; /* /* * If the device's PM QoS resume latency limit has been exposed to user * If the device's PM QoS resume latency limit has been exposed to user Loading @@ -173,7 +183,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) */ */ dev_pm_qos_hide_latency_limit(dev); dev_pm_qos_hide_latency_limit(dev); mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); dev->power.power_state = PMSG_INVALID; dev->power.power_state = PMSG_INVALID; c = dev->power.constraints; c = dev->power.constraints; Loading @@ -198,7 +208,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) kfree(c); kfree(c); out: out: mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); } } /** /** Loading @@ -223,6 +233,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, s32 value) s32 value) { { int ret = 0; int ret = 0; unsigned long flags; if (!dev || !req) /*guard against callers passing in null */ if (!dev || !req) /*guard against callers passing in null */ return -EINVAL; return -EINVAL; Loading @@ -233,7 +244,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, req->dev = dev; req->dev = dev; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (!dev->power.constraints) { if (!dev->power.constraints) { if (dev->power.power_state.event == PM_EVENT_INVALID) { if (dev->power.power_state.event == PM_EVENT_INVALID) { Loading @@ -255,7 +266,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, ret = apply_constraint(req, PM_QOS_ADD_REQ, value); ret = apply_constraint(req, PM_QOS_ADD_REQ, value); out: out: mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } Loading @@ -280,6 +291,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) s32 new_value) { { int ret = 0; int ret = 0; unsigned long flags; if (!req) /*guard against callers passing in null */ if (!req) /*guard against callers passing in null */ return -EINVAL; return -EINVAL; Loading @@ -288,7 +300,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, "%s() called for unknown object\n", __func__)) "%s() called for unknown object\n", __func__)) return -EINVAL; return -EINVAL; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (req->dev->power.constraints) { if (req->dev->power.constraints) { if (new_value != req->node.prio) if (new_value != req->node.prio) Loading @@ -299,7 +311,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, ret = -ENODEV; ret = -ENODEV; } } mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); Loading @@ -319,6 +331,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) { { int ret = 0; int ret = 0; unsigned long flags; if (!req) /*guard against callers passing in null */ if (!req) /*guard against callers passing in null */ return -EINVAL; return -EINVAL; Loading @@ -327,7 +340,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) "%s() called for unknown object\n", __func__)) "%s() called for unknown object\n", __func__)) return -EINVAL; return -EINVAL; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (req->dev->power.constraints) { if (req->dev->power.constraints) { ret = apply_constraint(req, PM_QOS_REMOVE_REQ, ret = apply_constraint(req, PM_QOS_REMOVE_REQ, Loading @@ -338,7 +351,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req) ret = -ENODEV; ret = -ENODEV; } } mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); Loading @@ -359,8 +372,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request); int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) { { int ret = 0; int ret = 0; unsigned long flags; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); if (!dev->power.constraints) if (!dev->power.constraints) ret = dev->power.power_state.event != PM_EVENT_INVALID ? ret = dev->power.power_state.event != PM_EVENT_INVALID ? Loading @@ -370,7 +384,7 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier) ret = blocking_notifier_chain_register( ret = blocking_notifier_chain_register( dev->power.constraints->notifiers, notifier); dev->power.constraints->notifiers, notifier); mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return ret; return ret; } } EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier); Loading @@ -389,8 +403,9 @@ int dev_pm_qos_remove_notifier(struct device *dev, struct notifier_block *notifier) struct notifier_block *notifier) { { int retval = 0; int retval = 0; unsigned long flags; mutex_lock(&dev_pm_qos_mtx); spin_lock_irqsave(&dev_pm_qos_lock, flags); /* Silently return if the constraints object is not present. */ /* Silently return if the constraints object is not present. */ if (dev->power.constraints) if (dev->power.constraints) Loading @@ -398,7 +413,7 @@ int dev_pm_qos_remove_notifier(struct device *dev, dev->power.constraints->notifiers, dev->power.constraints->notifiers, notifier); notifier); mutex_unlock(&dev_pm_qos_mtx); spin_unlock_irqrestore(&dev_pm_qos_lock, flags); return retval; return retval; } } EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier); Loading