Hi, Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx> writes: > 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@xxxxxxxxxxxxxxx> > Signed-off-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx> > > --- > > Guenter Roeck, does this work for you? > > A rework of how tt_info is stored and used might be needed, > but that will take some time and won't go to stable as easily. > --- > drivers/usb/host/xhci-mem.c | 38 ++++++++++++++++++++++++++++++++++++-- > 1 file changed, 36 insertions(+), 2 deletions(-) > > diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c > index 6afe323..b3a5cd8 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) { if (tt_info->slot_id != slot_id) continue; > + /* are any devices using this tt_info? */ > + for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { off-by-one here ? Why is i starting from 1? > + vdev = xhci->devs[i]; > + if (vdev && (vdev->tt_info == tt_info)) if (!vdev || vdev->tt_info != tt_info) continue; > + 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--) converting MAX_HC_SLOTS to HCS_MAX_SLOTS(xhci->hcs_params1) seems unrelated to $subject. Perhaps just mention on commit log? -- balbi
Attachment:
signature.asc
Description: PGP signature