[PATCH v2 3/4] usb: dwc2: host: Add a delay before releasing periodic bandwidth
We'd like to be able to use HCD_BH in order to speed up the dwc2 host interrupt handler quite a bit. However, according to the kernel doc for usb_submit_urb() (specifically the part about "Reserved Bandwidth Transfers"), we need to keep a reservation active as long as a device driver keeps submitting. That was easy to do when we gave back the URB in the interrupt context: we just looked at when our queue was empty and released the reserved bandwidth then. ...but now we need a little more complexity. We'll follow EHCI's lead in commit 9118f9eb4f1e ("USB: EHCI: improve interrupt qh unlink") and add a 5ms delay. Since we don't have a whole timer infrastructure in dwc2, we'll just add a timer per QH. The overhead for this is very small. Signed-off-by: Douglas Anderson --- Changes in v2: - Periodic bandwidth release delay new for V2 drivers/usb/dwc2/hcd.h | 6 ++ drivers/usb/dwc2/hcd_queue.c | 232 +-- 2 files changed, 184 insertions(+), 54 deletions(-) diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 8a4486e1a542..b75a8b116f6e 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -211,6 +211,7 @@ enum dwc2_transaction_type { /** * struct dwc2_qh - Software queue head structure * + * @hsotg: The HCD state structure for the DWC OTG controller * @ep_type:Endpoint type. One of the following values: * - USB_ENDPOINT_XFER_CONTROL * - USB_ENDPOINT_XFER_BULK @@ -247,13 +248,16 @@ enum dwc2_transaction_type { * @n_bytes:Xfer Bytes array. Each element corresponds to a transfer * descriptor and indicates original XferSize value for the * descriptor + * @unreserve_timer:Timer for releasing periodic reservation. * @tt_buffer_dirty True if clear_tt_buffer_complete is pending + * @unreserve_pending: True if we planned to unreserve but haven't yet. * * A Queue Head (QH) holds the static characteristics of an endpoint and * maintains a list of transfers (QTDs) for that endpoint. A QH structure may * be entered in either the non-periodic or periodic schedule. */ struct dwc2_qh { + struct dwc2_hsotg *hsotg; u8 ep_type; u8 ep_is_in; u16 maxp; @@ -275,7 +279,9 @@ struct dwc2_qh { struct dwc2_hcd_dma_desc *desc_list; dma_addr_t desc_list_dma; u32 *n_bytes; + struct timer_list unreserve_timer; unsigned tt_buffer_dirty:1; + unsigned unreserve_pending:1; }; /** diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 3e1edafc593c..10f27b594e92 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -53,6 +53,94 @@ #include "core.h" #include "hcd.h" +/* Wait this long before releasing periodic reservation */ +#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) + +/** + * dwc2_do_unreserve() - Actually release the periodic reservation + * + * This function actually releases the periodic bandwidth that was reserved + * by the given qh. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh:QH for the periodic transfer. + */ +static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + assert_spin_locked(>lock); + + WARN_ON(!qh->unreserve_pending); + + if (WARN_ON(!list_empty(>qh_list_entry))) + list_del_init(>qh_list_entry); + + /* Update claimed usecs per (micro)frame */ + hsotg->periodic_usecs -= qh->usecs; + + if (hsotg->core_params->uframe_sched > 0) { + int i; + + for (i = 0; i < 8; i++) { + hsotg->frame_usecs[i] += qh->frame_usecs[i]; + qh->frame_usecs[i] = 0; + } + } else { + /* Release periodic channel reservation */ + hsotg->periodic_channels--; + } + + /* No more unreserve pending--we're did it */ + qh->unreserve_pending = false; +} + +/** + * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation + * + * According to the kernel doc for usb_submit_urb() (specifically the part about + * "Reserved Bandwidth Transfers"), we need to keep a reservation active as + * long as a device driver keeps submitting. Since we're using HCD_BH to give + * back the URB we need to give the driver a little bit of time before we + * release the reservation. This worker is called after the appropriate + * delay. + * + * @work: Pointer to a qh unreserve_work. + */ +static void dwc2_unreserve_timer_fn(unsigned long data) +{ + struct dwc2_qh *qh = (struct dwc2_qh *)data; + struct dwc2_hsotg *hsotg = qh->hsotg; + unsigned long flags; + + /* +* Wait for the lock, or for us to be scheduled again. We +* could be scheduled again if: +* - We started executing but didn't get the lock
[PATCH v2 3/4] usb: dwc2: host: Add a delay before releasing periodic bandwidth
We'd like to be able to use HCD_BH in order to speed up the dwc2 host interrupt handler quite a bit. However, according to the kernel doc for usb_submit_urb() (specifically the part about "Reserved Bandwidth Transfers"), we need to keep a reservation active as long as a device driver keeps submitting. That was easy to do when we gave back the URB in the interrupt context: we just looked at when our queue was empty and released the reserved bandwidth then. ...but now we need a little more complexity. We'll follow EHCI's lead in commit 9118f9eb4f1e ("USB: EHCI: improve interrupt qh unlink") and add a 5ms delay. Since we don't have a whole timer infrastructure in dwc2, we'll just add a timer per QH. The overhead for this is very small. Signed-off-by: Douglas Anderson--- Changes in v2: - Periodic bandwidth release delay new for V2 drivers/usb/dwc2/hcd.h | 6 ++ drivers/usb/dwc2/hcd_queue.c | 232 +-- 2 files changed, 184 insertions(+), 54 deletions(-) diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 8a4486e1a542..b75a8b116f6e 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -211,6 +211,7 @@ enum dwc2_transaction_type { /** * struct dwc2_qh - Software queue head structure * + * @hsotg: The HCD state structure for the DWC OTG controller * @ep_type:Endpoint type. One of the following values: * - USB_ENDPOINT_XFER_CONTROL * - USB_ENDPOINT_XFER_BULK @@ -247,13 +248,16 @@ enum dwc2_transaction_type { * @n_bytes:Xfer Bytes array. Each element corresponds to a transfer * descriptor and indicates original XferSize value for the * descriptor + * @unreserve_timer:Timer for releasing periodic reservation. * @tt_buffer_dirty True if clear_tt_buffer_complete is pending + * @unreserve_pending: True if we planned to unreserve but haven't yet. * * A Queue Head (QH) holds the static characteristics of an endpoint and * maintains a list of transfers (QTDs) for that endpoint. A QH structure may * be entered in either the non-periodic or periodic schedule. */ struct dwc2_qh { + struct dwc2_hsotg *hsotg; u8 ep_type; u8 ep_is_in; u16 maxp; @@ -275,7 +279,9 @@ struct dwc2_qh { struct dwc2_hcd_dma_desc *desc_list; dma_addr_t desc_list_dma; u32 *n_bytes; + struct timer_list unreserve_timer; unsigned tt_buffer_dirty:1; + unsigned unreserve_pending:1; }; /** diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 3e1edafc593c..10f27b594e92 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -53,6 +53,94 @@ #include "core.h" #include "hcd.h" +/* Wait this long before releasing periodic reservation */ +#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) + +/** + * dwc2_do_unreserve() - Actually release the periodic reservation + * + * This function actually releases the periodic bandwidth that was reserved + * by the given qh. + * + * @hsotg: The HCD state structure for the DWC OTG controller + * @qh:QH for the periodic transfer. + */ +static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) +{ + assert_spin_locked(>lock); + + WARN_ON(!qh->unreserve_pending); + + if (WARN_ON(!list_empty(>qh_list_entry))) + list_del_init(>qh_list_entry); + + /* Update claimed usecs per (micro)frame */ + hsotg->periodic_usecs -= qh->usecs; + + if (hsotg->core_params->uframe_sched > 0) { + int i; + + for (i = 0; i < 8; i++) { + hsotg->frame_usecs[i] += qh->frame_usecs[i]; + qh->frame_usecs[i] = 0; + } + } else { + /* Release periodic channel reservation */ + hsotg->periodic_channels--; + } + + /* No more unreserve pending--we're did it */ + qh->unreserve_pending = false; +} + +/** + * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation + * + * According to the kernel doc for usb_submit_urb() (specifically the part about + * "Reserved Bandwidth Transfers"), we need to keep a reservation active as + * long as a device driver keeps submitting. Since we're using HCD_BH to give + * back the URB we need to give the driver a little bit of time before we + * release the reservation. This worker is called after the appropriate + * delay. + * + * @work: Pointer to a qh unreserve_work. + */ +static void dwc2_unreserve_timer_fn(unsigned long data) +{ + struct dwc2_qh *qh = (struct dwc2_qh *)data; + struct dwc2_hsotg *hsotg = qh->hsotg; + unsigned long flags; + + /* +* Wait for the lock, or for us to be scheduled again. We +* could be scheduled again if: +* - We started executing