Commit f4ed9303 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: don't shut down the filesystem for media failures beyond end of log



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: 6f643c57 ("xfs: implement ->notify_failure() for XFS")
Signed-off-by: default avatar"Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 9515572b
Loading
Loading
Loading
Loading
+82 −39
Original line number Diff line number Diff line
@@ -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 = {