Source-Changes-HG archive

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

[src/jdolecek-ncq]: src/sys/dev/ata restart I/O processing after freeing xfer...



details:   https://anonhg.NetBSD.org/src/rev/daf5cd436c4a
branches:  jdolecek-ncq
changeset: 822938:daf5cd436c4a
user:      jdolecek <jdolecek%NetBSD.org@localhost>
date:      Fri Jun 23 20:40:51 2017 +0000

description:
restart I/O processing after freeing xfer, i.e. now even after commands
like cache flush or standby; the command handling no longer use on-stack xfer,
hence use queue slot and compete with normal I/O for the xfers

the restart give change to all drives attached to the same channel
in round-robin fashion, for fair usage and to recover from situation
when disk is idle due to all xfers being consumed by other drives

make special concession for flush cache - ignore any new I/O requests
on the particular disk while the flush cache is waiting for xfer,
so that I/O queue won't starve the flush cache and the flush cache would
be done ASAP

tested on piixide(4), ahci(4), siisata(4)

diffstat:

 sys/dev/ata/TODO.ncq |  18 +++++++++----
 sys/dev/ata/ata.c    |  48 ++++++++++++++++++++++++++++++++++++-
 sys/dev/ata/atavar.h |   6 +++-
 sys/dev/ata/wd.c     |  66 +++++++++++++++++++++++++++++++++++++++------------
 sys/dev/ata/wdvar.h  |   3 +-
 5 files changed, 114 insertions(+), 27 deletions(-)

diffs (truncated from 362 to 300 lines):

diff -r c16c0656cf26 -r daf5cd436c4a sys/dev/ata/TODO.ncq
--- a/sys/dev/ata/TODO.ncq      Wed Jun 21 22:40:43 2017 +0000
+++ b/sys/dev/ata/TODO.ncq      Fri Jun 23 20:40:51 2017 +0000
@@ -2,12 +2,15 @@
 ----
 
 fix crashdump for mvsata - request times out (maybe same as HEAD?)
+- HEAD crash due to KASSERT(chp->ch_queue->active_xfer != NULL)
 
-siisata - fix all new XXX and unmergable bits, fix PMP
+siisata - fix all new XXX and unmergable bits
 
 test crashdump with siisata
 - fails with recursive panic via pmap_kremove_local() regardless if
-  drive connected via PMP or direct
+  drive connected via PMP or direct (failed KASSERT(panicstr != NULL))
+- HEAD crashes same as branch
+- see kern/49610 for potential fix
 
 test wd* at umass?, confirm the ata_channel kludge works
 + add detach code (channel detach, free queue)
@@ -19,13 +22,16 @@
 maybe do device error handling in not-interrupt-context (maybe this should be
 done on a mpata branch?)
 
-add mechanics to re-check queue when xfer is finished - needed on PMP
-and for IDE disks, when one drive uses up all available xfers nothing
-ever restarts queue for the other drives
+do not limit openings for xfers for non-NCQ drives in wdstart(), the tag
+will not be used so can use any xfer
+
+in atastart(), restrict NCQ commands to commands for the same drive? it's
+fine for fis-based switching to have outstanding for several drives, but
+not non-FIS
 
 Other random notes (do outside the NCQ branch):
 -----------------------------------------------------
-change wd(4) to use dk_open()
+change wd(4) to use dksubr
 
 dump to unopened disk fails (e.g. dump do wd1b when wd1a not mounted), due
 to the open path executing ata_get_params(), which eventually tsleeps()
diff -r c16c0656cf26 -r daf5cd436c4a sys/dev/ata/ata.c
--- a/sys/dev/ata/ata.c Wed Jun 21 22:40:43 2017 +0000
+++ b/sys/dev/ata/ata.c Fri Jun 23 20:40:51 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ata.c,v 1.132.8.15 2017/06/21 22:40:43 jdolecek Exp $  */
+/*     $NetBSD: ata.c,v 1.132.8.16 2017/06/23 20:40:51 jdolecek Exp $  */
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.15 2017/06/21 22:40:43 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.132.8.16 2017/06/23 20:40:51 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -2143,3 +2143,47 @@
        if (xfer->c_bio.flags & ATA_FUA)
                *device |= WDSD_FUA;
 }
+
+/*
+ * Must be called without any locks, i.e. with both drive and channel locks
+ * released.
+ */ 
+void
+ata_channel_start(struct ata_channel *chp, int drive)
+{
+       int i;
+       struct ata_drive_datas *drvp;
+
+       KASSERT(chp->ch_ndrives > 0);
+
+#define ATA_DRIVE_START(chp, drive) \
+       do {                                                    \
+               KASSERT(drive < chp->ch_ndrives);               \
+               drvp = &chp->ch_drive[drive];                   \
+                                                               \
+               if (drvp->drive_type != ATA_DRIVET_ATA &&       \
+                   drvp->drive_type != ATA_DRIVET_ATAPI &&     \
+                   drvp->drive_type != ATA_DRIVET_OLD)         \
+                       continue;                               \
+                                                               \
+               if (drvp->drv_start != NULL)                    \
+                       (*drvp->drv_start)(drvp->drv_softc);    \
+       } while (0)
+
+       /*
+        * Process drives in round robin fashion starting with next one after
+        * the one which finished transfer. Thus no single drive would
+        * completely starve other drives on same channel. 
+        * This loop processes all but the current drive, so won't do anything
+        * if there is only one drive in channel.
+        */
+       for (i = (drive + 1) % chp->ch_ndrives; i != drive;
+           i = (i + 1) % chp->ch_ndrives) {
+               ATA_DRIVE_START(chp, i);
+       }
+
+       /* Now try to kick off xfers on the current drive */
+       ATA_DRIVE_START(chp, drive);
+
+#undef ATA_DRIVE_START
+}
diff -r c16c0656cf26 -r daf5cd436c4a sys/dev/ata/atavar.h
--- a/sys/dev/ata/atavar.h      Wed Jun 21 22:40:43 2017 +0000
+++ b/sys/dev/ata/atavar.h      Fri Jun 23 20:40:51 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: atavar.h,v 1.92.8.14 2017/06/21 22:40:43 jdolecek Exp $        */
+/*     $NetBSD: atavar.h,v 1.92.8.15 2017/06/23 20:40:51 jdolecek Exp $        */
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -299,7 +299,8 @@
 #endif
 
        /* Callbacks into the drive's driver. */
-       void    (*drv_done)(void *, struct ata_xfer *); /* transfer is done */
+       void    (*drv_done)(device_t, struct ata_xfer *); /* xfer is done */
+       void    (*drv_start)(device_t);                   /* start queue */
 
        device_t drv_softc;             /* ATA drives softc, if any */
        struct ata_channel *chnl_softc; /* channel softc */
@@ -499,6 +500,7 @@
 void   ata_reset_channel(struct ata_channel *, int);
 void   ata_channel_freeze(struct ata_channel *);
 void   ata_channel_thaw(struct ata_channel *);
+void   ata_channel_start(struct ata_channel *, int);
 
 int    ata_addref(struct ata_channel *);
 void   ata_delref(struct ata_channel *);
diff -r c16c0656cf26 -r daf5cd436c4a sys/dev/ata/wd.c
--- a/sys/dev/ata/wd.c  Wed Jun 21 22:40:43 2017 +0000
+++ b/sys/dev/ata/wd.c  Fri Jun 23 20:40:51 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: wd.c,v 1.428.2.19 2017/06/20 20:58:22 jdolecek Exp $ */
+/*     $NetBSD: wd.c,v 1.428.2.20 2017/06/23 20:40:51 jdolecek Exp $ */
 
 /*
  * Copyright (c) 1998, 2001 Manuel Bouyer.  All rights reserved.
@@ -54,7 +54,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.428.2.19 2017/06/20 20:58:22 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.428.2.20 2017/06/23 20:40:51 jdolecek Exp $");
 
 #include "opt_ata.h"
 
@@ -192,10 +192,10 @@
 
 void  wdgetdefaultlabel(struct wd_softc *, struct disklabel *);
 void  wdgetdisklabel(struct wd_softc *);
-void  wdstart(struct wd_softc *);
+void  wdstart(device_t);
 void  wdstart1(struct wd_softc *, struct buf *, struct ata_xfer *);
 void  wdrestart(void *);
-void  wddone(void *, struct ata_xfer *);
+void  wddone(device_t, struct ata_xfer *);
 static void wd_params_to_properties(struct wd_softc *);
 int   wd_get_params(struct wd_softc *, uint8_t, struct ataparams *);
 int   wd_flushcache(struct wd_softc *, int);
@@ -310,6 +310,7 @@
        wd->drvp = adev->adev_drv_data;
 
        wd->drvp->drv_openings = 1;
+       wd->drvp->drv_start = wdstart;
        wd->drvp->drv_done = wddone;
        wd->drvp->drv_softc = wd->sc_dev; /* done in atabusconfig_thread()
                                             but too late */
@@ -518,6 +519,7 @@
        /* Unhook the entropy source. */
        rnd_detach_source(&sc->rnd_source);
 
+       mutex_destroy(&sc->sc_lock);
        callout_destroy(&sc->sc_restart_ch);
 
        sc->drvp->drive_type = ATA_DRIVET_NONE; /* no drive any more here */
@@ -620,7 +622,8 @@
        bufq_put(wd->sc_q, bp);
        mutex_exit(&wd->sc_lock);
 
-       wdstart(wd);
+       /* Try to queue on the current drive only */
+       wdstart(wd->sc_dev);
        return;
 done:
        /* Toss transfer; we're done early. */
@@ -632,8 +635,9 @@
  * Queue a drive for I/O.
  */
 void
-wdstart(struct wd_softc *wd)
+wdstart(device_t self)
 {
+       struct wd_softc *wd = device_private(self);
        struct buf *bp;
        struct ata_xfer *xfer;
 
@@ -645,11 +649,19 @@
 
        mutex_enter(&wd->sc_lock);
 
+       /*
+        * Do not queue any transfers until flush is finished, so that
+        * once flush is pending, it will get handled as soon as xfer
+        * is available.
+        */
+       if (ISSET(wd->sc_flags, WDF_FLUSH_PEND))
+               goto out;
+
        while (bufq_peek(wd->sc_q) != NULL) {
                /* First try to get command */
                xfer = ata_get_xfer_ext(wd->drvp->chnl_softc, false,
                    wd->drvp->drv_openings);
-               if (xfer == NULL) 
+               if (xfer == NULL)
                        break;
 
                /* There is got to be a buf for us */
@@ -660,6 +672,7 @@
                wdstart1(wd, bp, xfer);
        }
 
+out:
        mutex_exit(&wd->sc_lock);
 }
 
@@ -739,9 +752,9 @@
 }
 
 void
-wddone(void *v, struct ata_xfer *xfer)
+wddone(device_t self, struct ata_xfer *xfer)
 {
-       struct wd_softc *wd = device_private(v);
+       struct wd_softc *wd = device_private(self);
        const char *errmsg;
        int do_perror = 0;
        struct buf *bp;
@@ -854,7 +867,7 @@
        ata_free_xfer(wd->drvp->chnl_softc, xfer);
        mutex_exit(&wd->sc_lock);
        biodone(bp);
-       wdstart(wd);
+       ata_channel_start(wd->drvp->chnl_softc, wd->drvp->drive);
 }
 
 void
@@ -1861,6 +1874,7 @@
 
 out:
        ata_free_xfer(wd->drvp->chnl_softc, xfer);
+       ata_channel_start(wd->drvp->chnl_softc, wd->drvp->drive);
        return error;
 }
 
@@ -1903,6 +1917,7 @@
 
 out:
        ata_free_xfer(wd->drvp->chnl_softc, xfer);
+       ata_channel_start(wd->drvp->chnl_softc, wd->drvp->drive);
        return error;
 }
 
@@ -1921,9 +1936,20 @@
            wd->sc_params.atap_cmd_set2 == 0xffff))
                return ENODEV;
 
+       mutex_enter(&wd->sc_lock);
+       SET(wd->sc_flags, WDF_FLUSH_PEND);
+       mutex_exit(&wd->sc_lock);
+
        xfer = ata_get_xfer(wd->drvp->chnl_softc);
-       if (xfer == NULL)
-               return EINTR;
+
+       mutex_enter(&wd->sc_lock);
+       CLR(wd->sc_flags, WDF_FLUSH_PEND);
+       mutex_exit(&wd->sc_lock);
+
+       if (xfer == NULL) {
+               error = EINTR;
+               goto out;
+       }
 
        if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0 &&
            (wd->sc_params.atap_cmd2_en & ATA_CMD2_FCE) != 0) {
@@ -1939,13 +1965,13 @@
                aprint_error_dev(wd->sc_dev,
                    "flush cache command didn't complete\n");
                error = EIO;
-               goto out;
+               goto out_xfer;
        }
        if (xfer->c_ata_c.flags & AT_ERROR) {



Home | Main Index | Thread Index | Old Index