| From 7c3f308a67a5636699815049e1f9d184db069f2a Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Tue, 3 Jan 2017 18:28:43 +0200 |
| Subject: [PATCH] xhci: free xhci virtual devices with leaf nodes first |
| |
| commit ee8665e28e8d90ce69d4abe5a469c14a8707ae0e upstream. |
| |
| the tt_info provided by a HS hub might be in use to by a child device |
| Make sure we free the devices in the correct order. |
| |
| This is needed in special cases such as when xhci controller is |
| reset when resuming from hibernate, and all virt_devices are freed. |
| |
| Also free the virt_devices starting from max slot_id as children |
| more commonly have higher slot_id than parent. |
| |
| CC: <stable@vger.kernel.org> |
| Reported-by: Guenter Roeck <groeck@chromium.org> |
| Tested-by: Guenter Roeck <groeck@chromium.org> |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c |
| index 6afe32381209..b3a5cd86e86c 100644 |
| --- a/drivers/usb/host/xhci-mem.c |
| +++ b/drivers/usb/host/xhci-mem.c |
| @@ -979,6 +979,40 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) |
| xhci->devs[slot_id] = NULL; |
| } |
| |
| +/* |
| + * Free a virt_device structure. |
| + * If the virt_device added a tt_info (a hub) and has children pointing to |
| + * that tt_info, then free the child first. Recursive. |
| + * We can't rely on udev at this point to find child-parent relationships. |
| + */ |
| +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) |
| +{ |
| + struct xhci_virt_device *vdev; |
| + struct list_head *tt_list_head; |
| + struct xhci_tt_bw_info *tt_info, *next; |
| + int i; |
| + |
| + vdev = xhci->devs[slot_id]; |
| + if (!vdev) |
| + return; |
| + |
| + tt_list_head = &(xhci->rh_bw[vdev->real_port - 1].tts); |
| + list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { |
| + /* is this a hub device that added a tt_info to the tts list */ |
| + if (tt_info->slot_id == slot_id) { |
| + /* are any devices using this tt_info? */ |
| + for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { |
| + vdev = xhci->devs[i]; |
| + if (vdev && (vdev->tt_info == tt_info)) |
| + xhci_free_virt_devices_depth_first( |
| + xhci, i); |
| + } |
| + } |
| + } |
| + /* we are now at a leaf device */ |
| + xhci_free_virt_device(xhci, slot_id); |
| +} |
| + |
| int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, |
| struct usb_device *udev, gfp_t flags) |
| { |
| @@ -1829,8 +1863,8 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) |
| } |
| } |
| |
| - for (i = 1; i < MAX_HC_SLOTS; ++i) |
| - xhci_free_virt_device(xhci, i); |
| + for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) |
| + xhci_free_virt_devices_depth_first(xhci, i); |
| |
| dma_pool_destroy(xhci->segment_pool); |
| xhci->segment_pool = NULL; |
| -- |
| 2.10.1 |
| |