Commit 71bddbcc authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: fix scrub and remount-ro protection when running scrub

While running a new fstest that races a readonly remount with scrub
running in repair mode, I observed the kernel tripping over debugging
assertions in the log quiesce code that were checking that the CIL was
empty.  When the sysadmin runs scrub in repair mode, the scrub code
allocates real transactions (with reservations) to change things, but
doesn't increment the superblock writers count to block a readonly
remount attempt while it is running.

We don't require the userspace caller to have a writable file descriptor
to run repairs, so we have to call mnt_want_write_file to obtain freeze
protection and increment the writers count.  It's ok to remove the call
to sb_start_write for the dry-run case because commit 8321ddb2
removed the behavior where scrub and fsfreeze fight over the buffer LRU.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 7d88329e
...@@ -149,9 +149,10 @@ xchk_probe( ...@@ -149,9 +149,10 @@ xchk_probe(
STATIC int STATIC int
xchk_teardown( xchk_teardown(
struct xfs_scrub *sc, struct xfs_scrub *sc,
struct xfs_inode *ip_in,
int error) int error)
{ {
struct xfs_inode *ip_in = XFS_I(file_inode(sc->file));
xchk_ag_free(sc, &sc->sa); xchk_ag_free(sc, &sc->sa);
if (sc->tp) { if (sc->tp) {
if (error == 0 && (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)) if (error == 0 && (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
...@@ -168,7 +169,8 @@ xchk_teardown( ...@@ -168,7 +169,8 @@ xchk_teardown(
xfs_irele(sc->ip); xfs_irele(sc->ip);
sc->ip = NULL; sc->ip = NULL;
} }
sb_end_write(sc->mp->m_super); if (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)
mnt_drop_write_file(sc->file);
if (sc->flags & XCHK_REAPING_DISABLED) if (sc->flags & XCHK_REAPING_DISABLED)
xchk_start_reaping(sc); xchk_start_reaping(sc);
if (sc->flags & XCHK_HAS_QUOTAOFFLOCK) { if (sc->flags & XCHK_HAS_QUOTAOFFLOCK) {
...@@ -456,19 +458,22 @@ static inline void xchk_postmortem(struct xfs_scrub *sc) ...@@ -456,19 +458,22 @@ static inline void xchk_postmortem(struct xfs_scrub *sc)
/* Dispatch metadata scrubbing. */ /* Dispatch metadata scrubbing. */
int int
xfs_scrub_metadata( xfs_scrub_metadata(
struct xfs_inode *ip, struct file *file,
struct xfs_scrub_metadata *sm) struct xfs_scrub_metadata *sm)
{ {
struct xfs_scrub sc = { struct xfs_scrub sc = {
.mp = ip->i_mount, .file = file,
.sm = sm, .sm = sm,
.sa = { .sa = {
.agno = NULLAGNUMBER, .agno = NULLAGNUMBER,
}, },
}; };
struct xfs_inode *ip = XFS_I(file_inode(file));
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
int error = 0; int error = 0;
sc.mp = mp;
BUILD_BUG_ON(sizeof(meta_scrub_ops) != BUILD_BUG_ON(sizeof(meta_scrub_ops) !=
(sizeof(struct xchk_meta_ops) * XFS_SCRUB_TYPE_NR)); (sizeof(struct xchk_meta_ops) * XFS_SCRUB_TYPE_NR));
...@@ -492,12 +497,14 @@ xfs_scrub_metadata( ...@@ -492,12 +497,14 @@ xfs_scrub_metadata(
sc.sick_mask = xchk_health_mask_for_scrub_type(sm->sm_type); sc.sick_mask = xchk_health_mask_for_scrub_type(sm->sm_type);
retry_op: retry_op:
/* /*
* If freeze runs concurrently with a scrub, the freeze can be delayed * When repairs are allowed, prevent freezing or readonly remount while
* indefinitely as we walk the filesystem and iterate over metadata * scrub is running with a real transaction.
* buffers. Freeze quiesces the log (which waits for the buffer LRU to
* be emptied) and that won't happen while checking is running.
*/ */
sb_start_write(mp->m_super); if (sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) {
error = mnt_want_write_file(sc.file);
if (error)
goto out;
}
/* Set up for the operation. */ /* Set up for the operation. */
error = sc.ops->setup(&sc, ip); error = sc.ops->setup(&sc, ip);
...@@ -512,7 +519,7 @@ xfs_scrub_metadata( ...@@ -512,7 +519,7 @@ xfs_scrub_metadata(
* Tear down everything we hold, then set up again with * Tear down everything we hold, then set up again with
* preparation for worst-case scenarios. * preparation for worst-case scenarios.
*/ */
error = xchk_teardown(&sc, ip, 0); error = xchk_teardown(&sc, 0);
if (error) if (error)
goto out; goto out;
sc.flags |= XCHK_TRY_HARDER; sc.flags |= XCHK_TRY_HARDER;
...@@ -553,7 +560,7 @@ xfs_scrub_metadata( ...@@ -553,7 +560,7 @@ xfs_scrub_metadata(
* get all the resources it needs; either way, we go * get all the resources it needs; either way, we go
* back to the beginning and call the scrub function. * back to the beginning and call the scrub function.
*/ */
error = xchk_teardown(&sc, ip, 0); error = xchk_teardown(&sc, 0);
if (error) { if (error) {
xrep_failure(mp); xrep_failure(mp);
goto out; goto out;
...@@ -565,7 +572,7 @@ xfs_scrub_metadata( ...@@ -565,7 +572,7 @@ xfs_scrub_metadata(
out_nofix: out_nofix:
xchk_postmortem(&sc); xchk_postmortem(&sc);
out_teardown: out_teardown:
error = xchk_teardown(&sc, ip, error); error = xchk_teardown(&sc, error);
out: out:
trace_xchk_done(ip, sm, error); trace_xchk_done(ip, sm, error);
if (error == -EFSCORRUPTED || error == -EFSBADCRC) { if (error == -EFSCORRUPTED || error == -EFSBADCRC) {
......
...@@ -59,7 +59,18 @@ struct xfs_scrub { ...@@ -59,7 +59,18 @@ struct xfs_scrub {
struct xfs_scrub_metadata *sm; struct xfs_scrub_metadata *sm;
const struct xchk_meta_ops *ops; const struct xchk_meta_ops *ops;
struct xfs_trans *tp; struct xfs_trans *tp;
/* File that scrub was called with. */
struct file *file;
/*
* File that is undergoing the scrub operation. This can differ from
* the file that scrub was called with if we're checking file-based fs
* metadata (e.g. rt bitmaps) or if we're doing a scrub-by-handle for
* something that can't be opened directly (e.g. symlinks).
*/
struct xfs_inode *ip; struct xfs_inode *ip;
void *buf; void *buf;
uint ilock_flags; uint ilock_flags;
......
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
#define __XFS_SCRUB_H__ #define __XFS_SCRUB_H__
#ifndef CONFIG_XFS_ONLINE_SCRUB #ifndef CONFIG_XFS_ONLINE_SCRUB
# define xfs_scrub_metadata(ip, sm) (-ENOTTY) # define xfs_scrub_metadata(file, sm) (-ENOTTY)
#else #else
int xfs_scrub_metadata(struct xfs_inode *ip, struct xfs_scrub_metadata *sm); int xfs_scrub_metadata(struct file *file, struct xfs_scrub_metadata *sm);
#endif /* CONFIG_XFS_ONLINE_SCRUB */ #endif /* CONFIG_XFS_ONLINE_SCRUB */
#endif /* __XFS_SCRUB_H__ */ #endif /* __XFS_SCRUB_H__ */
...@@ -1847,7 +1847,7 @@ xfs_ioc_getfsmap( ...@@ -1847,7 +1847,7 @@ xfs_ioc_getfsmap(
STATIC int STATIC int
xfs_ioc_scrub_metadata( xfs_ioc_scrub_metadata(
struct xfs_inode *ip, struct file *file,
void __user *arg) void __user *arg)
{ {
struct xfs_scrub_metadata scrub; struct xfs_scrub_metadata scrub;
...@@ -1859,7 +1859,7 @@ xfs_ioc_scrub_metadata( ...@@ -1859,7 +1859,7 @@ xfs_ioc_scrub_metadata(
if (copy_from_user(&scrub, arg, sizeof(scrub))) if (copy_from_user(&scrub, arg, sizeof(scrub)))
return -EFAULT; return -EFAULT;
error = xfs_scrub_metadata(ip, &scrub); error = xfs_scrub_metadata(file, &scrub);
if (error) if (error)
return error; return error;
...@@ -2158,7 +2158,7 @@ xfs_file_ioctl( ...@@ -2158,7 +2158,7 @@ xfs_file_ioctl(
return xfs_ioc_getfsmap(ip, arg); return xfs_ioc_getfsmap(ip, arg);
case XFS_IOC_SCRUB_METADATA: case XFS_IOC_SCRUB_METADATA:
return xfs_ioc_scrub_metadata(ip, arg); return xfs_ioc_scrub_metadata(filp, arg);
case XFS_IOC_FD_TO_HANDLE: case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE: case XFS_IOC_PATH_TO_HANDLE:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment