Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/usb implement a gross hack to fix "boot -a" on syste...



details:   https://anonhg.NetBSD.org/src/rev/0b98432cf6fe
branches:  trunk
changeset: 835948:0b98432cf6fe
user:      mrg <mrg%NetBSD.org@localhost>
date:      Tue Sep 18 02:00:06 2018 +0000

description:
implement a gross hack to fix "boot -a" on systems with usb keyboards on
systems with ehci handover to uhci (and maybe ohci), and fix a similar
problem for "boot -s".

there is effort to ensure that all devices attached via USB are probed
before RB_ASKNAME or RB_SINGLE attempts to ask any questions on the console,
and largely this works, often by chance, today, for USB disks and root.
i've recently pushed this more into uhub and general USB device attachment
as well, and kept a config_pending reference across the first explore of
a bus.  these fix many issues with directly attached hubs.

however, on systems where devices connected to ehci ports are handed over
to a companion uhci or ohci port, it may not be the first, or even second,
bus explore that finds the device finally before attachment, and at this
point all config_pending references are dropped.

there is no direct communication between drivers, the potentials are
looked up but their device_t is only used for generic things like the name,
so informing the correct companion to expect a device and deal with the
config_pending references is not possible without some fairly ugly layer
violations or multi-level callbacks (eg, we have "ehci0", and usually an
the relevant companion, eg, "uhci2", but it is the uhub that uhci2 has
attached that will deal with the device attachment.)

with the above fixes to generic USB code, the disown happens during the
first explore.  the hack works by, at this point, checking if (a) root
is not mounted, (b) single user or ask name are set, and (c) if the hack
as not been triggered already.  if all 3 conditions are true, then a
config_pending_incr() is called and a callback is triggered for (default)
5 seconds to call config_pending_decr().  ehci detach pauses waiting for
this callback if scheduled.

this allows enough time for the uhub and the ukbd/wskbd to attach before
the RK_ASKROOT prompts appear.  testing shows it takes between 1.5 and
2 seconds for the keyboard to appear after the disown occurs.


Index: dev/usb/ehcivar.c
- new sc_compcallout, sc_compcallout, sc_complock, and a state for th
  handover hack.

Index: dev/usb/ehci.c
ehci_init():
- use aprint_normal_dev() instead of manual device_xname().
- initialise sc_compcallout, sc_compcallout, sc_complock, and sc_comp_state.
ehci_detach():
- if there are companion controllers, tear own the above, including waiting
  if there is a callback scheduled.
ehci_disown_callback():
- new callout to call config_pending_decr() in the the future.
  schedule this ca
ehci_disown_sched_callback():
- if booting to single user or asking names, call config_pending_incr() and
  schedule the callout above, default 5 second delay.
ehci_disown():
- if disowning a port call ehci_disown_sched_callback().

diffstat:

 sys/dev/usb/ehci.c    |  110 +++++++++++++++++++++++++++++++++++++++++++++----
 sys/dev/usb/ehcivar.h |   12 +++++-
 2 files changed, 111 insertions(+), 11 deletions(-)

diffs (204 lines):

diff -r 52e283d752e7 -r 0b98432cf6fe sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c        Tue Sep 18 01:36:44 2018 +0000
+++ b/sys/dev/usb/ehci.c        Tue Sep 18 02:00:06 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ehci.c,v 1.264 2018/09/16 20:21:56 mrg Exp $ */
+/*     $NetBSD: ehci.c,v 1.265 2018/09/18 02:00:06 mrg Exp $ */
 
 /*
  * Copyright (c) 2004-2012 The NetBSD Foundation, Inc.
@@ -53,7 +53,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.264 2018/09/16 20:21:56 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.265 2018/09/18 02:00:06 mrg Exp $");
 
 #include "ohci.h"
 #include "uhci.h"
@@ -75,6 +75,7 @@
 #include <sys/select.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
+#include <sys/reboot.h>
 
 #include <machine/endian.h>
 
@@ -446,8 +447,9 @@
        }
        if (sc->sc_ncomp > 0) {
                KASSERT(!(sc->sc_flags & EHCIF_ETTF));
-               aprint_normal("%s: %d companion controller%s, %d port%s%s",
-                   device_xname(sc->sc_dev), sc->sc_ncomp,
+               aprint_normal_dev(sc->sc_dev,
+                   "%d companion controller%s, %d port%s%s",
+                   sc->sc_ncomp,
                    sc->sc_ncomp!=1 ? "s" : "",
                    EHCI_HCS_N_PCC(sparams),
                    EHCI_HCS_N_PCC(sparams)!=1 ? "s" : "",
@@ -459,6 +461,11 @@
                                    device_xname(sc->sc_comps[i]));
                }
                aprint_normal("\n");
+
+               mutex_init(&sc->sc_complock, MUTEX_DEFAULT, IPL_USB);
+               callout_init(&sc->sc_compcallout, CALLOUT_MPSAFE);
+               cv_init(&sc->sc_compcv, "ehciccv");
+               sc->sc_comp_state = CO_EARLY;
        }
        sc->sc_noport = EHCI_HCS_N_PORTS(sparams);
        sc->sc_hasppc = EHCI_HCS_PPC(sparams);
@@ -1337,6 +1344,19 @@
        if (rv != 0)
                return rv;
 
+       if (sc->sc_ncomp > 0) {
+               mutex_enter(&sc->sc_complock);
+               /* XXX try to halt callout instead of waiting */
+               while (sc->sc_comp_state == CO_SCHED)
+                       cv_wait(&sc->sc_compcv, &sc->sc_complock);
+               mutex_exit(&sc->sc_complock);
+
+               callout_halt(&sc->sc_compcallout, NULL);
+               callout_destroy(&sc->sc_compcallout);
+               cv_destroy(&sc->sc_compcv);
+               mutex_destroy(&sc->sc_complock);
+       }
+
        callout_halt(&sc->sc_tmo_intrlist, NULL);
        callout_destroy(&sc->sc_tmo_intrlist);
 
@@ -2584,6 +2604,72 @@
        return totlen;
 }
 
+/*
+ * Handle ehci hand-off in early boot vs RB_ASKNAME/RB_SINGLE.
+ *
+ * This pile of garbage below works around the following problem without
+ * holding boots with no hand-over devices present, while penalising
+ * boots where the first ehci probe hands off devices with a 5 second
+ * delay, if RB_ASKNAME/RB_SINGLE is set.  This is typically not a problem
+ * for RB_SINGLE, but the same basic issue exists.
+ *
+ * The way ehci hand-off works, the companion controller does not get the
+ * device until after its' initial bus explore, so the reference dropped
+ * after the first explore is not enough.  5 seconds should be enough,
+ * and EHCI_DISOWN_DELAY_SECONDS can be set to another value.
+ *
+ * There are 3 states.  CO_EARLY is set during attach.  CO_SCHED is set
+ * if the callback is scheduled.  CO_DONE is set when the callout has
+ * called config_pending_decr().
+ *
+ * There's a mutex, a cv and a callout here, and we delay detach if the
+ * callout has been set.
+ */
+#ifndef EHCI_DISOWN_DELAY_SECONDS
+#define EHCI_DISOWN_DELAY_SECONDS 5
+#endif
+static int ehci_disown_delay_seconds = EHCI_DISOWN_DELAY_SECONDS;
+
+static void
+ehci_disown_callback(void *arg)
+{
+       ehci_softc_t *sc = arg;
+
+       config_pending_decr(sc->sc_dev);
+
+       mutex_enter(&sc->sc_complock);
+       KASSERT(sc->sc_comp_state == CO_SCHED);
+       sc->sc_comp_state = CO_DONE;
+       cv_signal(&sc->sc_compcv);
+       mutex_exit(&sc->sc_complock);
+}
+
+static void
+ehci_disown_sched_callback(ehci_softc_t *sc)
+{
+       extern bool root_is_mounted;
+
+       mutex_enter(&sc->sc_complock);
+
+       if (root_is_mounted ||
+           (boothowto & (RB_ASKNAME|RB_SINGLE)) == 0 ||
+           sc->sc_comp_state != CO_EARLY) {
+               mutex_exit(&sc->sc_complock);
+               return;
+       }
+
+       callout_reset(&sc->sc_compcallout, ehci_disown_delay_seconds * hz,
+           ehci_disown_callback, &sc->sc_dev);
+       sc->sc_comp_state = CO_SCHED;
+
+       mutex_exit(&sc->sc_complock);
+
+       config_pending_incr(sc->sc_dev);
+       aprint_normal("delaying %s by %u seconds due to USB owner change.",
+           (boothowto & RB_ASKNAME) == 0 ? "ask root" : "single user",
+           ehci_disown_delay_seconds);
+}
+
 Static void
 ehci_disown(ehci_softc_t *sc, int index, int lowspeed)
 {
@@ -2593,13 +2679,11 @@
        EHCIHIST_FUNC(); EHCIHIST_CALLED();
 
        DPRINTF("index=%jd lowspeed=%jd", index, lowspeed, 0, 0);
-#ifdef DIAGNOSTIC
        if (sc->sc_npcomp != 0) {
                int i = (index-1) / sc->sc_npcomp;
-               if (i >= sc->sc_ncomp)
-                       printf("%s: strange port\n",
-                              device_xname(sc->sc_dev));
-               else
+               if (i < sc->sc_ncomp) {
+                       ehci_disown_sched_callback(sc);
+#ifdef DIAGNOSTIC
                        printf("%s: handing over %s speed device on "
                               "port %d to %s\n",
                               device_xname(sc->sc_dev),
@@ -2607,10 +2691,16 @@
                               index, sc->sc_comps[i] ?
                                 device_xname(sc->sc_comps[i]) :
                                 "companion controller");
+               } else {
+                       printf("%s: strange port\n",
+                              device_xname(sc->sc_dev));
+#endif
+               }
        } else {
+#ifdef DIAGNOSTIC
                printf("%s: npcomp == 0\n", device_xname(sc->sc_dev));
+#endif
        }
-#endif
        port = EHCI_PORTSC(index);
        v = EOREAD4(sc, port) &~ EHCI_PS_CLEAR;
        EOWRITE4(sc, port, v | EHCI_PS_PO);
diff -r 52e283d752e7 -r 0b98432cf6fe sys/dev/usb/ehcivar.h
--- a/sys/dev/usb/ehcivar.h     Tue Sep 18 01:36:44 2018 +0000
+++ b/sys/dev/usb/ehcivar.h     Tue Sep 18 02:00:06 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ehcivar.h,v 1.45 2018/08/09 06:26:47 mrg Exp $ */
+/*     $NetBSD: ehcivar.h,v 1.46 2018/09/18 02:00:06 mrg Exp $ */
 
 /*
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -182,6 +182,16 @@
        u_int sc_npcomp;
        device_t sc_comps[EHCI_COMPANION_MAX];
 
+       /* This chunk to handle early RB_ASKNAME hand over. */
+       callout_t sc_compcallout;
+       kmutex_t sc_complock;
+       kcondvar_t sc_compcv;
+       enum {
+               CO_EARLY,
+               CO_SCHED,
+               CO_DONE,
+       } sc_comp_state;
+
        usb_dma_t sc_fldma;
        ehci_link_t *sc_flist;
        u_int sc_flsize;



Home | Main Index | Thread Index | Old Index