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 */