]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
libxfs: flush all dirty buffers and report errors when unmounting filesystem
authorDarrick J. Wong <darrick.wong@oracle.com>
Sun, 1 Mar 2020 17:33:38 +0000 (12:33 -0500)
committerEric Sandeen <sandeen@sandeen.net>
Sun, 1 Mar 2020 17:33:38 +0000 (12:33 -0500)
Teach libxfs_umount to flush all dirty buffers when unmounting the
filesystem, to log write verifier errors and IO errors, and to return an
error code when things go wrong.  Subsequent patches will teach critical
utilities to exit with EXIT_FAILURE when this happens.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
include/xfs_mount.h
libxfs/init.c
libxfs/libxfs_io.h
libxfs/rdwr.c

index 29b3cc1b24ad35e240b259ede5151890bf3610b6..7bd23fbbe50f305a98672f55e8f8f747f67ec6d0 100644 (file)
@@ -184,7 +184,7 @@ xfs_perag_resv(
 
 extern xfs_mount_t     *libxfs_mount (xfs_mount_t *, xfs_sb_t *,
                                dev_t, dev_t, dev_t, int);
-extern void    libxfs_umount (xfs_mount_t *);
+int            libxfs_umount(struct xfs_mount *mp);
 extern void    libxfs_rtmount_destroy (xfs_mount_t *);
 
 #endif /* __XFS_MOUNT_H__ */
index 3c3063fc773473bfe12b2a264e15846705d3a635..42d4c8e47d37c0721240ca74c2c2cb9eeebff90e 100644 (file)
@@ -581,6 +581,8 @@ libxfs_buftarg_alloc(
        }
        btp->bt_mount = mp;
        btp->dev = dev;
+       btp->flags = 0;
+
        return btp;
 }
 
@@ -803,17 +805,104 @@ libxfs_rtmount_destroy(xfs_mount_t *mp)
        mp->m_rsumip = mp->m_rbmip = NULL;
 }
 
+/* Flush a device and report on writes that didn't make it to stable storage. */
+static inline int
+libxfs_flush_buftarg(
+       struct xfs_buftarg      *btp,
+       const char              *buftarg_descr)
+{
+       int                     error = 0;
+       int                     err2;
+
+       /*
+        * Write verifier failures are evidence of a buggy program.  Make sure
+        * that this state is always reported to the caller.
+        */
+       if (btp->flags & XFS_BUFTARG_CORRUPT_WRITE) {
+               fprintf(stderr,
+_("%s: Refusing to write a corrupt buffer to the %s!\n"),
+                               progname, buftarg_descr);
+               error = -EFSCORRUPTED;
+       }
+
+       if (btp->flags & XFS_BUFTARG_LOST_WRITE) {
+               fprintf(stderr,
+_("%s: Lost a write to the %s!\n"),
+                               progname, buftarg_descr);
+               if (!error)
+                       error = -EIO;
+       }
+
+       err2 = libxfs_blkdev_issue_flush(btp);
+       if (err2) {
+               fprintf(stderr,
+_("%s: Flushing the %s failed, err=%d!\n"),
+                               progname, buftarg_descr, -err2);
+       }
+       if (!error)
+               error = err2;
+
+       return error;
+}
+
+/*
+ * Flush all dirty buffers to stable storage and report on writes that didn't
+ * make it to stable storage.
+ */
+static int
+libxfs_flush_mount(
+       struct xfs_mount        *mp)
+{
+       int                     error = 0;
+       int                     err2;
+
+       /*
+        * Purge the buffer cache to write all dirty buffers to disk and free
+        * all incore buffers.  Buffers that fail write verification will cause
+        * the CORRUPT_WRITE flag to be set in the buftarg.  Buffers that
+        * cannot be written will cause the LOST_WRITE flag to be set in the
+        * buftarg.
+        */
+       libxfs_bcache_purge();
+
+       /* Flush all kernel and disk write caches, and report failures. */
+       if (mp->m_ddev_targp) {
+               err2 = libxfs_flush_buftarg(mp->m_ddev_targp, _("data device"));
+               if (!error)
+                       error = err2;
+       }
+
+       if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp) {
+               err2 = libxfs_flush_buftarg(mp->m_logdev_targp,
+                               _("log device"));
+               if (!error)
+                       error = err2;
+       }
+
+       if (mp->m_rtdev_targp) {
+               err2 = libxfs_flush_buftarg(mp->m_rtdev_targp,
+                               _("realtime device"));
+               if (!error)
+                       error = err2;
+       }
+
+       return error;
+}
+
 /*
  * Release any resource obtained during a mount.
  */
-void
-libxfs_umount(xfs_mount_t *mp)
+int
+libxfs_umount(
+       struct xfs_mount        *mp)
 {
        struct xfs_perag        *pag;
        int                     agno;
+       int                     error;
 
        libxfs_rtmount_destroy(mp);
-       libxfs_bcache_purge();
+
+       error = libxfs_flush_mount(mp);
 
        for (agno = 0; agno < mp->m_maxagi; agno++) {
                pag = radix_tree_delete(&mp->m_perag_tree, agno);
@@ -828,6 +917,7 @@ libxfs_umount(xfs_mount_t *mp)
                kmem_free(mp->m_logdev_targp);
        kmem_free(mp->m_ddev_targp);
 
+       return error;
 }
 
 /*
index 579df52bfef2aa99c1572cb3a3fcf1f062cce3ee..6bb75a671508a9672304cee9a71c24d6a2185ccf 100644 (file)
@@ -23,10 +23,17 @@ struct xfs_perag;
 struct xfs_buftarg {
        struct xfs_mount        *bt_mount;
        dev_t                   dev;
+       unsigned int            flags;
 };
 
+/* We purged a dirty buffer and lost a write. */
+#define XFS_BUFTARG_LOST_WRITE         (1 << 0)
+/* A dirty buffer failed the write verifier. */
+#define XFS_BUFTARG_CORRUPT_WRITE      (1 << 1)
+
 extern void    libxfs_buftarg_init(struct xfs_mount *mp, dev_t ddev,
                                    dev_t logdev, dev_t rtdev);
+int libxfs_blkdev_issue_flush(struct xfs_buftarg *btp);
 
 #define LIBXFS_BBTOOFF64(bbs)  (((xfs_off_t)(bbs)) << BBSHIFT)
 
index 4c021316ab9682adb6b03862ff94037708aa7b7a..737e51d1f04f96cacf808640acdd2eec3de72000 100644 (file)
@@ -17,6 +17,7 @@
 #include "xfs_inode_fork.h"
 #include "xfs_inode.h"
 #include "xfs_trans.h"
+#include "libfrog/platform.h"
 
 #include "libxfs.h"            /* for LIBXFS_EXIT_ON_FAILURE */
 
@@ -1226,6 +1227,19 @@ libxfs_iomove(xfs_buf_t *bp, uint boff, int len, void *data, int flags)
        }
 }
 
+/* Complain about (and remember) dropping dirty buffers. */
+static void
+libxfs_whine_dirty_buf(
+       struct xfs_buf          *bp)
+{
+       fprintf(stderr, _("%s: Releasing dirty buffer to free list!\n"),
+                       progname);
+
+       if (bp->b_error == -EFSCORRUPTED)
+               bp->b_target->flags |= XFS_BUFTARG_CORRUPT_WRITE;
+       bp->b_target->flags |= XFS_BUFTARG_LOST_WRITE;
+}
+
 static void
 libxfs_brelse(
        struct cache_node       *node)
@@ -1235,8 +1249,7 @@ libxfs_brelse(
        if (!bp)
                return;
        if (bp->b_flags & LIBXFS_B_DIRTY)
-               fprintf(stderr,
-                       "releasing dirty buffer to free list!\n");
+               libxfs_whine_dirty_buf(bp);
 
        pthread_mutex_lock(&xfs_buf_freelist.cm_mutex);
        list_add(&bp->b_node.cn_mru, &xfs_buf_freelist.cm_list);
@@ -1256,8 +1269,7 @@ libxfs_bulkrelse(
 
        list_for_each_entry(bp, list, b_node.cn_mru) {
                if (bp->b_flags & LIBXFS_B_DIRTY)
-                       fprintf(stderr,
-                               "releasing dirty buffer (bulk) to free list!\n");
+                       libxfs_whine_dirty_buf(bp);
                count++;
        }
 
@@ -1486,6 +1498,24 @@ libxfs_irele(
        kmem_cache_free(xfs_inode_zone, ip);
 }
 
+/*
+ * Flush everything dirty in the kernel and disk write caches to stable media.
+ * Returns 0 for success or a negative error code.
+ */
+int
+libxfs_blkdev_issue_flush(
+       struct xfs_buftarg      *btp)
+{
+       int                     fd, ret;
+
+       if (btp->dev == 0)
+               return 0;
+
+       fd = libxfs_device_to_fd(btp->dev);
+       ret = platform_flush_device(fd, btp->dev);
+       return ret ? -errno : 0;
+}
+
 /*
  * Write out a buffer list synchronously.
  *