mirror of https://github.com/torvalds/linux.git
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: 6f643c57d5 ("xfs: implement ->notify_failure() for XFS")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
9515572be6
commit
f4ed930379
|
|
@ -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;
|
||||
error = xfs_dax_translate_range(mp->m_ddev_targp, offset, len, &daddr,
|
||||
&bblen);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* 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;
|
||||
|
||||
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 = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue