Module Name: src
Committed By: jdolecek
Date: Fri Jun 23 20:40:51 UTC 2017
Modified Files:
src/sys/dev/ata [jdolecek-ncq]: TODO.ncq ata.c atavar.h wd.c wdvar.h
Log Message:
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)
To generate a diff of this commit:
cvs rdiff -u -r1.1.2.21 -r1.1.2.22 src/sys/dev/ata/TODO.ncq
cvs rdiff -u -r1.132.8.15 -r1.132.8.16 src/sys/dev/ata/ata.c
cvs rdiff -u -r1.92.8.14 -r1.92.8.15 src/sys/dev/ata/atavar.h
cvs rdiff -u -r1.428.2.19 -r1.428.2.20 src/sys/dev/ata/wd.c
cvs rdiff -u -r1.43.4.5 -r1.43.4.6 src/sys/dev/ata/wdvar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/ata/TODO.ncq
diff -u src/sys/dev/ata/TODO.ncq:1.1.2.21 src/sys/dev/ata/TODO.ncq:1.1.2.22
--- src/sys/dev/ata/TODO.ncq:1.1.2.21 Wed Jun 21 22:40:43 2017
+++ src/sys/dev/ata/TODO.ncq Fri Jun 23 20:40:51 2017
@@ -2,12 +2,15 @@ Bugs
----
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 @@ do proper NCQ error recovery (currently
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()
Index: src/sys/dev/ata/ata.c
diff -u src/sys/dev/ata/ata.c:1.132.8.15 src/sys/dev/ata/ata.c:1.132.8.16
--- src/sys/dev/ata/ata.c:1.132.8.15 Wed Jun 21 22:40:43 2017
+++ src/sys/dev/ata/ata.c Fri Jun 23 20:40:51 2017
@@ -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 @@ atacmd_toncq(struct ata_xfer *xfer, uint
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
+}
Index: src/sys/dev/ata/atavar.h
diff -u src/sys/dev/ata/atavar.h:1.92.8.14 src/sys/dev/ata/atavar.h:1.92.8.15
--- src/sys/dev/ata/atavar.h:1.92.8.14 Wed Jun 21 22:40:43 2017
+++ src/sys/dev/ata/atavar.h Fri Jun 23 20:40:51 2017
@@ -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 @@ struct ata_drive_datas {
#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_kill_active(struct ata_channel
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 *);
Index: src/sys/dev/ata/wd.c
diff -u src/sys/dev/ata/wd.c:1.428.2.19 src/sys/dev/ata/wd.c:1.428.2.20
--- src/sys/dev/ata/wd.c:1.428.2.19 Tue Jun 20 20:58:22 2017
+++ src/sys/dev/ata/wd.c Fri Jun 23 20:40:51 2017
@@ -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 wdioctlstrategy(struct buf *);
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 @@ wdattach(device_t parent, device_t self,
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 @@ wddetach(device_t self, int flags)
/* 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 @@ wdstrategy(struct buf *bp)
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 @@ done:
* 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 @@ wdstart(struct wd_softc *wd)
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 @@ wdstart(struct wd_softc *wd)
wdstart1(wd, bp, xfer);
}
+out:
mutex_exit(&wd->sc_lock);
}
@@ -739,9 +752,9 @@ wdstart1(struct wd_softc *wd, struct buf
}
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 @@ noerror: if ((xfer->c_bio.flags & ATA_CO
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 @@ wd_setcache(struct wd_softc *wd, int bit
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 @@ wd_standby(struct wd_softc *wd, int flag
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_flushcache(struct wd_softc *wd, int f
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 @@ wd_flushcache(struct wd_softc *wd, int f
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) {
if (xfer->c_ata_c.r_error == WDCE_ABRT) {
/* command not supported */
error = ENODEV;
- goto out;
+ goto out_xfer;
}
}
if (xfer->c_ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
@@ -1954,12 +1980,17 @@ wd_flushcache(struct wd_softc *wd, int f
aprint_error_dev(wd->sc_dev, "wd_flushcache: status=%s\n",
sbuf);
error = EIO;
- goto out;
+ goto out_xfer;
}
error = 0;
-out:
+out_xfer:
ata_free_xfer(wd->drvp->chnl_softc, xfer);
+
+out:
+ /* kick queue processing blocked while waiting for flush xfer */
+ ata_channel_start(wd->drvp->chnl_softc, wd->drvp->drive);
+
return error;
}
@@ -2023,6 +2054,7 @@ wd_trim(struct wd_softc *wd, int part, d
out:
ata_free_xfer(wd->drvp->chnl_softc, xfer);
+ ata_channel_start(wd->drvp->chnl_softc, wd->drvp->drive);
return error;
}
@@ -2233,6 +2265,8 @@ wdioctlstrategy(struct buf *bp)
out:
ata_free_xfer(wi->wi_softc->drvp->chnl_softc, xfer);
+ ata_channel_start(wi->wi_softc->drvp->chnl_softc,
+ wi->wi_softc->drvp->drive);
out2:
bp->b_error = error;
if (error)
Index: src/sys/dev/ata/wdvar.h
diff -u src/sys/dev/ata/wdvar.h:1.43.4.5 src/sys/dev/ata/wdvar.h:1.43.4.6
--- src/sys/dev/ata/wdvar.h:1.43.4.5 Sun Apr 23 01:21:04 2017
+++ src/sys/dev/ata/wdvar.h Fri Jun 23 20:40:51 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: wdvar.h,v 1.43.4.5 2017/04/23 01:21:04 jakllsch Exp $ */
+/* $NetBSD: wdvar.h,v 1.43.4.6 2017/06/23 20:40:51 jdolecek Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -58,6 +58,7 @@ struct wd_softc {
#define WDF_LBA 0x040 /* using LBA mode */
#define WDF_KLABEL 0x080 /* retain label after 'full' close */
#define WDF_LBA48 0x100 /* using 48-bit LBA mode */
+#define WDF_FLUSH_PEND 0x200 /* cache flush waits for free xfer */
uint64_t sc_capacity; /* full capacity of the device */
uint64_t sc_capacity512; /* ... in DEV_BSIZE blocks */
uint32_t sc_capacity28; /* capacity accessible with LBA28 commands */