Add an ioctl to tell storage devices to flush their internal caches
authorsf <sf@openbsd.org>
Mon, 29 May 2017 14:05:31 +0000 (14:05 +0000)
committersf <sf@openbsd.org>
Mon, 29 May 2017 14:05:31 +0000 (14:05 +0000)
Currently implemented for wd and sd.
Initially ported from netbsd by pedro@

ok deraadt@

sys/dev/ata/wd.c
sys/scsi/sd.c
sys/sys/dkio.h

index cd16822..a6dc2c5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: wd.c,v 1.121 2017/05/04 22:47:27 deraadt Exp $ */
+/*     $OpenBSD: wd.c,v 1.122 2017/05/29 14:05:31 sf Exp $ */
 /*     $NetBSD: wd.c,v 1.193 1999/02/28 17:15:27 explorer Exp $ */
 
 /*
@@ -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,14 @@ wdioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p)
                }
 #endif
 
+       case DIOCCACHESYNC:
+               if ((flag & FWRITE) == 0) {
+                       error = EBADF;
+                       goto exit;
+               }
+               error = wd_flushcache(wd, AT_WAIT);
+               goto exit;
+
        default:
                error = wdc_ioctl(wd->drvp, xfer, addr, flag, p);
                goto exit;
@@ -1067,13 +1075,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 +1096,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
index bb93ca1..0a26c87 100644 (file)
@@ -1,8 +1,8 @@
-/*     $OpenBSD: sd.c,v 1.271 2017/05/29 07:47:13 krw Exp $    */
+/*     $OpenBSD: sd.c,v 1.272 2017/05/29 14:05:31 sf Exp $     */
 /*     $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);
 
@@ -989,6 +989,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;
@@ -1866,19 +1875,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
@@ -1889,7 +1899,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;
@@ -1899,10 +1909,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);
 }
index c774e19..300a9a7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: dkio.h,v 1.9 2011/06/05 18:40:33 matthew Exp $        */
+/*     $OpenBSD: dkio.h,v 1.10 2017/05/29 14:05:31 sf Exp $    */
 /*     $NetBSD: dkio.h,v 1.1 1996/01/30 18:21:48 thorpej Exp $ */
 
 /*
@@ -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_ */