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

USB: fix the clear_tt_buffer interface

This patch (as1255) updates the interface for calling
usb_hub_clear_tt_buffer(). Even the name of the function is changed!

When an async URB (i.e., Control or Bulk) going through a high-speed
hub to a non-high-speed device is cancelled or fails, the hub's
Transaction Translator buffer may be left busy still trying to
complete the transaction. The buffer has to be cleared; that's what
usb_hub_clear_tt_buffer() does.

It isn't safe to send any more URBs to the same endpoint until the TT
buffer is fully clear. Therefore the HCD needs to be told when the
Clear-TT-Buffer request has finished. This patch adds a callback
method to struct hc_driver for that purpose, and makes the hub driver
invoke the callback at the proper time.

The patch also changes a couple of names; "hub_tt_kevent" and
"tt.kevent" now look rather antiquated.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
cb88a1b8 87ea8c88

+35 -17
+4
drivers/usb/core/hcd.h
··· 227 227 /* has a port been handed over to a companion? */ 228 228 int (*port_handed_over)(struct usb_hcd *, int); 229 229 230 + /* CLEAR_TT_BUFFER completion callback */ 231 + void (*clear_tt_buffer_complete)(struct usb_hcd *, 232 + struct usb_host_endpoint *); 233 + 230 234 /* xHCI specific functions */ 231 235 /* Called by usb_alloc_dev to alloc HC device structures */ 232 236 int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
+26 -14
drivers/usb/core/hub.c
··· 450 450 * talking to TTs must queue control transfers (not just bulk and iso), so 451 451 * both can talk to the same hub concurrently. 452 452 */ 453 - static void hub_tt_kevent (struct work_struct *work) 453 + static void hub_tt_work(struct work_struct *work) 454 454 { 455 455 struct usb_hub *hub = 456 - container_of(work, struct usb_hub, tt.kevent); 456 + container_of(work, struct usb_hub, tt.clear_work); 457 457 unsigned long flags; 458 458 int limit = 100; 459 459 ··· 462 462 struct list_head *next; 463 463 struct usb_tt_clear *clear; 464 464 struct usb_device *hdev = hub->hdev; 465 + const struct hc_driver *drv; 465 466 int status; 466 467 467 468 next = hub->tt.clear_list.next; ··· 472 471 /* drop lock so HCD can concurrently report other TT errors */ 473 472 spin_unlock_irqrestore (&hub->tt.lock, flags); 474 473 status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); 475 - spin_lock_irqsave (&hub->tt.lock, flags); 476 - 477 474 if (status) 478 475 dev_err (&hdev->dev, 479 476 "clear tt %d (%04x) error %d\n", 480 477 clear->tt, clear->devinfo, status); 478 + 479 + /* Tell the HCD, even if the operation failed */ 480 + drv = clear->hcd->driver; 481 + if (drv->clear_tt_buffer_complete) 482 + (drv->clear_tt_buffer_complete)(clear->hcd, clear->ep); 483 + 481 484 kfree(clear); 485 + spin_lock_irqsave(&hub->tt.lock, flags); 482 486 } 483 487 spin_unlock_irqrestore (&hub->tt.lock, flags); 484 488 } 485 489 486 490 /** 487 - * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub 488 - * @udev: the device whose split transaction failed 489 - * @pipe: identifies the endpoint of the failed transaction 491 + * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub 492 + * @urb: an URB associated with the failed or incomplete split transaction 490 493 * 491 494 * High speed HCDs use this to tell the hub driver that some split control or 492 495 * bulk transaction failed in a way that requires clearing internal state of ··· 500 495 * It may not be possible for that hub to handle additional full (or low) 501 496 * speed transactions until that state is fully cleared out. 502 497 */ 503 - void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) 498 + int usb_hub_clear_tt_buffer(struct urb *urb) 504 499 { 500 + struct usb_device *udev = urb->dev; 501 + int pipe = urb->pipe; 505 502 struct usb_tt *tt = udev->tt; 506 503 unsigned long flags; 507 504 struct usb_tt_clear *clear; ··· 515 508 if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { 516 509 dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); 517 510 /* FIXME recover somehow ... RESET_TT? */ 518 - return; 511 + return -ENOMEM; 519 512 } 520 513 521 514 /* info that CLEAR_TT_BUFFER needs */ ··· 527 520 : (USB_ENDPOINT_XFER_BULK << 11); 528 521 if (usb_pipein (pipe)) 529 522 clear->devinfo |= 1 << 15; 530 - 523 + 524 + /* info for completion callback */ 525 + clear->hcd = bus_to_hcd(udev->bus); 526 + clear->ep = urb->ep; 527 + 531 528 /* tell keventd to clear state for this TT */ 532 529 spin_lock_irqsave (&tt->lock, flags); 533 530 list_add_tail (&clear->clear_list, &tt->clear_list); 534 - schedule_work (&tt->kevent); 531 + schedule_work(&tt->clear_work); 535 532 spin_unlock_irqrestore (&tt->lock, flags); 533 + return 0; 536 534 } 537 - EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); 535 + EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer); 538 536 539 537 /* If do_delay is false, return the number of milliseconds the caller 540 538 * needs to delay. ··· 830 818 if (hub->has_indicators) 831 819 cancel_delayed_work_sync(&hub->leds); 832 820 if (hub->tt.hub) 833 - cancel_work_sync(&hub->tt.kevent); 821 + cancel_work_sync(&hub->tt.clear_work); 834 822 } 835 823 836 824 /* caller has locked the hub device */ ··· 947 935 948 936 spin_lock_init (&hub->tt.lock); 949 937 INIT_LIST_HEAD (&hub->tt.clear_list); 950 - INIT_WORK (&hub->tt.kevent, hub_tt_kevent); 938 + INIT_WORK(&hub->tt.clear_work, hub_tt_work); 951 939 switch (hdev->descriptor.bDeviceProtocol) { 952 940 case 0: 953 941 break;
+4 -2
drivers/usb/core/hub.h
··· 188 188 /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ 189 189 spinlock_t lock; 190 190 struct list_head clear_list; /* of usb_tt_clear */ 191 - struct work_struct kevent; 191 + struct work_struct clear_work; 192 192 }; 193 193 194 194 struct usb_tt_clear { 195 195 struct list_head clear_list; 196 196 unsigned tt; 197 197 u16 devinfo; 198 + struct usb_hcd *hcd; 199 + struct usb_host_endpoint *ep; 198 200 }; 199 201 200 - extern void usb_hub_tt_clear_buffer(struct usb_device *dev, int pipe); 202 + extern int usb_hub_clear_tt_buffer(struct urb *urb); 201 203 extern void usb_ep0_reinit(struct usb_device *); 202 204 203 205 #endif /* __LINUX_HUB_H */
+1 -1
drivers/usb/host/ehci-q.c
··· 215 215 /* REVISIT ARC-derived cores don't clear the root 216 216 * hub TT buffer in this way... 217 217 */ 218 - usb_hub_tt_clear_buffer (urb->dev, urb->pipe); 218 + usb_hub_clear_tt_buffer(urb); 219 219 } 220 220 } 221 221