aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-06-17 19:16:32 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-06-17 20:04:39 -0400
commite3d105055525d9ea9f8e9cb0db8237df3df1bb9f (patch)
tree37448f1ac032726d1e290124734a6a7a067d095d /drivers/usb/core
parent6c79fe4afcb0450bd638f6e959e512aad270ff2f (diff)
usb: fix hub-port pm_runtime_enable() vs runtime pm transitions
Commit 9262c19d14c4 "usb: disable port power control if not supported in wHubCharacteristics" gated enabling runtime pm for usb_port devices on whether the parent hub supports power control, which causes a regression. The port must still be allowed to carry out runtime pm callbacks and receive a -EAGAIN or -EBUSY result. Otherwise the usb_port device will transition to the pm error state and trigger the same for the child usb_device. Prior to the offending commit usb_hub_create_port_device() arranged for runtime pm to be disabled is dev_pm_qos_expose_flags() failed. Instead, force the default state of PM_QOS_FLAG_NO_POWER_OFF flag to be set prior to enabling runtime pm. If that policy can not be set then fail registration. Report: http://marc.info/?l=linux-usb&m=140290586301336&w=2 Fixes: 9262c19d14c4 ("usb: disable port power control if not supported in wHubCharacteristics") Reported-by: Bjørn Mork <bjorn@mork.no> Reported-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c6
-rw-r--r--drivers/usb/core/hub.h2
-rw-r--r--drivers/usb/core/port.c65
3 files changed, 54 insertions, 19 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 78c3cd20d7ae..21b99b4b4082 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1577,6 +1577,12 @@ static int hub_configure(struct usb_hub *hub,
1577 } 1577 }
1578 } 1578 }
1579 hdev->maxchild = i; 1579 hdev->maxchild = i;
1580 for (i = 0; i < hdev->maxchild; i++) {
1581 struct usb_port *port_dev = hub->ports[i];
1582
1583 pm_runtime_put(&port_dev->dev);
1584 }
1585
1580 mutex_unlock(&usb_port_peer_mutex); 1586 mutex_unlock(&usb_port_peer_mutex);
1581 if (ret < 0) 1587 if (ret < 0)
1582 goto fail; 1588 goto fail;
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 0a7cdc0ef0a9..326308e53961 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -84,6 +84,7 @@ struct usb_hub {
84 * @dev: generic device interface 84 * @dev: generic device interface
85 * @port_owner: port's owner 85 * @port_owner: port's owner
86 * @peer: related usb2 and usb3 ports (share the same connector) 86 * @peer: related usb2 and usb3 ports (share the same connector)
87 * @req: default pm qos request for hubs without port power control
87 * @connect_type: port's connect type 88 * @connect_type: port's connect type
88 * @location: opaque representation of platform connector location 89 * @location: opaque representation of platform connector location
89 * @status_lock: synchronize port_event() vs usb_port_{suspend|resume} 90 * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
@@ -95,6 +96,7 @@ struct usb_port {
95 struct device dev; 96 struct device dev;
96 struct usb_dev_state *port_owner; 97 struct usb_dev_state *port_owner;
97 struct usb_port *peer; 98 struct usb_port *peer;
99 struct dev_pm_qos_request *req;
98 enum usb_port_connect_type connect_type; 100 enum usb_port_connect_type connect_type;
99 usb_port_location_t location; 101 usb_port_location_t location;
100 struct mutex status_lock; 102 struct mutex status_lock;
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 9347ade7d5fe..fe1b6d0967e3 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -68,6 +68,7 @@ static void usb_port_device_release(struct device *dev)
68{ 68{
69 struct usb_port *port_dev = to_usb_port(dev); 69 struct usb_port *port_dev = to_usb_port(dev);
70 70
71 kfree(port_dev->req);
71 kfree(port_dev); 72 kfree(port_dev);
72} 73}
73 74
@@ -400,9 +401,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
400 int retval; 401 int retval;
401 402
402 port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); 403 port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
403 if (!port_dev) { 404 if (!port_dev)
404 retval = -ENOMEM; 405 return -ENOMEM;
405 goto exit; 406
407 port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
408 if (!port_dev->req) {
409 kfree(port_dev);
410 return -ENOMEM;
406 } 411 }
407 412
408 hub->ports[port1 - 1] = port_dev; 413 hub->ports[port1 - 1] = port_dev;
@@ -418,31 +423,53 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
418 port1); 423 port1);
419 mutex_init(&port_dev->status_lock); 424 mutex_init(&port_dev->status_lock);
420 retval = device_register(&port_dev->dev); 425 retval = device_register(&port_dev->dev);
421 if (retval) 426 if (retval) {
422 goto error_register; 427 put_device(&port_dev->dev);
428 return retval;
429 }
430
431 /* Set default policy of port-poweroff disabled. */
432 retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req,
433 DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF);
434 if (retval < 0) {
435 device_unregister(&port_dev->dev);
436 return retval;
437 }
423 438
424 find_and_link_peer(hub, port1); 439 find_and_link_peer(hub, port1);
425 440
441 /*
442 * Enable runtime pm and hold a refernce that hub_configure()
443 * will drop once the PM_QOS_NO_POWER_OFF flag state has been set
444 * and the hub has been fully registered (hdev->maxchild set).
445 */
426 pm_runtime_set_active(&port_dev->dev); 446 pm_runtime_set_active(&port_dev->dev);
447 pm_runtime_get_noresume(&port_dev->dev);
448 pm_runtime_enable(&port_dev->dev);
449 device_enable_async_suspend(&port_dev->dev);
427 450
428 /* 451 /*
429 * Do not enable port runtime pm if the hub does not support 452 * Keep hidden the ability to enable port-poweroff if the hub
430 * power switching. Also, userspace must have final say of 453 * does not support power switching.
431 * whether a port is permitted to power-off. Do not enable
432 * runtime pm if we fail to expose pm_qos_no_power_off.
433 */ 454 */
434 if (hub_is_port_power_switchable(hub) 455 if (!hub_is_port_power_switchable(hub))
435 && dev_pm_qos_expose_flags(&port_dev->dev, 456 return 0;
436 PM_QOS_FLAG_NO_POWER_OFF) == 0)
437 pm_runtime_enable(&port_dev->dev);
438 457
439 device_enable_async_suspend(&port_dev->dev); 458 /* Attempt to let userspace take over the policy. */
440 return 0; 459 retval = dev_pm_qos_expose_flags(&port_dev->dev,
460 PM_QOS_FLAG_NO_POWER_OFF);
461 if (retval < 0) {
462 dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n");
463 return 0;
464 }
441 465
442error_register: 466 /* Userspace owns the policy, drop the kernel 'no_poweroff' request. */
443 put_device(&port_dev->dev); 467 retval = dev_pm_qos_remove_request(port_dev->req);
444exit: 468 if (retval >= 0) {
445 return retval; 469 kfree(port_dev->req);
470 port_dev->req = NULL;
471 }
472 return 0;
446} 473}
447 474
448void usb_hub_remove_port_device(struct usb_hub *hub, int port1) 475void usb_hub_remove_port_device(struct usb_hub *hub, int port1)