From: Darrick J. Wong <darrick.wong@xxxxxxxxxx> Use a workqueue thread to call xfile io operations because lockdep complains when we freeze the filesystem. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- fs/xfs/scrub/array.c | 20 ++++++----- fs/xfs/scrub/array.h | 1 + fs/xfs/scrub/blob.c | 16 ++++++--- fs/xfs/scrub/blob.h | 1 + fs/xfs/scrub/xfile.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++--- fs/xfs/scrub/xfile.h | 1 + 6 files changed, 107 insertions(+), 20 deletions(-) diff --git a/fs/xfs/scrub/array.c b/fs/xfs/scrub/array.c index 47028449071e..7e1fef3c947a 100644 --- a/fs/xfs/scrub/array.c +++ b/fs/xfs/scrub/array.c @@ -66,6 +66,7 @@ xfbma_init( array->filp = filp; array->obj_size = obj_size; array->nr = 0; + array->io_flags = 0; return array; out_filp: fput(filp); @@ -105,7 +106,8 @@ xfbma_get( return -ENODATA; } - return xfile_io(array->filp, XFILE_IO_READ, &pos, ptr, array->obj_size); + return xfile_io(array->filp, array->io_flags | XFILE_IO_READ, &pos, + ptr, array->obj_size); } /* Put an element in the array. */ @@ -122,8 +124,8 @@ xfbma_set( return -ENODATA; } - return xfile_io(array->filp, XFILE_IO_WRITE, &pos, ptr, - array->obj_size); + return xfile_io(array->filp, array->io_flags | XFILE_IO_WRITE, &pos, + ptr, array->obj_size); } /* Is this array element NULL? */ @@ -172,8 +174,8 @@ xfbma_nullify( } memset(temp, 0, array->obj_size); - return xfile_io(array->filp, XFILE_IO_WRITE, &pos, temp, - array->obj_size); + return xfile_io(array->filp, array->io_flags | XFILE_IO_WRITE, &pos, + temp, array->obj_size); } /* Append an element to the array. */ @@ -190,8 +192,8 @@ xfbma_append( return -ENODATA; } - error = xfile_io(array->filp, XFILE_IO_WRITE, &pos, ptr, - array->obj_size); + error = xfile_io(array->filp, array->io_flags | XFILE_IO_WRITE, &pos, + ptr, array->obj_size); if (error) return error; array->nr++; @@ -219,8 +221,8 @@ xfbma_iter_del( for (pos = 0, i = 0; pos < max_bytes; i++) { pgoff_t pagenr; - error = xfile_io(array->filp, XFILE_IO_READ, &pos, temp, - array->obj_size); + error = xfile_io(array->filp, array->io_flags | XFILE_IO_READ, + &pos, temp, array->obj_size); if (error) break; if (xfbma_is_null(array, temp)) diff --git a/fs/xfs/scrub/array.h b/fs/xfs/scrub/array.h index 77b7f6005da4..6ce40c2e61f1 100644 --- a/fs/xfs/scrub/array.h +++ b/fs/xfs/scrub/array.h @@ -10,6 +10,7 @@ struct xfbma { struct file *filp; size_t obj_size; uint64_t nr; + unsigned int io_flags; }; struct xfbma *xfbma_init(size_t obj_size); diff --git a/fs/xfs/scrub/blob.c b/fs/xfs/scrub/blob.c index 94912fcb1fd1..30e189a8bd3c 100644 --- a/fs/xfs/scrub/blob.c +++ b/fs/xfs/scrub/blob.c @@ -46,6 +46,7 @@ xblob_init(void) blob->filp = filp; blob->last_offset = PAGE_SIZE; + blob->io_flags = 0; return blob; out_filp: fput(filp); @@ -73,7 +74,8 @@ xblob_get( loff_t pos = cookie; int error; - error = xfile_io(blob->filp, XFILE_IO_READ, &pos, &key, sizeof(key)); + error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_READ, &pos, + &key, sizeof(key)); if (error) return error; @@ -86,7 +88,8 @@ xblob_get( return -EFBIG; } - return xfile_io(blob->filp, XFILE_IO_READ, &pos, ptr, key.size); + return xfile_io(blob->filp, blob->io_flags | XFILE_IO_READ, &pos, ptr, + key.size); } /* Store a blob. */ @@ -105,11 +108,13 @@ xblob_put( loff_t pos = blob->last_offset; int error; - error = xfile_io(blob->filp, XFILE_IO_WRITE, &pos, &key, sizeof(key)); + error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_WRITE, &pos, + &key, sizeof(key)); if (error) goto out_err; - error = xfile_io(blob->filp, XFILE_IO_WRITE, &pos, ptr, size); + error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_WRITE, &pos, + ptr, size); if (error) goto out_err; @@ -131,7 +136,8 @@ xblob_free( loff_t pos = cookie; int error; - error = xfile_io(blob->filp, XFILE_IO_READ, &pos, &key, sizeof(key)); + error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_READ, &pos, + &key, sizeof(key)); if (error) return error; diff --git a/fs/xfs/scrub/blob.h b/fs/xfs/scrub/blob.h index c6f6c6a2e084..77b515aa4d21 100644 --- a/fs/xfs/scrub/blob.h +++ b/fs/xfs/scrub/blob.h @@ -9,6 +9,7 @@ struct xblob { struct file *filp; loff_t last_offset; + unsigned int io_flags; }; typedef loff_t xblob_cookie; diff --git a/fs/xfs/scrub/xfile.c b/fs/xfs/scrub/xfile.c index 2d96e2f9917c..504f1aa30c61 100644 --- a/fs/xfs/scrub/xfile.c +++ b/fs/xfs/scrub/xfile.c @@ -41,14 +41,76 @@ xfile_destroy( fput(filp); } +struct xfile_io_args { + struct work_struct work; + struct completion *done; + + struct file *filp; + void *ptr; + loff_t *pos; + size_t count; + ssize_t ret; + bool is_read; +}; + +static void +xfile_io_worker( + struct work_struct *work) +{ + struct xfile_io_args *args; + unsigned int pflags; + + args = container_of(work, struct xfile_io_args, work); + pflags = memalloc_nofs_save(); + + if (args->is_read) + args->ret = kernel_read(args->filp, args->ptr, args->count, + args->pos); + else + args->ret = kernel_write(args->filp, args->ptr, args->count, + args->pos); + complete(args->done); + + memalloc_nofs_restore(pflags); +} + /* - * Perform a read or write IO to the file backing the array. We can defer - * the work to a workqueue if the caller so desires, either to reduce stack - * usage or because the xfs is frozen and we want to avoid deadlocking on the - * page fault that might be about to happen. + * Perform a read or write IO to the file backing the array. Defer the work to + * a workqueue to avoid recursing into the filesystem while we have locks held. */ -int -xfile_io( +static int +xfile_io_async( + struct file *filp, + unsigned int cmd_flags, + loff_t *pos, + void *ptr, + size_t count) +{ + DECLARE_COMPLETION_ONSTACK(done); + struct xfile_io_args args = { + .filp = filp, + .ptr = ptr, + .pos = pos, + .count = count, + .done = &done, + .is_read = (cmd_flags & XFILE_IO_MASK) == XFILE_IO_READ, + }; + + INIT_WORK_ONSTACK(&args.work, xfile_io_worker); + schedule_work(&args.work); + wait_for_completion(&done); + destroy_work_on_stack(&args.work); + + /* + * Since we're treating this file as "memory", any IO error should be + * treated as a failure to find any memory. + */ + return args.ret == count ? 0 : -ENOMEM; +} + +/* Perform a read or write IO to the file backing the array. */ +static int +xfile_io_sync( struct file *filp, unsigned int cmd_flags, loff_t *pos, @@ -71,6 +133,20 @@ xfile_io( return ret == count ? 0 : -ENOMEM; } +/* Perform a read or write IO to the file backing the array. */ +int +xfile_io( + struct file *filp, + unsigned int cmd_flags, + loff_t *pos, + void *ptr, + size_t count) +{ + if (cmd_flags & XFILE_IO_ASYNC) + return xfile_io_async(filp, cmd_flags, pos, ptr, count); + return xfile_io_sync(filp, cmd_flags, pos, ptr, count); +} + /* Discard pages backing a range of the file. */ void xfile_discard( diff --git a/fs/xfs/scrub/xfile.h b/fs/xfs/scrub/xfile.h index 41817bcadc43..ae52053bf2e3 100644 --- a/fs/xfs/scrub/xfile.h +++ b/fs/xfs/scrub/xfile.h @@ -13,6 +13,7 @@ void xfile_destroy(struct file *filp); #define XFILE_IO_READ (0) #define XFILE_IO_WRITE (1) #define XFILE_IO_MASK (1 << 0) +#define XFILE_IO_ASYNC (1 << 1) int xfile_io(struct file *filp, unsigned int cmd_flags, loff_t *pos, void *ptr, size_t count);