Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / QoS: Add function dev_pm_qos_read_value() (v3)

To read the current PM QoS value for a given device we need to
make sure that the device's power.constraints object won't be
removed while we're doing that. For this reason, put the
operation under dev->power.lock and acquire the lock
around the initialization and removal of power.constraints.

Moreover, since we're using the value of power.constraints to
determine whether or not the object is present, the
power.constraints_state field isn't necessary any more and may be
removed. However, dev_pm_qos_add_request() needs to check if the
device is being removed from the system before allocating a new
PM QoS constraints object for it, so make it use the
power.power_state field of struct device for this purpose.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

+114 -84
+3 -3
drivers/base/power/main.c
··· 22 22 #include <linux/mutex.h> 23 23 #include <linux/pm.h> 24 24 #include <linux/pm_runtime.h> 25 - #include <linux/pm_qos.h> 26 25 #include <linux/resume-trace.h> 27 26 #include <linux/interrupt.h> 28 27 #include <linux/sched.h> ··· 65 66 spin_lock_init(&dev->power.lock); 66 67 pm_runtime_init(dev); 67 68 INIT_LIST_HEAD(&dev->power.entry); 69 + dev->power.power_state = PMSG_INVALID; 68 70 } 69 71 70 72 /** ··· 97 97 dev_warn(dev, "parent %s should not be sleeping\n", 98 98 dev_name(dev->parent)); 99 99 list_add_tail(&dev->power.entry, &dpm_list); 100 - mutex_unlock(&dpm_list_mtx); 101 100 dev_pm_qos_constraints_init(dev); 101 + mutex_unlock(&dpm_list_mtx); 102 102 } 103 103 104 104 /** ··· 109 109 { 110 110 pr_debug("PM: Removing info for %s:%s\n", 111 111 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 112 - dev_pm_qos_constraints_destroy(dev); 113 112 complete_all(&dev->power.completion); 114 113 mutex_lock(&dpm_list_mtx); 114 + dev_pm_qos_constraints_destroy(dev); 115 115 list_del_init(&dev->power.entry); 116 116 mutex_unlock(&dpm_list_mtx); 117 117 device_wakeup_disable(dev);
+9 -1
drivers/base/power/power.h
··· 1 + #include <linux/pm_qos.h> 2 + 1 3 #ifdef CONFIG_PM_RUNTIME 2 4 3 5 extern void pm_runtime_init(struct device *dev); ··· 37 35 static inline void device_pm_init(struct device *dev) 38 36 { 39 37 spin_lock_init(&dev->power.lock); 38 + dev->power.power_state = PMSG_INVALID; 40 39 pm_runtime_init(dev); 40 + } 41 + 42 + static inline void device_pm_add(struct device *dev) 43 + { 44 + dev_pm_qos_constraints_init(dev); 41 45 } 42 46 43 47 static inline void device_pm_remove(struct device *dev) 44 48 { 49 + dev_pm_qos_constraints_destroy(dev); 45 50 pm_runtime_remove(dev); 46 51 } 47 52 48 - static inline void device_pm_add(struct device *dev) {} 49 53 static inline void device_pm_move_before(struct device *deva, 50 54 struct device *devb) {} 51 55 static inline void device_pm_move_after(struct device *deva,
+90 -70
drivers/base/power/qos.c
··· 30 30 * . To minimize the data usage by the per-device constraints, the data struct 31 31 * is only allocated at the first call to dev_pm_qos_add_request. 32 32 * . The data is later free'd when the device is removed from the system. 33 - * . The constraints_state variable from dev_pm_info tracks the data struct 34 - * allocation state: 35 - * DEV_PM_QOS_NO_DEVICE: No device present or device removed, no data 36 - * allocated, 37 - * DEV_PM_QOS_DEVICE_PRESENT: Device present, data not allocated and will be 38 - * allocated at the first call to dev_pm_qos_add_request, 39 - * DEV_PM_QOS_ALLOCATED: Device present, data allocated. The per-device 40 - * PM QoS constraints framework is operational and constraints can be 41 - * added, updated or removed using the dev_pm_qos_* API. 42 33 * . A global mutex protects the constraints users from the data being 43 34 * allocated and free'd. 44 35 */ ··· 42 51 43 52 44 53 static DEFINE_MUTEX(dev_pm_qos_mtx); 54 + 45 55 static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); 56 + 57 + /** 58 + * dev_pm_qos_read_value - Get PM QoS constraint for a given device. 59 + * @dev: Device to get the PM QoS constraint value for. 60 + */ 61 + s32 dev_pm_qos_read_value(struct device *dev) 62 + { 63 + struct pm_qos_constraints *c; 64 + unsigned long flags; 65 + s32 ret = 0; 66 + 67 + spin_lock_irqsave(&dev->power.lock, flags); 68 + 69 + c = dev->power.constraints; 70 + if (c) 71 + ret = pm_qos_read_value(c); 72 + 73 + spin_unlock_irqrestore(&dev->power.lock, flags); 74 + 75 + return ret; 76 + } 46 77 47 78 /* 48 79 * apply_constraint ··· 118 105 } 119 106 BLOCKING_INIT_NOTIFIER_HEAD(n); 120 107 108 + plist_head_init(&c->list); 109 + c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; 110 + c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; 111 + c->type = PM_QOS_MIN; 112 + c->notifiers = n; 113 + 114 + spin_lock_irq(&dev->power.lock); 121 115 dev->power.constraints = c; 122 - plist_head_init(&dev->power.constraints->list); 123 - dev->power.constraints->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; 124 - dev->power.constraints->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE; 125 - dev->power.constraints->type = PM_QOS_MIN; 126 - dev->power.constraints->notifiers = n; 127 - dev->power.constraints_state = DEV_PM_QOS_ALLOCATED; 116 + spin_unlock_irq(&dev->power.lock); 128 117 129 118 return 0; 130 119 } 131 120 132 121 /** 133 - * dev_pm_qos_constraints_init 122 + * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer. 134 123 * @dev: target device 135 124 * 136 - * Called from the device PM subsystem at device insertion 125 + * Called from the device PM subsystem during device insertion under 126 + * device_pm_lock(). 137 127 */ 138 128 void dev_pm_qos_constraints_init(struct device *dev) 139 129 { 140 130 mutex_lock(&dev_pm_qos_mtx); 141 - dev->power.constraints_state = DEV_PM_QOS_DEVICE_PRESENT; 131 + dev->power.constraints = NULL; 132 + dev->power.power_state = PMSG_ON; 142 133 mutex_unlock(&dev_pm_qos_mtx); 143 134 } 144 135 ··· 150 133 * dev_pm_qos_constraints_destroy 151 134 * @dev: target device 152 135 * 153 - * Called from the device PM subsystem at device removal 136 + * Called from the device PM subsystem on device removal under device_pm_lock(). 154 137 */ 155 138 void dev_pm_qos_constraints_destroy(struct device *dev) 156 139 { 157 140 struct dev_pm_qos_request *req, *tmp; 141 + struct pm_qos_constraints *c; 158 142 159 143 mutex_lock(&dev_pm_qos_mtx); 160 144 161 - if (dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { 162 - /* Flush the constraints list for the device */ 163 - plist_for_each_entry_safe(req, tmp, 164 - &dev->power.constraints->list, 165 - node) { 166 - /* 167 - * Update constraints list and call the notification 168 - * callbacks if needed 169 - */ 170 - apply_constraint(req, PM_QOS_REMOVE_REQ, 171 - PM_QOS_DEFAULT_VALUE); 172 - memset(req, 0, sizeof(*req)); 173 - } 145 + dev->power.power_state = PMSG_INVALID; 146 + c = dev->power.constraints; 147 + if (!c) 148 + goto out; 174 149 175 - kfree(dev->power.constraints->notifiers); 176 - kfree(dev->power.constraints); 177 - dev->power.constraints = NULL; 150 + /* Flush the constraints list for the device */ 151 + plist_for_each_entry_safe(req, tmp, &c->list, node) { 152 + /* 153 + * Update constraints list and call the notification 154 + * callbacks if needed 155 + */ 156 + apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); 157 + memset(req, 0, sizeof(*req)); 178 158 } 179 - dev->power.constraints_state = DEV_PM_QOS_NO_DEVICE; 180 159 160 + spin_lock_irq(&dev->power.lock); 161 + dev->power.constraints = NULL; 162 + spin_unlock_irq(&dev->power.lock); 163 + 164 + kfree(c->notifiers); 165 + kfree(c); 166 + 167 + out: 181 168 mutex_unlock(&dev_pm_qos_mtx); 182 169 } 183 170 ··· 199 178 * 200 179 * Returns 1 if the aggregated constraint value has changed, 201 180 * 0 if the aggregated constraint value has not changed, 202 - * -EINVAL in case of wrong parameters, -ENODEV if the device has been 203 - * removed from the system 181 + * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory 182 + * to allocate for data structures, -ENODEV if the device has just been removed 183 + * from the system. 204 184 */ 205 185 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, 206 186 s32 value) ··· 217 195 return -EINVAL; 218 196 } 219 197 220 - mutex_lock(&dev_pm_qos_mtx); 221 198 req->dev = dev; 222 199 223 - /* Return if the device has been removed */ 224 - if (req->dev->power.constraints_state == DEV_PM_QOS_NO_DEVICE) { 225 - ret = -ENODEV; 226 - goto out; 227 - } 200 + mutex_lock(&dev_pm_qos_mtx); 228 201 229 - /* 230 - * Allocate the constraints data on the first call to add_request, 231 - * i.e. only if the data is not already allocated and if the device has 232 - * not been removed 233 - */ 234 - if (dev->power.constraints_state == DEV_PM_QOS_DEVICE_PRESENT) 235 - ret = dev_pm_qos_constraints_allocate(dev); 202 + if (!dev->power.constraints) { 203 + if (dev->power.power_state.event == PM_EVENT_INVALID) { 204 + /* The device has been removed from the system. */ 205 + req->dev = NULL; 206 + ret = -ENODEV; 207 + goto out; 208 + } else { 209 + /* 210 + * Allocate the constraints data on the first call to 211 + * add_request, i.e. only if the data is not already 212 + * allocated and if the device has not been removed. 213 + */ 214 + ret = dev_pm_qos_constraints_allocate(dev); 215 + } 216 + } 236 217 237 218 if (!ret) 238 219 ret = apply_constraint(req, PM_QOS_ADD_REQ, value); 239 220 240 - out: 221 + out: 241 222 mutex_unlock(&dev_pm_qos_mtx); 223 + 242 224 return ret; 243 225 } 244 226 EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); ··· 278 252 279 253 mutex_lock(&dev_pm_qos_mtx); 280 254 281 - if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { 255 + if (req->dev->power.constraints) { 282 256 if (new_value != req->node.prio) 283 257 ret = apply_constraint(req, PM_QOS_UPDATE_REQ, 284 258 new_value); ··· 319 293 320 294 mutex_lock(&dev_pm_qos_mtx); 321 295 322 - if (req->dev->power.constraints_state == DEV_PM_QOS_ALLOCATED) { 296 + if (req->dev->power.constraints) { 323 297 ret = apply_constraint(req, PM_QOS_REMOVE_REQ, 324 298 PM_QOS_DEFAULT_VALUE); 325 299 memset(req, 0, sizeof(*req)); ··· 349 323 350 324 mutex_lock(&dev_pm_qos_mtx); 351 325 352 - /* Silently return if the device has been removed */ 353 - if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED) 354 - goto out; 326 + /* Silently return if the constraints object is not present. */ 327 + if (dev->power.constraints) 328 + retval = blocking_notifier_chain_register( 329 + dev->power.constraints->notifiers, 330 + notifier); 355 331 356 - retval = blocking_notifier_chain_register( 357 - dev->power.constraints->notifiers, 358 - notifier); 359 - 360 - out: 361 332 mutex_unlock(&dev_pm_qos_mtx); 362 333 return retval; 363 334 } ··· 377 354 378 355 mutex_lock(&dev_pm_qos_mtx); 379 356 380 - /* Silently return if the device has been removed */ 381 - if (dev->power.constraints_state != DEV_PM_QOS_ALLOCATED) 382 - goto out; 357 + /* Silently return if the constraints object is not present. */ 358 + if (dev->power.constraints) 359 + retval = blocking_notifier_chain_unregister( 360 + dev->power.constraints->notifiers, 361 + notifier); 383 362 384 - retval = blocking_notifier_chain_unregister( 385 - dev->power.constraints->notifiers, 386 - notifier); 387 - 388 - out: 389 363 mutex_unlock(&dev_pm_qos_mtx); 390 364 return retval; 391 365 }
+2 -8
include/linux/pm.h
··· 326 326 * requested by a driver. 327 327 */ 328 328 329 + #define PM_EVENT_INVALID (-1) 329 330 #define PM_EVENT_ON 0x0000 330 331 #define PM_EVENT_FREEZE 0x0001 331 332 #define PM_EVENT_SUSPEND 0x0002 ··· 347 346 #define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND) 348 347 #define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) 349 348 349 + #define PMSG_INVALID ((struct pm_message){ .event = PM_EVENT_INVALID, }) 350 350 #define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) 351 351 #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) 352 352 #define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) ··· 421 419 RPM_REQ_RESUME, 422 420 }; 423 421 424 - /* Per-device PM QoS constraints data struct state */ 425 - enum dev_pm_qos_state { 426 - DEV_PM_QOS_NO_DEVICE, /* No device present */ 427 - DEV_PM_QOS_DEVICE_PRESENT, /* Device present, data not allocated */ 428 - DEV_PM_QOS_ALLOCATED, /* Device present, data allocated */ 429 - }; 430 - 431 422 struct wakeup_source; 432 423 433 424 struct pm_domain_data { ··· 483 488 #endif 484 489 struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ 485 490 struct pm_qos_constraints *constraints; 486 - enum dev_pm_qos_state constraints_state; 487 491 }; 488 492 489 493 extern void update_pm_runtime_accounting(struct device *dev);
+10 -2
include/linux/pm_qos.h
··· 7 7 #include <linux/plist.h> 8 8 #include <linux/notifier.h> 9 9 #include <linux/miscdevice.h> 10 + #include <linux/device.h> 10 11 11 12 #define PM_QOS_RESERVED 0 12 13 #define PM_QOS_CPU_DMA_LATENCY 1 ··· 78 77 int pm_qos_request_active(struct pm_qos_request *req); 79 78 s32 pm_qos_read_value(struct pm_qos_constraints *c); 80 79 80 + s32 dev_pm_qos_read_value(struct device *dev); 81 81 int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, 82 82 s32 value); 83 83 int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value); ··· 119 117 static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) 120 118 { return 0; } 121 119 120 + static inline s32 dev_pm_qos_read_value(struct device *dev) 121 + { return 0; } 122 122 static inline int dev_pm_qos_add_request(struct device *dev, 123 123 struct dev_pm_qos_request *req, 124 124 s32 value) ··· 143 139 struct notifier_block *notifier) 144 140 { return 0; } 145 141 static inline void dev_pm_qos_constraints_init(struct device *dev) 146 - { return; } 142 + { 143 + dev->power.power_state = PMSG_ON; 144 + } 147 145 static inline void dev_pm_qos_constraints_destroy(struct device *dev) 148 - { return; } 146 + { 147 + dev->power.power_state = PMSG_INVALID; 148 + } 149 149 #endif 150 150 151 151 #endif