Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/external/bsd/dwc2/dist Merge



details:   https://anonhg.NetBSD.org/src/rev/f04285508e0e
branches:  trunk
changeset: 365234:f04285508e0e
user:      simonb <simonb%NetBSD.org@localhost>
date:      Wed Aug 08 07:20:44 2018 +0000

description:
Merge
https://github.com/torvalds/linux/commit/38d2b5fb75c15923fb89c32134516a623515bce4
to mitigate USB NAK interrupt storms, with an extra change from
skrll@ to also mitigate interrupt storms on the non-split case with
older DWC2 cores.

Fixes woeful USB disk performance on an ERLITE.

Much thanks to skrll@ for pointer to the above patch, handling the
non-split case and testing.

diffstat:

 sys/external/bsd/dwc2/dist/dwc2_core.h     |   3 +-
 sys/external/bsd/dwc2/dist/dwc2_hcd.c      |  11 ++-
 sys/external/bsd/dwc2/dist/dwc2_hcd.h      |  14 ++++-
 sys/external/bsd/dwc2/dist/dwc2_hcdintr.c  |  30 ++++++++-
 sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c |  92 ++++++++++++++++++++++++++++-
 5 files changed, 137 insertions(+), 13 deletions(-)

diffs (truncated from 361 to 300 lines):

diff -r acee9bc58568 -r f04285508e0e sys/external/bsd/dwc2/dist/dwc2_core.h
--- a/sys/external/bsd/dwc2/dist/dwc2_core.h    Wed Aug 08 03:15:18 2018 +0000
+++ b/sys/external/bsd/dwc2/dist/dwc2_core.h    Wed Aug 08 07:20:44 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc2_core.h,v 1.8 2016/02/24 22:17:54 skrll Exp $      */
+/*     $NetBSD: dwc2_core.h,v 1.9 2018/08/08 07:20:44 simonb Exp $     */
 
 /*
  * core.h - DesignWare HS OTG Controller common declarations
@@ -756,6 +756,7 @@
        } flags;
 
        struct list_head non_periodic_sched_inactive;
+       struct list_head non_periodic_sched_waiting;
        struct list_head non_periodic_sched_active;
        struct list_head *non_periodic_qh_ptr;
        struct list_head periodic_sched_inactive;
diff -r acee9bc58568 -r f04285508e0e sys/external/bsd/dwc2/dist/dwc2_hcd.c
--- a/sys/external/bsd/dwc2/dist/dwc2_hcd.c     Wed Aug 08 03:15:18 2018 +0000
+++ b/sys/external/bsd/dwc2/dist/dwc2_hcd.c     Wed Aug 08 07:20:44 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc2_hcd.c,v 1.20 2018/08/01 16:44:14 skrll Exp $      */
+/*     $NetBSD: dwc2_hcd.c,v 1.21 2018/08/08 07:20:44 simonb Exp $     */
 
 /*
  * hcd.c - DesignWare HS OTG Controller host-mode routines
@@ -42,7 +42,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcd.c,v 1.20 2018/08/01 16:44:14 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcd.c,v 1.21 2018/08/08 07:20:44 simonb Exp $");
 
 #include <sys/types.h>
 #include <sys/kmem.h>
@@ -117,6 +117,10 @@
        list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive,
                            qh_list_entry)
                dev_dbg(hsotg->dev, "    %p\n", qh);
+       dev_dbg(hsotg->dev, "  NP waiting sched:\n");
+       list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting,
+                           qh_list_entry)
+               dev_dbg(hsotg->dev, "    %p\n", qh);
        dev_dbg(hsotg->dev, "  NP active sched:\n");
        list_for_each_entry(qh, &hsotg->non_periodic_sched_active,
                            qh_list_entry)
@@ -194,6 +198,7 @@
 static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg)
 {
        dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive);
+       dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting);
        dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active);
        dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive);
        dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready);
@@ -2215,6 +2220,7 @@
 
        /* Free memory for QH/QTD lists */
        dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive);
+       dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting);
        dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active);
        dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive);
        dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready);
@@ -2337,6 +2343,7 @@
 
        /* Initialize the non-periodic schedule */
        INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive);
+       INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting);
        INIT_LIST_HEAD(&hsotg->non_periodic_sched_active);
 
        /* Initialize the periodic schedule */
diff -r acee9bc58568 -r f04285508e0e sys/external/bsd/dwc2/dist/dwc2_hcd.h
--- a/sys/external/bsd/dwc2/dist/dwc2_hcd.h     Wed Aug 08 03:15:18 2018 +0000
+++ b/sys/external/bsd/dwc2/dist/dwc2_hcd.h     Wed Aug 08 07:20:44 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc2_hcd.h,v 1.14 2016/04/23 10:15:30 skrll Exp $      */
+/*     $NetBSD: dwc2_hcd.h,v 1.15 2018/08/08 07:20:44 simonb Exp $     */
 
 /*
  * hcd.h - DesignWare HS OTG Controller host-mode declarations
@@ -222,6 +222,7 @@
 /**
  * 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
@@ -264,13 +265,18 @@
  * @n_bytes:            Xfer Bytes array. Each element corresponds to a transfer
  *                      descriptor and indicates original XferSize value for the
  *                      descriptor
+ * @wait_timer:         Timer used to wait before re-queuing.
  * @tt_buffer_dirty     True if clear_tt_buffer_complete is pending
+ * @want_wait:          We should wait before re-queuing; only matters for non-
+ *                      periodic transfers and is ignored for periodic ones.
+ * @wait_timer_cancel:  Set to true to cancel the wait_timer.
  *
  * 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;
@@ -299,7 +305,11 @@
        dma_addr_t desc_list_dma;
        u32 desc_list_sz;
        u32 *n_bytes;
+       /* XXX struct timer_list wait_timer; */
+       callout_t wait_timer;
        unsigned tt_buffer_dirty:1;
+       unsigned want_wait:1;
+       unsigned wait_timer_cancel:1;
 };
 
 /**
@@ -330,6 +340,7 @@
  * @n_desc:             Number of DMA descriptors for this QTD
  * @isoc_frame_index_last: Last activated frame (packet) index, used in
  *                      descriptor DMA mode only
+ * @num_naks:           Number of NAKs received on this QTD.
  * @urb:                URB for this transfer
  * @qh:                 Queue head for this QTD
  * @qtd_list_entry:     For linking to the QH's list of QTDs
@@ -360,6 +371,7 @@
        u8 error_count;
        u8 n_desc;
        u16 isoc_frame_index_last;
+       u16 num_naks;
        struct dwc2_hcd_urb *urb;
        struct dwc2_qh *qh;
        struct list_head qtd_list_entry;
diff -r acee9bc58568 -r f04285508e0e sys/external/bsd/dwc2/dist/dwc2_hcdintr.c
--- a/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c Wed Aug 08 03:15:18 2018 +0000
+++ b/sys/external/bsd/dwc2/dist/dwc2_hcdintr.c Wed Aug 08 07:20:44 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc2_hcdintr.c,v 1.13 2016/02/14 10:53:30 skrll Exp $  */
+/*     $NetBSD: dwc2_hcdintr.c,v 1.14 2018/08/08 07:20:44 simonb Exp $ */
 
 /*
  * hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling
@@ -40,7 +40,7 @@
  * This file contains the interrupt handlers for Host mode
  */
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdintr.c,v 1.13 2016/02/14 10:53:30 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdintr.c,v 1.14 2018/08/08 07:20:44 simonb Exp $");
 
 #include <sys/types.h>
 #include <sys/pool.h>
@@ -60,6 +60,13 @@
 #include "dwc2_core.h"
 #include "dwc2_hcd.h"
 
+/*
+ * If we get this many NAKs on a split transaction we'll slow down
+ * retransmission.  A 1 here means delay after the first NAK.
+ */
+#define DWC2_NAKS_BEFORE_DELAY         3
+int dwc2_naks_before_delay = DWC2_NAKS_BEFORE_DELAY;
+
 /* This function is for debug only */
 static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
 {
@@ -1256,6 +1263,16 @@
        /*
         * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
         * interrupt. Re-start the SSPLIT transfer.
+        *
+        * Normally for non-periodic transfers we'll retry right away, but to
+        * avoid interrupt storms we'll wait before retrying if we've got
+        * several NAKs. If we didn't do this we'd retry directly from the
+        * interrupt handler and could end up quickly getting another
+        * interrupt (another NAK), which we'd retry.
+        *
+        * Note that in DMA mode software only gets involved to re-send NAKed
+        * transfers for split transactions unless the core is missing OUT NAK
+        * enhancement.
         */
        if (chan->do_split) {
                /*
@@ -1272,6 +1289,8 @@
                if (chan->complete_split)
                        qtd->error_count = 0;
                qtd->complete_split = 0;
+               qtd->num_naks++;
+               qtd->qh->want_wait = qtd->num_naks >= dwc2_naks_before_delay;
                dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
                goto handle_nak_done;
        }
@@ -1297,7 +1316,12 @@
                 */
                qtd->error_count = 0;
 
-               if (!chan->qh->ping_state) {
+               if (hsotg->core_params->dma_enable > 0 && !chan->ep_is_in) {
+                       /*
+                        * Avoid interrupt storms.
+                        */
+                       qtd->qh->want_wait = 1;
+               } else if (!chan->qh->ping_state) {
                        dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
                                                  qtd, DWC2_HC_XFER_NAK);
                        dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
diff -r acee9bc58568 -r f04285508e0e sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c
--- a/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c        Wed Aug 08 03:15:18 2018 +0000
+++ b/sys/external/bsd/dwc2/dist/dwc2_hcdqueue.c        Wed Aug 08 07:20:44 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: dwc2_hcdqueue.c,v 1.14 2016/02/14 10:53:30 skrll Exp $ */
+/*     $NetBSD: dwc2_hcdqueue.c,v 1.15 2018/08/08 07:20:44 simonb Exp $        */
 
 /*
  * hcd_queue.c - DesignWare HS OTG Controller host queuing routines
@@ -42,7 +42,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdqueue.c,v 1.14 2016/02/14 10:53:30 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dwc2_hcdqueue.c,v 1.15 2018/08/08 07:20:44 simonb Exp $");
 
 #include <sys/types.h>
 #include <sys/kmem.h>
@@ -64,6 +64,10 @@
 #include "dwc2_hcd.h"
 
 static u32 dwc2_calc_bus_time(struct dwc2_hsotg *, int, int, int, int);
+static void dwc2_wait_timer_fn(void *);
+
+/* If we get a NAK, wait this long before retrying */
+#define DWC2_RETRY_WAIT_DELAY 1        /* msec */
 
 /**
  * dwc2_qh_init() - Initializes a QH structure
@@ -82,6 +86,10 @@
        dev_vdbg(hsotg->dev, "%s()\n", __func__);
 
        /* Initialize QH */
+       qh->hsotg = hsotg;
+       /* XXX timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0); */
+       callout_init(&qh->wait_timer, 0);
+       callout_setfunc(&qh->wait_timer, dwc2_wait_timer_fn, qh);
        qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
        qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
 
@@ -250,6 +258,17 @@
 void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
        struct dwc2_softc *sc = hsotg->hsotg_sc;
+
+       /*
+        * We don't have the lock so we can safely wait until the wait timer
+        * finishes.  Of course, at this point in time we'd better have set
+        * wait_timer_active to false so if this timer was still pending it
+        * won't do anything anyway, but we want it to finish before we free
+        * memory.
+        */
+       /* XXX del_timer_sync(&qh->wait_timer); */
+       callout_destroy(&qh->wait_timer);       /* XXX need to callout_halt() first? */
+
        if (qh->desc_list) {
                dwc2_hcd_qh_free_ddma(hsotg, qh);
        } else if (qh->dw_align_buf) {
@@ -583,6 +602,55 @@
 }
 
 /**
+ * dwc2_wait_timer_fn() - Timer function to re-queue after waiting
+ *
+ * As per the spec, a NAK indicates that "a function is temporarily unable to
+ * transmit or receive data, but will eventually be able to do so without need
+ * of host intervention".
+ *
+ * That means that when we encounter a NAK we're supposed to retry.
+ *
+ * ...but if we retry right away (from the interrupt handler that saw the NAK)
+ * then we can end up with an interrupt storm (if the other side keeps NAKing
+ * us) because on slow enough CPUs it could take us longer to get out of the
+ * interrupt routine than it takes for the device to send another NAK.  That
+ * leads to a constant stream of NAK interrupts and the CPU locks.
+ *
+ * ...so instead of retrying right away in the case of a NAK we'll set a timer
+ * to retry some time later.  This function handles that timer and moves the
+ * qh back to the "inactive" list, then queues transactions.
+ *
+ * @t: Pointer to wait_timer in a qh.
+ */
+static void dwc2_wait_timer_fn(void *arg)
+{
+       struct dwc2_qh *qh = arg;
+       struct dwc2_hsotg *hsotg = qh->hsotg;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+
+       /*
+        * We'll set wait_timer_cancel to true if we want to cancel this
+        * operation in dwc2_hcd_qh_unlink().
+        */



Home | Main Index | Thread Index | Old Index