aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/scrub/scrub.c31
-rw-r--r--fs/xfs/scrub/scrub.h11
-rw-r--r--fs/xfs/scrub/xfs_scrub.h4
-rw-r--r--fs/xfs/xfs_ioctl.c6
4 files changed, 35 insertions, 17 deletions
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index 47c68c72bcac..21ebd3f4af9f 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -149,9 +149,10 @@ xchk_probe(
STATIC int
xchk_teardown(
struct xfs_scrub *sc,
- struct xfs_inode *ip_in,
int error)
{
+ struct xfs_inode *ip_in = XFS_I(file_inode(sc->file));
+
xchk_ag_free(sc, &sc->sa);
if (sc->tp) {
if (error == 0 && (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR))
@@ -168,7 +169,8 @@ xchk_teardown(
xfs_irele(sc->ip);
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)
xchk_start_reaping(sc);
if (sc->flags & XCHK_HAS_QUOTAOFFLOCK) {
@@ -456,19 +458,22 @@ static inline void xchk_postmortem(struct xfs_scrub *sc)
/* Dispatch metadata scrubbing. */
int
xfs_scrub_metadata(
- struct xfs_inode *ip,
+ struct file *file,
struct xfs_scrub_metadata *sm)
{
struct xfs_scrub sc = {
- .mp = ip->i_mount,
+ .file = file,
.sm = sm,
.sa = {
.agno = NULLAGNUMBER,
},
};
+ struct xfs_inode *ip = XFS_I(file_inode(file));
struct xfs_mount *mp = ip->i_mount;
int error = 0;
+ sc.mp = mp;
+
BUILD_BUG_ON(sizeof(meta_scrub_ops) !=
(sizeof(struct xchk_meta_ops) * XFS_SCRUB_TYPE_NR));
@@ -492,12 +497,14 @@ xfs_scrub_metadata(
sc.sick_mask = xchk_health_mask_for_scrub_type(sm->sm_type);
retry_op:
/*
- * If freeze runs concurrently with a scrub, the freeze can be delayed
- * indefinitely as we walk the filesystem and iterate over metadata
- * buffers. Freeze quiesces the log (which waits for the buffer LRU to
- * be emptied) and that won't happen while checking is running.
+ * When repairs are allowed, prevent freezing or readonly remount while
+ * scrub is running with a real transaction.
*/
- 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. */
error = sc.ops->setup(&sc, ip);
@@ -512,7 +519,7 @@ retry_op:
* Tear down everything we hold, then set up again with
* preparation for worst-case scenarios.
*/
- error = xchk_teardown(&sc, ip, 0);
+ error = xchk_teardown(&sc, 0);
if (error)
goto out;
sc.flags |= XCHK_TRY_HARDER;
@@ -553,7 +560,7 @@ retry_op:
* get all the resources it needs; either way, we go
* back to the beginning and call the scrub function.
*/
- error = xchk_teardown(&sc, ip, 0);
+ error = xchk_teardown(&sc, 0);
if (error) {
xrep_failure(mp);
goto out;
@@ -565,7 +572,7 @@ retry_op:
out_nofix:
xchk_postmortem(&sc);
out_teardown:
- error = xchk_teardown(&sc, ip, error);
+ error = xchk_teardown(&sc, error);
out:
trace_xchk_done(ip, sm, error);
if (error == -EFSCORRUPTED || error == -EFSBADCRC) {
diff --git a/fs/xfs/scrub/scrub.h b/fs/xfs/scrub/scrub.h
index ad1ceb44a628..e776ab4ad322 100644
--- a/fs/xfs/scrub/scrub.h
+++ b/fs/xfs/scrub/scrub.h
@@ -59,7 +59,18 @@ struct xfs_scrub {
struct xfs_scrub_metadata *sm;
const struct xchk_meta_ops *ops;
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;
+
void *buf;
uint ilock_flags;
diff --git a/fs/xfs/scrub/xfs_scrub.h b/fs/xfs/scrub/xfs_scrub.h
index 2897ba3a17e6..2ceae614ade8 100644
--- a/fs/xfs/scrub/xfs_scrub.h
+++ b/fs/xfs/scrub/xfs_scrub.h
@@ -7,9 +7,9 @@
#define __XFS_SCRUB_H__
#ifndef CONFIG_XFS_ONLINE_SCRUB
-# define xfs_scrub_metadata(ip, sm) (-ENOTTY)
+# define xfs_scrub_metadata(file, sm) (-ENOTTY)
#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 /* __XFS_SCRUB_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index e6e4e248cd86..708b77341a70 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1847,7 +1847,7 @@ out_free:
STATIC int
xfs_ioc_scrub_metadata(
- struct xfs_inode *ip,
+ struct file *file,
void __user *arg)
{
struct xfs_scrub_metadata scrub;
@@ -1859,7 +1859,7 @@ xfs_ioc_scrub_metadata(
if (copy_from_user(&scrub, arg, sizeof(scrub)))
return -EFAULT;
- error = xfs_scrub_metadata(ip, &scrub);
+ error = xfs_scrub_metadata(file, &scrub);
if (error)
return error;
@@ -2158,7 +2158,7 @@ xfs_file_ioctl(
return xfs_ioc_getfsmap(ip, arg);
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_PATH_TO_HANDLE: