summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Sandeen <sandeen@sandeen.net>2011-07-13 10:48:46 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-03 12:42:45 -0700
commit5dde5952b696d0f97730ed45271d21d9e50b32f7 (patch)
treeb0a5507901152988040275818eb01336213129d2
parent3c84cb1e00ef95da040d93b92ae00e16c3b2dabc (diff)
downloadlinux-stable-5dde5952b696d0f97730ed45271d21d9e50b32f7.tar.gz
linux-stable-5dde5952b696d0f97730ed45271d21d9e50b32f7.tar.bz2
linux-stable-5dde5952b696d0f97730ed45271d21d9e50b32f7.zip
xfs [stable only]: restart busy extent search after node removal
A user on #xfs reported that a log replay was oopsing in __rb_rotate_left() with a null pointer deref, and provided an xfs_metadump image for reproduction and testing. I traced this down to the fact that in xfs_alloc_busy_insert(), we erased a node with rb_erase() when the new node overlapped, but left the erased node specified as the parent node for the new insertion. So when we try to insert a new node with an erased node as its parent, obviously things go very wrong. Upstream, 97d3ac75e5e0ebf7ca38ae74cebd201c09b97ab2 xfs: exact busy extent tracking actually fixed this, but as part of a much larger change. Here's the relevant code from that commit: * We also need to restart the busy extent search from the * tree root, because erasing the node can rearrange the * tree topology. */ rb_erase(&busyp->rb_node, &pag->pagb_tree); busyp->length = 0; return false; We can do essentially the same thing to older codebases by restarting the tree search after the erase. This should apply to .35.y through .39.y, and was tested on .39 with the oopsing replay reproducer. Signed-off-by: Eric Sandeen <sandeen@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Alex Elder <aelder@sgi.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--fs/xfs/xfs_alloc.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
index 27d64d752eab..a892ed905423 100644
--- a/fs/xfs/xfs_alloc.c
+++ b/fs/xfs/xfs_alloc.c
@@ -2610,6 +2610,12 @@ restart:
new->bno + new->length) -
min(busyp->bno, new->bno);
new->bno = min(busyp->bno, new->bno);
+ /*
+ * Start the search over from the tree root, because
+ * erasing the node can rearrange the tree topology.
+ */
+ spin_unlock(&pag->pagb_lock);
+ goto restart;
} else
busyp = NULL;