[PATCH] xfs: Do not free xfs_extent_busy from inside a spinlock

Carlos Maiolino
2019-07-23T15:00:22Z

xfs_extent_busy_clear_one() calls kmem_free() with the pag spinlock
locked.

Fix this by adding a new temporary list, and, make
xfs_extent_busy_clear_one() to move the extent_busy items to this new
list, instead of freeing them.

Free the objects in the temporary list after we drop the pagb_lock

Reported-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
fs/xfs/xfs_extent_busy.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/xfs_extent_busy.c b/fs/xfs/xfs_extent_busy.c
index 0ed68379e551..0a7dcf03340b 100644
--- a/fs/xfs/xfs_extent_busy.c
+++ b/fs/xfs/xfs_extent_busy.c
@@ -523,7 +523,8 @@ STATIC void
xfs_extent_busy_clear_one(
struct xfs_mount *mp,
struct xfs_perag *pag,
- struct xfs_extent_busy *busyp)
+ struct xfs_extent_busy *busyp,
+ struct list_head *list)
{
if (busyp->length) {
trace_xfs_extent_busy_clear(mp, busyp->agno, busyp->bno,
@@ -531,8 +532,7 @@ xfs_extent_busy_clear_one(
rb_erase(&busyp->rb_node, &pag->pagb_tree);
}

- list_del_init(&busyp->list);
- kmem_free(busyp);
+ list_move(&busyp->list, list);
}

static void
@@ -565,6 +565,7 @@ xfs_extent_busy_clear(
struct xfs_perag *pag = NULL;
xfs_agnumber_t agno = NULLAGNUMBER;
bool wakeup = false;
+ LIST_HEAD(busy_list);

list_for_each_entry_safe(busyp, n, list, list) {
if (busyp->agno != agno) {
@@ -580,13 +581,18 @@ xfs_extent_busy_clear(
!(busyp->flags & XFS_EXTENT_BUSY_SKIP_DISCARD)) {
busyp->flags = XFS_EXTENT_BUSY_DISCARDED;
} else {
- xfs_extent_busy_clear_one(mp, pag, busyp);
+ xfs_extent_busy_clear_one(mp, pag, busyp, &busy_list);
wakeup = true;
}
}

if (pag)
xfs_extent_busy_put_pag(pag, wakeup);
+
+ list_for_each_entry_safe(busyp, n, &busy_list, list) {
+ list_del_init(&busyp->list);
+ kmem_free(busyp);
+ }
}

/*
--
2.20.1