Subject: Re: Request for review on scsipi changes
To: James Chacon <jchacon@genuity.net>
From: Matthew Jacob <mjacob@feral.com>
List: tech-kern
Date: 09/16/2002 23:01:13
Looks good to me too- FWIW.
On Mon, 16 Sep 2002, James Chacon wrote:
> Today scsipi forks a kthread off for each bus and all work happens within
> the thread.
>
> However in one place it attempts to do some scsi probes outside of this model,
> which is at autoconfiguration time. The current code uses config_interrupts
> and a callback to do the initial device probe and subattach's as required.
>
> The problem with this is thread context. It runs the initial probes inside
> of whatever thread configured the bus in the first place. For firewire this
> is the fwohci thread for instance. What happens in the probes is this thread
> is then put to sleep waiting on a scsipi_complete to be called from an
> interrupt handler. That then causes a deadlock for that thread since in cases
> of firewire the fwohci thread is just concerned with pulling packets off the
> wire and notifying upper layer devices to go to work.
>
> So the 2 choices to fix this were:
>
> 1. Make the main firewire driver aware of it's scsipi attachment and figure
> out a clean way to post the scsipi_complete's.
>
> 2. The rest of the scsipi model works once the kthread is forked off
> and all work happens within the thread. (it can be put to sleep waiting on
> a scsipi_complete call and nothing else will deadlock). So to solve this,
> force the device probes and subattach's to happen within the kthread.
>
> After talking with Jason a few months ago it seemed like #2 is the cleaner
> way to do this and makes the most sense.
>
> Attached are patches that do #2. Of the 2 sides to scsipi only the scsi end
> needed this as atapi doesn't have this problem. To account for "jitter" and
> to make sure all devices always attach in the same order a queue is used to
> force all probes to occur in bus attach order.
>
> I'd like any comments/concerns on these before I commit them as it affects
> a fairly critical area of code for systems. I've been running with them in
> a 2 scsibus configured box for months now and every reboot probes in the
> correct order. (and firewire doesn't deadlock anymore).
>
> James
>
> Index: sys/dev/scsipi/atapiconf.c
> ===================================================================
> RCS file: /cvsroot/syssrc/sys/dev/scsipi/atapiconf.c,v
> retrieving revision 1.48
> diff -u -r1.48 atapiconf.c
> --- sys/dev/scsipi/atapiconf.c 2002/04/01 20:37:41 1.48
> +++ sys/dev/scsipi/atapiconf.c 2002/09/16 16:35:07
> @@ -186,6 +186,8 @@
> printf(": %d targets\n", chan->chan_ntargets);
>
> /* Initialize the channel. */
> + chan->chan_init_cb = NULL;
> + chan->chan_init_cb_arg = NULL;
> scsipi_channel_init(chan);
>
> /* Probe the bus for devices. */
> Index: sys/dev/scsipi/scsiconf.c
> ===================================================================
> RCS file: /cvsroot/syssrc/sys/dev/scsipi/scsiconf.c,v
> retrieving revision 1.187
> diff -u -r1.187 scsiconf.c
> --- sys/dev/scsipi/scsiconf.c 2002/09/06 13:23:31 1.187
> +++ sys/dev/scsipi/scsiconf.c 2002/09/16 16:35:07
> @@ -67,6 +67,8 @@
> #include <sys/conf.h>
> #include <sys/fcntl.h>
> #include <sys/scsiio.h>
> +#include <sys/queue.h>
> +#include <sys/lock.h>
>
> #include <dev/scsipi/scsi_all.h>
> #include <dev/scsipi/scsipi_all.h>
> @@ -81,6 +83,15 @@
> NULL,
> };
>
> +struct scsi_initq {
> + struct scsipi_channel *sc_channel;
> + TAILQ_ENTRY(scsi_initq) scsi_initq;
> +};
> +
> +static TAILQ_HEAD(, scsi_initq) scsi_initq_head;
> +static int scsi_initq_inited = 0;
> +static struct simplelock scsibus_interlock = SIMPLELOCK_INITIALIZER;
> +
> int scsi_probe_device __P((struct scsibus_softc *, int, int));
>
> int scsibusmatch __P((struct device *, struct cfdata *, void *));
> @@ -107,7 +118,7 @@
> };
>
> int scsibusprint __P((void *, const char *));
> -void scsibus_config_interrupts __P((struct device *));
> +void scsibus_config __P((struct scsipi_channel *, void *));
>
> const struct scsipi_bustype scsi_bustype = {
> SCSIPI_BUSTYPE_SCSI,
> @@ -161,47 +172,78 @@
> {
> struct scsibus_softc *sc = (void *) self;
> struct scsipi_channel *chan = aux;
> + struct scsi_initq *scsi_initq;
>
> sc->sc_channel = chan;
> chan->chan_name = sc->sc_dev.dv_xname;
>
> + printf(": %d %s, %d luns per target\n",
> + chan->chan_ntargets, chan->chan_ntargets == 1 ? "target":"targets",
> + chan->chan_nluns);
> +
> /* Initialize the channel structure first */
> + chan->chan_init_cb = scsibus_config;
> + chan->chan_init_cb_arg = sc;
> +
> + scsi_initq = malloc(sizeof(struct scsi_initq), M_DEVBUF, M_WAITOK);
> + if (!scsi_initq_inited) {
> + TAILQ_INIT(&scsi_initq_head);
> + scsi_initq_inited = 1;
> + }
> + scsi_initq->sc_channel = chan;
> + TAILQ_INSERT_TAIL(&scsi_initq_head, scsi_initq, scsi_initq);
> if (scsipi_channel_init(chan)) {
> printf(": failed to init channel\n");
> return;
> }
>
> - printf(": %d targets, %d luns per target\n",
> - chan->chan_ntargets, chan->chan_nluns);
> -
> -
> - /*
> - * Defer configuration of the children until interrupts
> - * are enabled.
> - */
> - config_interrupts(self, scsibus_config_interrupts);
> }
>
> void
> -scsibus_config_interrupts(self)
> - struct device *self;
> +scsibus_config(chan, arg)
> + struct scsipi_channel *chan;
> + void *arg;
> {
> - struct scsibus_softc *sc = (void *) self;
> + struct scsibus_softc *sc = arg;
> + struct scsi_initq *scsi_initq;
>
> #ifndef SCSI_DELAY
> #define SCSI_DELAY 2
> #endif
>
> - if ((sc->sc_channel->chan_flags & SCSIPI_CHAN_NOSETTLE) == 0 &&
> + config_pending_incr();
> +
> + /* Make sure the devices probe in scsibus order to avoid jitter. */
> + simple_lock(&scsibus_interlock);
> + while (1) {
> + scsi_initq = TAILQ_FIRST(&scsi_initq_head);
> + if (scsi_initq->sc_channel == chan)
> + break;
> + ltsleep(&scsi_initq_head, PRIBIO, "scsi_initq", 0,
> + &scsibus_interlock);
> + }
> +
> + simple_unlock(&scsibus_interlock);
> +
> + if ((chan->chan_flags & SCSIPI_CHAN_NOSETTLE) == 0 &&
> SCSI_DELAY > 0) {
> printf("%s: waiting %d seconds for devices to settle...\n",
> - self->dv_xname, SCSI_DELAY);
> + sc->sc_dev.dv_xname, SCSI_DELAY);
> /* ...an identifier we know no one will use... */
> - (void) tsleep(scsibus_config_interrupts, PRIBIO,
> + (void) tsleep(scsibus_config, PRIBIO,
> "scsidly", SCSI_DELAY * hz);
> }
>
> scsi_probe_bus(sc, -1, -1);
> +
> + simple_lock(&scsibus_interlock);
> + TAILQ_REMOVE(&scsi_initq_head, scsi_initq, scsi_initq);
> + simple_unlock(&scsibus_interlock);
> +
> + free(scsi_initq, M_DEVBUF);
> + wakeup(&scsi_initq_head);
> +
> + config_pending_decr();
> }
>
> int
> Index: sys/dev/scsipi/scsipi_base.c
> ===================================================================
> RCS file: /cvsroot/syssrc/sys/dev/scsipi/scsipi_base.c,v
> retrieving revision 1.78
> diff -u -r1.78 scsipi_base.c
> --- sys/dev/scsipi/scsipi_base.c 2002/07/26 14:11:34 1.78
> +++ sys/dev/scsipi/scsipi_base.c 2002/09/16 16:35:08
> @@ -2075,6 +2075,9 @@
> struct scsipi_xfer *xs;
> int s;
>
> + if (chan->chan_init_cb)
> + chan->chan_init_cb(chan, chan->chan_init_cb_arg);
> +
> s = splbio();
> chan->chan_flags |= SCSIPI_CHAN_TACTIVE;
> splx(s);
> Index: sys/dev/scsipi/scsipiconf.h
> ===================================================================
> RCS file: /cvsroot/syssrc/sys/dev/scsipi/scsipiconf.h,v
> retrieving revision 1.69
> diff -u -r1.69 scsipiconf.h
> --- sys/dev/scsipi/scsipiconf.h 2002/05/16 02:54:21 1.69
> +++ sys/dev/scsipi/scsipiconf.h 2002/09/16 16:35:08
> @@ -298,6 +298,10 @@
> /* callback we may have to call from completion thread */
> void (*chan_callback) __P((struct scsipi_channel *, void *));
> void *chan_callback_arg;
> +
> + /* callback we may have to call after forking the kthread */
> + void (*chan_init_cb) __P((struct scsipi_channel *, void *));
> + void *chan_init_cb_arg;
> };
>
> /* chan_flags */
>