usb/hcd: Send a uevent signaling that the host controller had died
authorRaul E Rangel <rrangel@chromium.org>
Fri, 19 Apr 2019 15:30:22 +0000 (09:30 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Apr 2019 09:07:09 +0000 (11:07 +0200)
This change will send an OFFLINE event to udev with the ERROR=DEAD
environment variable set when the HC dies.

By notifying user space the appropriate policies can be applied.
i.e.,
 * Collect error logs.
 * Notify the user that USB is no longer functional.
 * Perform a graceful reboot.

Reported-by: kbuild test robot <lkp@intel.com>
Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/usb-uevent [new file with mode: 0644]
drivers/usb/core/hcd.c
include/linux/usb/hcd.h

diff --git a/Documentation/ABI/testing/usb-uevent b/Documentation/ABI/testing/usb-uevent
new file mode 100644 (file)
index 0000000..d35c3ca
--- /dev/null
@@ -0,0 +1,27 @@
+What:          Raise a uevent when a USB Host Controller has died
+Date:          2019-04-17
+KernelVersion: 5.2
+Contact:       linux-usb@vger.kernel.org
+Description:   When the USB Host Controller has entered a state where it is no
+               longer functional a uevent will be raised. The uevent will
+               contain ACTION=offline and ERROR=DEAD.
+
+               Here is an example taken using udevadm monitor -p:
+
+               KERNEL[130.428945] offline  /devices/pci0000:00/0000:00:10.0/usb2 (usb)
+               ACTION=offline
+               BUSNUM=002
+               DEVNAME=/dev/bus/usb/002/001
+               DEVNUM=001
+               DEVPATH=/devices/pci0000:00/0000:00:10.0/usb2
+               DEVTYPE=usb_device
+               DRIVER=usb
+               ERROR=DEAD
+               MAJOR=189
+               MINOR=128
+               PRODUCT=1d6b/2/414
+               SEQNUM=2168
+               SUBSYSTEM=usb
+               TYPE=9/0/1
+
+Users:         chromium-os-dev@chromium.org
index 00655424baf514275c0c3bd28323a01ef3c26dec..94d22551fc1bf1bd9d1c6f99649bd70692c5fd3c 100644 (file)
@@ -2435,6 +2435,19 @@ EXPORT_SYMBOL_GPL(usb_hcd_irq);
 
 /*-------------------------------------------------------------------------*/
 
+/* Workqueue routine for when the root-hub has died. */
+static void hcd_died_work(struct work_struct *work)
+{
+       struct usb_hcd *hcd = container_of(work, struct usb_hcd, died_work);
+       static char *env[] = {
+               "ERROR=DEAD",
+               NULL
+       };
+
+       /* Notify user space that the host controller has died */
+       kobject_uevent_env(&hcd->self.root_hub->dev.kobj, KOBJ_OFFLINE, env);
+}
+
 /**
  * usb_hc_died - report abnormal shutdown of a host controller (bus glue)
  * @hcd: pointer to the HCD representing the controller
@@ -2475,6 +2488,13 @@ void usb_hc_died (struct usb_hcd *hcd)
                        usb_kick_hub_wq(hcd->self.root_hub);
                }
        }
+
+       /* Handle the case where this function gets called with a shared HCD */
+       if (usb_hcd_is_primary_hcd(hcd))
+               schedule_work(&hcd->died_work);
+       else
+               schedule_work(&hcd->primary_hcd->died_work);
+
        spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
        /* Make sure that the other roothub is also deallocated. */
 }
@@ -2542,6 +2562,8 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
        INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
 #endif
 
+       INIT_WORK(&hcd->died_work, hcd_died_work);
+
        hcd->driver = driver;
        hcd->speed = driver->flags & HCD_MASK;
        hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -2895,6 +2917,7 @@ error_create_attr_group:
 #ifdef CONFIG_PM
        cancel_work_sync(&hcd->wakeup_work);
 #endif
+       cancel_work_sync(&hcd->died_work);
        mutex_lock(&usb_bus_idr_lock);
        usb_disconnect(&rhdev);         /* Sets rhdev to NULL */
        mutex_unlock(&usb_bus_idr_lock);
@@ -2955,6 +2978,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 #ifdef CONFIG_PM
        cancel_work_sync(&hcd->wakeup_work);
 #endif
+       cancel_work_sync(&hcd->died_work);
 
        mutex_lock(&usb_bus_idr_lock);
        usb_disconnect(&rhdev);         /* Sets rhdev to NULL */
index 695931b03684d8677642a274864ab282b386a55d..66a24b13e2ab5e2129808b2cfc70bd5e1513b6d7 100644 (file)
@@ -98,6 +98,7 @@ struct usb_hcd {
 #ifdef CONFIG_PM
        struct work_struct      wakeup_work;    /* for remote wakeup */
 #endif
+       struct work_struct      died_work;      /* for when the device dies */
 
        /*
         * hardware info/state