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

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>

authored by

Dan Williams and committed by
Greg Kroah-Hartman
e3d10505 6c79fe4a

+54 -19
+6
drivers/usb/core/hub.c
··· 1577 1577 } 1578 1578 } 1579 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 1586 mutex_unlock(&usb_port_peer_mutex); 1581 1587 if (ret < 0) 1582 1588 goto fail;
+2
drivers/usb/core/hub.h
··· 84 84 * @dev: generic device interface 85 85 * @port_owner: port's owner 86 86 * @peer: related usb2 and usb3 ports (share the same connector) 87 + * @req: default pm qos request for hubs without port power control 87 88 * @connect_type: port's connect type 88 89 * @location: opaque representation of platform connector location 89 90 * @status_lock: synchronize port_event() vs usb_port_{suspend|resume} ··· 96 95 struct device dev; 97 96 struct usb_dev_state *port_owner; 98 97 struct usb_port *peer; 98 + struct dev_pm_qos_request *req; 99 99 enum usb_port_connect_type connect_type; 100 100 usb_port_location_t location; 101 101 struct mutex status_lock;
+46 -19
drivers/usb/core/port.c
··· 68 68 { 69 69 struct usb_port *port_dev = to_usb_port(dev); 70 70 71 + kfree(port_dev->req); 71 72 kfree(port_dev); 72 73 } 73 74 ··· 401 400 int retval; 402 401 403 402 port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); 404 - if (!port_dev) { 405 - retval = -ENOMEM; 406 - goto exit; 403 + if (!port_dev) 404 + return -ENOMEM; 405 + 406 + port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL); 407 + if (!port_dev->req) { 408 + kfree(port_dev); 409 + return -ENOMEM; 407 410 } 408 411 409 412 hub->ports[port1 - 1] = port_dev; ··· 423 418 port1); 424 419 mutex_init(&port_dev->status_lock); 425 420 retval = device_register(&port_dev->dev); 426 - if (retval) 427 - goto error_register; 421 + if (retval) { 422 + put_device(&port_dev->dev); 423 + return retval; 424 + } 425 + 426 + /* Set default policy of port-poweroff disabled. */ 427 + retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req, 428 + DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF); 429 + if (retval < 0) { 430 + device_unregister(&port_dev->dev); 431 + return retval; 432 + } 428 433 429 434 find_and_link_peer(hub, port1); 430 435 436 + /* 437 + * Enable runtime pm and hold a refernce that hub_configure() 438 + * will drop once the PM_QOS_NO_POWER_OFF flag state has been set 439 + * and the hub has been fully registered (hdev->maxchild set). 440 + */ 431 441 pm_runtime_set_active(&port_dev->dev); 442 + pm_runtime_get_noresume(&port_dev->dev); 443 + pm_runtime_enable(&port_dev->dev); 444 + device_enable_async_suspend(&port_dev->dev); 432 445 433 446 /* 434 - * Do not enable port runtime pm if the hub does not support 435 - * power switching. Also, userspace must have final say of 436 - * whether a port is permitted to power-off. Do not enable 437 - * runtime pm if we fail to expose pm_qos_no_power_off. 447 + * Keep hidden the ability to enable port-poweroff if the hub 448 + * does not support power switching. 438 449 */ 439 - if (hub_is_port_power_switchable(hub) 440 - && dev_pm_qos_expose_flags(&port_dev->dev, 441 - PM_QOS_FLAG_NO_POWER_OFF) == 0) 442 - pm_runtime_enable(&port_dev->dev); 450 + if (!hub_is_port_power_switchable(hub)) 451 + return 0; 443 452 444 - device_enable_async_suspend(&port_dev->dev); 453 + /* Attempt to let userspace take over the policy. */ 454 + retval = dev_pm_qos_expose_flags(&port_dev->dev, 455 + PM_QOS_FLAG_NO_POWER_OFF); 456 + if (retval < 0) { 457 + dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n"); 458 + return 0; 459 + } 460 + 461 + /* Userspace owns the policy, drop the kernel 'no_poweroff' request. */ 462 + retval = dev_pm_qos_remove_request(port_dev->req); 463 + if (retval >= 0) { 464 + kfree(port_dev->req); 465 + port_dev->req = NULL; 466 + } 445 467 return 0; 446 - 447 - error_register: 448 - put_device(&port_dev->dev); 449 - exit: 450 - return retval; 451 468 } 452 469 453 470 void usb_hub_remove_port_device(struct usb_hub *hub, int port1)