On Mon, 17 Oct 2016, Alexander Bluhm wrote:
> > Part 1: Add an ioctl:
> >
> >
> > add DIOCCACHESYNC
> >
> > Add an ioctl to tell storage devices to flush their internal caches.
> > Ported from netbsd by pedro@
> >
> > OK?
>
> Some remarks inline
Sorry for the long delay, I was very busy in the last weeks.
I agree with all your comments (and should have reviewed the initial patch
better). Here is an updated diff.
Cheers,
Stefan
diff --git sys/dev/ata/wd.c sys/dev/ata/wd.c
index 5e2461f..a644cbf 100644
--- sys/dev/ata/wd.c
+++ sys/dev/ata/wd.c
@@ -26,7 +26,7 @@
*/
/*-
- * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -138,7 +138,7 @@ void wdstart(void *);
void __wdstart(struct wd_softc*, struct buf *);
void wdrestart(void *);
int wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *);
-void wd_flushcache(struct wd_softc *, int);
+int wd_flushcache(struct wd_softc *, int);
void wd_standby(struct wd_softc *, int);
/* XXX: these should go elsewhere */
@@ -848,6 +848,10 @@ wdioctl(dev_t dev, u_long xfer, caddr_t addr, int flag,
struct proc *p)
}
#endif
+ case DIOCCACHESYNC:
+ error = wd_flushcache(wd, AT_WAIT);
+ goto exit;
+
default:
error = wdc_ioctl(wd->drvp, xfer, addr, flag, p);
goto exit;
@@ -1067,13 +1071,13 @@ wd_get_params(struct wd_softc *wd, u_int8_t flags,
struct ataparams *params)
}
}
-void
+int
wd_flushcache(struct wd_softc *wd, int flags)
{
struct wdc_command wdc_c;
if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */
- return;
+ return EIO;
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = (wd->sc_flags & WDF_LBA48 ? WDCC_FLUSHCACHE_EXT :
WDCC_FLUSHCACHE);
@@ -1088,20 +1092,28 @@ wd_flushcache(struct wd_softc *wd, int flags)
if (wdc_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
printf("%s: flush cache command didn't complete\n",
wd->sc_dev.dv_xname);
+ return EIO;
}
+ if (wdc_c.flags & ERR_NODEV)
+ return ENODEV;
if (wdc_c.flags & AT_TIMEOU) {
printf("%s: flush cache command timeout\n",
wd->sc_dev.dv_xname);
+ return EIO;
+ }
+ if (wdc_c.flags & AT_ERROR) {
+ if (wdc_c.r_error == WDCE_ABRT) /* command not supported */
+ return ENODEV;
+ printf("%s: flush cache command: error 0x%x\n",
+ wd->sc_dev.dv_xname, wdc_c.r_error);
+ return EIO;
}
if (wdc_c.flags & AT_DF) {
printf("%s: flush cache command: drive fault\n",
wd->sc_dev.dv_xname);
+ return EIO;
}
- /*
- * Ignore error register, it shouldn't report anything else
- * than COMMAND ABORTED, which means the device doesn't support
- * flush cache
- */
+ return 0;
}
void
diff --git sys/scsi/sd.c sys/scsi/sd.c
index 30fb36b..e5a0139 100644
--- sys/scsi/sd.c
+++ sys/scsi/sd.c
@@ -2,7 +2,7 @@
/* $NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $ */
/*-
- * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * Copyright (c) 1998, 2003, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@@ -96,7 +96,7 @@ int sd_vpd_block_limits(struct sd_softc *, int);
int sd_vpd_thin(struct sd_softc *, int);
int sd_thin_params(struct sd_softc *, int);
int sd_get_parms(struct sd_softc *, struct disk_parms *, int);
-void sd_flush(struct sd_softc *, int);
+int sd_flush(struct sd_softc *, int);
void viscpy(u_char *, u_char *, int);
@@ -999,6 +999,15 @@ sdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag,
struct proc *p)
error = sd_ioctl_cache(sc, cmd, (struct dk_cache *)addr);
goto exit;
+ case DIOCCACHESYNC:
+ if (!ISSET(flag, FWRITE)) {
+ error = EBADF;
+ goto exit;
+ }
+ if ((sc->flags & SDF_DIRTY) != 0 || *(int *)addr != 0)
+ error = sd_flush(sc, 0);
+ goto exit;
+
default:
if (part != RAW_PART) {
error = ENOTTY;
@@ -1876,19 +1885,20 @@ die:
return (SDGP_RESULT_OFFLINE);
}
-void
+int
sd_flush(struct sd_softc *sc, int flags)
{
struct scsi_link *link;
struct scsi_xfer *xs;
struct scsi_synchronize_cache *cmd;
+ int error;
if (sc->flags & SDF_DYING)
- return;
+ return (ENXIO);
link = sc->sc_link;
if (link->quirks & SDEV_NOSYNCCACHE)
- return;
+ return (0);
/*
* Issue a SYNCHRONIZE CACHE. Address 0, length 0 means "all remaining
@@ -1899,7 +1909,7 @@ sd_flush(struct sd_softc *sc, int flags)
xs = scsi_xs_get(link, flags);
if (xs == NULL) {
SC_DEBUG(link, SDEV_DB1, ("cache sync failed to get xs\n"));
- return;
+ return (EIO);
}
cmd = (struct scsi_synchronize_cache *)xs->cmd;
@@ -1909,10 +1919,14 @@ sd_flush(struct sd_softc *sc, int flags)
xs->timeout = 100000;
xs->flags |= SCSI_IGNORE_ILLEGAL_REQUEST;
- if (scsi_xs_sync(xs) == 0)
- sc->flags &= ~SDF_DIRTY;
- else
- SC_DEBUG(link, SDEV_DB1, ("cache sync failed\n"));
+ error = scsi_xs_sync(xs);
scsi_xs_put(xs);
+
+ if (error)
+ SC_DEBUG(link, SDEV_DB1, ("cache sync failed\n"));
+ else
+ sc->flags &= ~SDF_DIRTY;
+
+ return (error);
}
diff --git sys/sys/dkio.h sys/sys/dkio.h
index c774e19..0a373af 100644
--- sys/sys/dkio.h
+++ sys/sys/dkio.h
@@ -83,4 +83,6 @@ struct dk_diskmap {
#define DIOCMAP _IOWR('d', 119, struct dk_diskmap)
+#define DIOCCACHESYNC _IOW('d', 120, int) /* sync cache (force?)
*/
+
#endif /* _SYS_DKIO_H_ */