]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: don't shut down the filesystem for media failures beyond end of log
authorDarrick J. Wong <djwong@kernel.org>
Tue, 17 Dec 2024 21:43:06 +0000 (13:43 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Mon, 23 Dec 2024 21:06:09 +0000 (13:06 -0800)
If the filesystem has an external log device on pmem and the pmem
reports a media error beyond the end of the log area, don't shut down
the filesystem because we don't use that space.

Cc: <stable@vger.kernel.org> # v6.0
Fixes: 6f643c57d57c56 ("xfs: implement ->notify_failure() for XFS")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/xfs_notify_failure.c

index fa50e5308292d3eaf2e77b0aafef5848e5ed18fe..0b0b0f31aca2741c1e33377180a675de830ae976 100644 (file)
@@ -153,6 +153,79 @@ xfs_dax_notify_failure_thaw(
        thaw_super(sb, FREEZE_HOLDER_USERSPACE);
 }
 
+static int
+xfs_dax_translate_range(
+       struct xfs_buftarg      *btp,
+       u64                     offset,
+       u64                     len,
+       xfs_daddr_t             *daddr,
+       uint64_t                *bblen)
+{
+       u64                     dev_start = btp->bt_dax_part_off;
+       u64                     dev_len = bdev_nr_bytes(btp->bt_bdev);
+       u64                     dev_end = dev_start + dev_len - 1;
+
+       /* Notify failure on the whole device. */
+       if (offset == 0 && len == U64_MAX) {
+               offset = dev_start;
+               len = dev_len;
+       }
+
+       /* Ignore the range out of filesystem area */
+       if (offset + len - 1 < dev_start)
+               return -ENXIO;
+       if (offset > dev_end)
+               return -ENXIO;
+
+       /* Calculate the real range when it touches the boundary */
+       if (offset > dev_start)
+               offset -= dev_start;
+       else {
+               len -= dev_start - offset;
+               offset = 0;
+       }
+       if (offset + len - 1 > dev_end)
+               len = dev_end - offset + 1;
+
+       *daddr = BTOBB(offset);
+       *bblen = BTOBB(len);
+       return 0;
+}
+
+static int
+xfs_dax_notify_logdev_failure(
+       struct xfs_mount        *mp,
+       u64                     offset,
+       u64                     len,
+       int                     mf_flags)
+{
+       xfs_daddr_t             daddr;
+       uint64_t                bblen;
+       int                     error;
+
+       /*
+        * Return ENXIO instead of shutting down the filesystem if the failed
+        * region is beyond the end of the log.
+        */
+       error = xfs_dax_translate_range(mp->m_logdev_targp,
+                       offset, len, &daddr, &bblen);
+       if (error)
+               return error;
+
+       /*
+        * In the pre-remove case the failure notification is attempting to
+        * trigger a force unmount.  The expectation is that the device is
+        * still present, but its removal is in progress and can not be
+        * cancelled, proceed with accessing the log device.
+        */
+       if (mf_flags & MF_MEM_PRE_REMOVE)
+               return 0;
+
+       xfs_err(mp, "ondisk log corrupt, shutting down fs!");
+       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
+       return -EFSCORRUPTED;
+}
+
 static int
 xfs_dax_notify_ddev_failure(
        struct xfs_mount        *mp,
@@ -263,8 +336,9 @@ xfs_dax_notify_failure(
        int                     mf_flags)
 {
        struct xfs_mount        *mp = dax_holder(dax_dev);
-       u64                     ddev_start;
-       u64                     ddev_end;
+       xfs_daddr_t             daddr;
+       uint64_t                bblen;
+       int                     error;
 
        if (!(mp->m_super->s_flags & SB_BORN)) {
                xfs_warn(mp, "filesystem is not ready for notify_failure()!");
@@ -279,17 +353,7 @@ xfs_dax_notify_failure(
 
        if (mp->m_logdev_targp && mp->m_logdev_targp->bt_daxdev == dax_dev &&
            mp->m_logdev_targp != mp->m_ddev_targp) {
-               /*
-                * In the pre-remove case the failure notification is attempting
-                * to trigger a force unmount.  The expectation is that the
-                * device is still present, but its removal is in progress and
-                * can not be cancelled, proceed with accessing the log device.
-                */
-               if (mf_flags & MF_MEM_PRE_REMOVE)
-                       return 0;
-               xfs_err(mp, "ondisk log corrupt, shutting down fs!");
-               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_ONDISK);
-               return -EFSCORRUPTED;
+               return xfs_dax_notify_logdev_failure(mp, offset, len, mf_flags);
        }
 
        if (!xfs_has_rmapbt(mp)) {
@@ -297,33 +361,12 @@ xfs_dax_notify_failure(
                return -EOPNOTSUPP;
        }
 
-       ddev_start = mp->m_ddev_targp->bt_dax_part_off;
-       ddev_end = ddev_start + bdev_nr_bytes(mp->m_ddev_targp->bt_bdev) - 1;
-
-       /* Notify failure on the whole device. */
-       if (offset == 0 && len == U64_MAX) {
-               offset = ddev_start;
-               len = bdev_nr_bytes(mp->m_ddev_targp->bt_bdev);
-       }
-
-       /* Ignore the range out of filesystem area */
-       if (offset + len - 1 < ddev_start)
-               return -ENXIO;
-       if (offset > ddev_end)
-               return -ENXIO;
-
-       /* Calculate the real range when it touches the boundary */
-       if (offset > ddev_start)
-               offset -= ddev_start;
-       else {
-               len -= ddev_start - offset;
-               offset = 0;
-       }
-       if (offset + len - 1 > ddev_end)
-               len = ddev_end - offset + 1;
+       error = xfs_dax_translate_range(mp->m_ddev_targp, offset, len, &daddr,
+                       &bblen);
+       if (error)
+               return error;
 
-       return xfs_dax_notify_ddev_failure(mp, BTOBB(offset), BTOBB(len),
-                       mf_flags);
+       return xfs_dax_notify_ddev_failure(mp, daddr, bblen, mf_flags);
 }
 
 const struct dax_holder_operations xfs_dax_holder_operations = {