Move the allocation worker call so that any loops on xfs_alloc_vextent()
calls for a particular transaction are contained within a single worker.
This prevents a filesystem hang that can occur because the holder of
AGF buffer lock cannot allocate a worker.
Signed-off-by: Mark Tinguely <tinguely@xxxxxxx>
---
fs/xfs/xfs_alloc.c | 53 -------------------------------------------
fs/xfs/xfs_alloc.h | 3 --
fs/xfs/xfs_bmap.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/xfs_bmap.h | 16 +++++++++++++
4 files changed, 80 insertions(+), 56 deletions(-)
Index: b/fs/xfs/xfs_alloc.c
===================================================================
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -2207,7 +2207,7 @@ xfs_alloc_read_agf(
* group or loop over the allocation groups to find the result.
*/
int /* error */
-__xfs_alloc_vextent(
+xfs_alloc_vextent(
xfs_alloc_arg_t *args) /* allocation argument structure */
{
xfs_agblock_t agsize; /* allocation group size */
@@ -2417,57 +2417,6 @@ error0:
return error;
}
-#if defined(CONFIG_X86_64)
-static void
-xfs_alloc_vextent_worker(
- struct work_struct *work)
-{
- struct xfs_alloc_arg *args = container_of(work,
- struct xfs_alloc_arg, work);
- unsigned long pflags;
-
- /* we are in a transaction context here */
- current_set_flags_nested(&pflags, PF_FSTRANS);
-
- args->result = __xfs_alloc_vextent(args);
- complete(args->done);
-
- current_restore_flags_nested(&pflags, PF_FSTRANS);
-}
-#endif
-
-/*
- * Data allocation requests often come in with little stack to work on. Push
- * them off to a worker thread so there is lots of stack to use. Metadata
- * requests, OTOH, are generally from low stack usage paths, so avoid the
- * context switch overhead here.
- */
-int
-xfs_alloc_vextent(
- struct xfs_alloc_arg *args)
-{
-#if defined(CONFIG_X86_64)
- DECLARE_COMPLETION_ONSTACK(done);
-
- if (!args->userdata)
- return __xfs_alloc_vextent(args);
-
-
- args->done = &done;
- INIT_WORK_ONSTACK(&args->work, xfs_alloc_vextent_worker);
- queue_work(xfs_alloc_wq, &args->work);
- wait_for_completion(&done);
- return args->result;
-#else
- /* The allocation worker is needed by the i386_64.
- * Do not use the worker for other platforms. This will
- * allow those platforms avoid the performance hit and
- * the potential AGF buffer deadlock issue.
- */
- return __xfs_alloc_vextent(args);
-#endif
-}
-
/*
* Free an extent.
* Just break up the extent address and hand off to xfs_free_ag_extent
Index: b/fs/xfs/xfs_alloc.h
===================================================================
--- a/fs/xfs/xfs_alloc.h
+++ b/fs/xfs/xfs_alloc.h
@@ -120,9 +120,6 @@ typedef struct xfs_alloc_arg {
char isfl; /* set if is freelist blocks - !acctg */
char userdata; /* set if this is user data */
xfs_fsblock_t firstblock; /* io first block allocated */
- struct completion *done;
- struct work_struct work;
- int result;
} xfs_alloc_arg_t;
/*
Index: b/fs/xfs/xfs_bmap.c
===================================================================
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -48,7 +48,6 @@
#include "xfs_vnodeops.h"
#include "xfs_trace.h"
-
kmem_zone_t *xfs_bmap_free_item_zone;
/*
@@ -4807,7 +4806,11 @@ xfs_bmapi_convert_unwritten(
* blocks then the call will fail (return NULLFSBLOCK in "firstblock").
*/
int
+#if defined(CONFIG_X86_64)
+__xfs_bmapi_write(
+#else
xfs_bmapi_write(
+#endif
struct xfs_trans *tp, /* transaction pointer */
struct xfs_inode *ip, /* incore inode */
xfs_fileoff_t bno, /* starting file offs. mapped */
@@ -5031,6 +5034,65 @@ error0:
return error;
}
+#if defined(CONFIG_X86_64)
+static void
+xfs_bmapi_write_worker(
+ struct work_struct *work)
+{
+ struct xfs_bmw_wkr *bw = container_of(work,
+ struct xfs_bmw_wkr, work);
+ unsigned long pflags;
+
+ /* we are in a transaction context here */
+ current_set_flags_nested(&pflags, PF_FSTRANS);
+
+ bw->result = __xfs_bmapi_write(bw->tp, bw->ip, bw->bno, bw->len,
+ bw->flags, bw->firstblock, bw->total,
+ bw->mval, bw->nmap, bw->flist);
+ complete(bw->done);
+
+ current_restore_flags_nested(&pflags, PF_FSTRANS);
+}
+
+int
+xfs_bmapi_write(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_fileoff_t bno, /* starting file offs. mapped */
+ xfs_filblks_t len, /* length to map in file */
+ int flags, /* XFS_BMAPI_... */
+ xfs_fsblock_t *firstblock, /* first allocated block
+ controls a.g. for allocs */
+ xfs_extlen_t total, /* total blocks needed */
+ struct xfs_bmbt_irec *mval, /* output: map values */
+ int *nmap, /* i/o: mval size/count */
+ struct xfs_bmap_free *flist) /* i/o: list extents to free */
+{
+ struct xfs_bmw_wkr bw;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ if (flags & XFS_BMAPI_METADATA)
+ return __xfs_bmapi_write(tp, ip, bno, len, flags, firstblock,
+ total, mval, nmap, flist);
+ /* initialize the worker argument list structure */
+ bw.tp = tp;
+ bw.ip = ip;
+ bw.bno = bno;
+ bw.len = len;
+ bw.flags = flags;
+ bw.firstblock = firstblock;
+ bw.total = total;
+ bw.mval = mval;
+ bw.nmap = nmap;
+ bw.flist = flist;
+ bw.done = &done;
+ INIT_WORK_ONSTACK(&bw.work, xfs_bmapi_write_worker);
+ queue_work(xfs_alloc_wq, &bw.work);
+ wait_for_completion(&done);
+ return bw.result;
+}
+#endif
+
/*
* Unmap (remove) blocks from a file.
* If nexts is nonzero then the number of extents to remove is limited to
Index: b/fs/xfs/xfs_bmap.h
===================================================================
--- a/fs/xfs/xfs_bmap.h
+++ b/fs/xfs/xfs_bmap.h
@@ -135,6 +135,22 @@ typedef struct xfs_bmalloca {
char conv; /* overwriting unwritten extents */
} xfs_bmalloca_t;
+struct xfs_bmw_wkr {
+ struct xfs_trans *tp; /* transaction pointer */
+ struct xfs_inode *ip; /* incore inode */
+ xfs_fileoff_t bno; /* starting file offs. mapped */
+ xfs_filblks_t len; /* length to map in file */
+ int flags; /* XFS_BMAPI_... */
+ xfs_fsblock_t *firstblock; /* first allocblock controls */
+ xfs_extlen_t total; /* total blocks needed */
+ struct xfs_bmbt_irec *mval; /* output: map values */
+ int *nmap; /* i/o: mval size/count */
+ struct xfs_bmap_free *flist; /* bmap freelist */
+ struct completion *done; /* worker completion ptr */
+ struct work_struct work; /* worker */
+ int result; /* worker function result */
+} ;
+
/*
* Flags for xfs_bmap_add_extent*.
*/
|