Commit 564fbee9 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'usb-3.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB fixes from Greg KH:
 "Here are some USB fixes for 3.16-rc2 that resolve some reported
  issues.  All of these have been in linux-next for a while with no
  problems"

* tag 'usb-3.16-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
  USB: usbtest: add a timeout for scatter-gather tests
  USB: EHCI: avoid BIOS handover on the HASEE E200
  usb: fix hub-port pm_runtime_enable() vs runtime pm transitions
  usb: quiet peer failure warning, disable poweroff
  usb: improve "not suspended yet" message in hub_suspend()
  xhci: Fix sleeping with IRQs disabled in xhci_stop_device()
  usb: fix ->update_hub_device() vs hdev->maxchild
parents 3c8fb504 32b36eea
...@@ -1526,18 +1526,6 @@ static int hub_configure(struct usb_hub *hub, ...@@ -1526,18 +1526,6 @@ static int hub_configure(struct usb_hub *hub,
dev_dbg(hub_dev, "%umA bus power budget for each child\n", dev_dbg(hub_dev, "%umA bus power budget for each child\n",
hub->mA_per_port); hub->mA_per_port);
/* Update the HCD's internal representation of this hub before khubd
* starts getting port status changes for devices under the hub.
*/
if (hcd->driver->update_hub_device) {
ret = hcd->driver->update_hub_device(hcd, hdev,
&hub->tt, GFP_KERNEL);
if (ret < 0) {
message = "can't update HCD hub info";
goto fail;
}
}
ret = hub_hub_status(hub, &hubstatus, &hubchange); ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) { if (ret < 0) {
message = "can't get hub status"; message = "can't get hub status";
...@@ -1589,10 +1577,28 @@ static int hub_configure(struct usb_hub *hub, ...@@ -1589,10 +1577,28 @@ static int hub_configure(struct usb_hub *hub,
} }
} }
hdev->maxchild = i; hdev->maxchild = i;
for (i = 0; i < hdev->maxchild; i++) {
struct usb_port *port_dev = hub->ports[i];
pm_runtime_put(&port_dev->dev);
}
mutex_unlock(&usb_port_peer_mutex); mutex_unlock(&usb_port_peer_mutex);
if (ret < 0) if (ret < 0)
goto fail; goto fail;
/* Update the HCD's internal representation of this hub before khubd
* starts getting port status changes for devices under the hub.
*/
if (hcd->driver->update_hub_device) {
ret = hcd->driver->update_hub_device(hcd, hdev,
&hub->tt, GFP_KERNEL);
if (ret < 0) {
message = "can't update HCD hub info";
goto fail;
}
}
usb_hub_adjust_deviceremovable(hdev, hub->descriptor); usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
hub_activate(hub, HUB_INIT); hub_activate(hub, HUB_INIT);
...@@ -3458,7 +3464,8 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) ...@@ -3458,7 +3464,8 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_device *udev = port_dev->child; struct usb_device *udev = port_dev->child;
if (udev && udev->can_submit) { if (udev && udev->can_submit) {
dev_warn(&port_dev->dev, "not suspended yet\n"); dev_warn(&port_dev->dev, "device %s not suspended yet\n",
dev_name(&udev->dev));
if (PMSG_IS_AUTO(msg)) if (PMSG_IS_AUTO(msg))
return -EBUSY; return -EBUSY;
} }
......
...@@ -84,6 +84,7 @@ struct usb_hub { ...@@ -84,6 +84,7 @@ struct usb_hub {
* @dev: generic device interface * @dev: generic device interface
* @port_owner: port's owner * @port_owner: port's owner
* @peer: related usb2 and usb3 ports (share the same connector) * @peer: related usb2 and usb3 ports (share the same connector)
* @req: default pm qos request for hubs without port power control
* @connect_type: port's connect type * @connect_type: port's connect type
* @location: opaque representation of platform connector location * @location: opaque representation of platform connector location
* @status_lock: synchronize port_event() vs usb_port_{suspend|resume} * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
...@@ -95,6 +96,7 @@ struct usb_port { ...@@ -95,6 +96,7 @@ struct usb_port {
struct device dev; struct device dev;
struct usb_dev_state *port_owner; struct usb_dev_state *port_owner;
struct usb_port *peer; struct usb_port *peer;
struct dev_pm_qos_request *req;
enum usb_port_connect_type connect_type; enum usb_port_connect_type connect_type;
usb_port_location_t location; usb_port_location_t location;
struct mutex status_lock; struct mutex status_lock;
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include "hub.h" #include "hub.h"
static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[]; static const struct attribute_group *port_dev_group[];
static ssize_t connect_type_show(struct device *dev, static ssize_t connect_type_show(struct device *dev,
...@@ -66,6 +68,7 @@ static void usb_port_device_release(struct device *dev) ...@@ -66,6 +68,7 @@ static void usb_port_device_release(struct device *dev)
{ {
struct usb_port *port_dev = to_usb_port(dev); struct usb_port *port_dev = to_usb_port(dev);
kfree(port_dev->req);
kfree(port_dev); kfree(port_dev);
} }
...@@ -142,6 +145,9 @@ static int usb_port_runtime_suspend(struct device *dev) ...@@ -142,6 +145,9 @@ static int usb_port_runtime_suspend(struct device *dev)
== PM_QOS_FLAGS_ALL) == PM_QOS_FLAGS_ALL)
return -EAGAIN; return -EAGAIN;
if (usb_port_block_power_off)
return -EBUSY;
usb_autopm_get_interface(intf); usb_autopm_get_interface(intf);
retval = usb_hub_set_port_power(hdev, hub, port1, false); retval = usb_hub_set_port_power(hdev, hub, port1, false);
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
...@@ -190,11 +196,19 @@ static int link_peers(struct usb_port *left, struct usb_port *right) ...@@ -190,11 +196,19 @@ static int link_peers(struct usb_port *left, struct usb_port *right)
if (left->peer || right->peer) { if (left->peer || right->peer) {
struct usb_port *lpeer = left->peer; struct usb_port *lpeer = left->peer;
struct usb_port *rpeer = right->peer; struct usb_port *rpeer = right->peer;
char *method;
WARN(1, "failed to peer %s and %s (%s -> %p) (%s -> %p)\n",
dev_name(&left->dev), dev_name(&right->dev), if (left->location && left->location == right->location)
dev_name(&left->dev), lpeer, method = "location";
dev_name(&right->dev), rpeer); else
method = "default";
pr_warn("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n",
dev_name(&left->dev), dev_name(&right->dev), method,
dev_name(&left->dev),
lpeer ? dev_name(&lpeer->dev) : "none",
dev_name(&right->dev),
rpeer ? dev_name(&rpeer->dev) : "none");
return -EBUSY; return -EBUSY;
} }
...@@ -251,6 +265,7 @@ static void link_peers_report(struct usb_port *left, struct usb_port *right) ...@@ -251,6 +265,7 @@ static void link_peers_report(struct usb_port *left, struct usb_port *right)
dev_warn(&left->dev, "failed to peer to %s (%d)\n", dev_warn(&left->dev, "failed to peer to %s (%d)\n",
dev_name(&right->dev), rc); dev_name(&right->dev), rc);
pr_warn_once("usb: port power management may be unreliable\n"); pr_warn_once("usb: port power management may be unreliable\n");
usb_port_block_power_off = 1;
} }
} }
...@@ -386,9 +401,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) ...@@ -386,9 +401,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
int retval; int retval;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
if (!port_dev) { if (!port_dev)
retval = -ENOMEM; return -ENOMEM;
goto exit;
port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
if (!port_dev->req) {
kfree(port_dev);
return -ENOMEM;
} }
hub->ports[port1 - 1] = port_dev; hub->ports[port1 - 1] = port_dev;
...@@ -404,31 +423,53 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) ...@@ -404,31 +423,53 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
port1); port1);
mutex_init(&port_dev->status_lock); mutex_init(&port_dev->status_lock);
retval = device_register(&port_dev->dev); retval = device_register(&port_dev->dev);
if (retval) if (retval) {
goto error_register; put_device(&port_dev->dev);
return retval;
}
/* Set default policy of port-poweroff disabled. */
retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req,
DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF);
if (retval < 0) {
device_unregister(&port_dev->dev);
return retval;
}
find_and_link_peer(hub, port1); find_and_link_peer(hub, port1);
/*
* Enable runtime pm and hold a refernce that hub_configure()
* will drop once the PM_QOS_NO_POWER_OFF flag state has been set
* and the hub has been fully registered (hdev->maxchild set).
*/
pm_runtime_set_active(&port_dev->dev); pm_runtime_set_active(&port_dev->dev);
pm_runtime_get_noresume(&port_dev->dev);
pm_runtime_enable(&port_dev->dev);
device_enable_async_suspend(&port_dev->dev);
/* /*
* Do not enable port runtime pm if the hub does not support * Keep hidden the ability to enable port-poweroff if the hub
* power switching. Also, userspace must have final say of * does not support power switching.
* whether a port is permitted to power-off. Do not enable
* runtime pm if we fail to expose pm_qos_no_power_off.
*/ */
if (hub_is_port_power_switchable(hub) if (!hub_is_port_power_switchable(hub))
&& dev_pm_qos_expose_flags(&port_dev->dev, return 0;
PM_QOS_FLAG_NO_POWER_OFF) == 0)
pm_runtime_enable(&port_dev->dev);
device_enable_async_suspend(&port_dev->dev); /* Attempt to let userspace take over the policy. */
retval = dev_pm_qos_expose_flags(&port_dev->dev,
PM_QOS_FLAG_NO_POWER_OFF);
if (retval < 0) {
dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n");
return 0; return 0;
}
error_register: /* Userspace owns the policy, drop the kernel 'no_poweroff' request. */
put_device(&port_dev->dev); retval = dev_pm_qos_remove_request(port_dev->req);
exit: if (retval >= 0) {
return retval; kfree(port_dev->req);
port_dev->req = NULL;
}
return 0;
} }
void usb_hub_remove_port_device(struct usb_hub *hub, int port1) void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
......
...@@ -656,6 +656,14 @@ static const struct dmi_system_id ehci_dmi_nohandoff_table[] = { ...@@ -656,6 +656,14 @@ static const struct dmi_system_id ehci_dmi_nohandoff_table[] = {
DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"), DMI_MATCH(DMI_BIOS_VERSION, "Lucid-"),
}, },
}, },
{
/* HASEE E200 */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HASEE"),
DMI_MATCH(DMI_BOARD_NAME, "E210"),
DMI_MATCH(DMI_BIOS_VERSION, "6.00"),
},
},
{ } { }
}; };
...@@ -665,9 +673,14 @@ static void ehci_bios_handoff(struct pci_dev *pdev, ...@@ -665,9 +673,14 @@ static void ehci_bios_handoff(struct pci_dev *pdev,
{ {
int try_handoff = 1, tried_handoff = 0; int try_handoff = 1, tried_handoff = 0;
/* The Pegatron Lucid tablet sporadically waits for 98 seconds trying /*
* the handoff on its unused controller. Skip it. */ * The Pegatron Lucid tablet sporadically waits for 98 seconds trying
if (pdev->vendor == 0x8086 && pdev->device == 0x283a) { * the handoff on its unused controller. Skip it.
*
* The HASEE E200 hangs when the semaphore is set (bugzilla #77021).
*/
if (pdev->vendor == 0x8086 && (pdev->device == 0x283a ||
pdev->device == 0x27cc)) {
if (dmi_check_system(ehci_dmi_nohandoff_table)) if (dmi_check_system(ehci_dmi_nohandoff_table))
try_handoff = 0; try_handoff = 0;
} }
......
...@@ -287,7 +287,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) ...@@ -287,7 +287,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) { if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) {
struct xhci_command *command; struct xhci_command *command;
command = xhci_alloc_command(xhci, false, false, command = xhci_alloc_command(xhci, false, false,
GFP_NOIO); GFP_NOWAIT);
if (!command) { if (!command) {
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
xhci_free_command(xhci, cmd); xhci_free_command(xhci, cmd);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/usb.h> #include <linux/usb.h>
#define SIMPLE_IO_TIMEOUT 10000 /* in milliseconds */ #define SIMPLE_IO_TIMEOUT 10000 /* in milliseconds */
...@@ -484,6 +484,14 @@ alloc_sglist(int nents, int max, int vary) ...@@ -484,6 +484,14 @@ alloc_sglist(int nents, int max, int vary)
return sg; return sg;
} }
static void sg_timeout(unsigned long _req)
{
struct usb_sg_request *req = (struct usb_sg_request *) _req;
req->status = -ETIMEDOUT;
usb_sg_cancel(req);
}
static int perform_sglist( static int perform_sglist(
struct usbtest_dev *tdev, struct usbtest_dev *tdev,
unsigned iterations, unsigned iterations,
...@@ -495,6 +503,9 @@ static int perform_sglist( ...@@ -495,6 +503,9 @@ static int perform_sglist(
{ {
struct usb_device *udev = testdev_to_usbdev(tdev); struct usb_device *udev = testdev_to_usbdev(tdev);
int retval = 0; int retval = 0;
struct timer_list sg_timer;
setup_timer_on_stack(&sg_timer, sg_timeout, (unsigned long) req);
while (retval == 0 && iterations-- > 0) { while (retval == 0 && iterations-- > 0) {
retval = usb_sg_init(req, udev, pipe, retval = usb_sg_init(req, udev, pipe,
...@@ -505,7 +516,10 @@ static int perform_sglist( ...@@ -505,7 +516,10 @@ static int perform_sglist(
if (retval) if (retval)
break; break;
mod_timer(&sg_timer, jiffies +
msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
usb_sg_wait(req); usb_sg_wait(req);
del_timer_sync(&sg_timer);
retval = req->status; retval = req->status;
/* FIXME check resulting data pattern */ /* FIXME check resulting data pattern */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment