[PATCH v2 3/4] usb: dwc2: host: Add a delay before releasing periodic bandwidth

2015-11-06 Thread Douglas Anderson
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

2015-11-06 Thread Douglas Anderson
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