Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a7edddc authored by Dave Chinner's avatar Dave Chinner Committed by Alex Elder
Browse files

xfs: fix memory reclaim recursion deadlock on locked inode buffer



Calling into memory reclaim with a locked inode buffer can deadlock
if memory reclaim tries to lock the inode buffer during inode
teardown. Convert the relevant memory allocations to use KM_NOFS to
avoid this deadlock condition.

Reported-by: default avatarPeter Watkins <treestem@gmail.com>
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarAlex Elder <aelder@sgi.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent 43869706
Loading
Loading
Loading
Loading
+11 −9
Original line number Original line Diff line number Diff line
@@ -422,7 +422,7 @@ xfs_iformat(
	if (!XFS_DFORK_Q(dip))
	if (!XFS_DFORK_Q(dip))
		return 0;
		return 0;
	ASSERT(ip->i_afp == NULL);
	ASSERT(ip->i_afp == NULL);
	ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP);
	ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP | KM_NOFS);
	ip->i_afp->if_ext_max =
	ip->i_afp->if_ext_max =
		XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
		XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
	switch (dip->di_aformat) {
	switch (dip->di_aformat) {
@@ -505,7 +505,7 @@ xfs_iformat_local(
		ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
		ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
	else {
	else {
		real_size = roundup(size, 4);
		real_size = roundup(size, 4);
		ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP);
		ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
	}
	}
	ifp->if_bytes = size;
	ifp->if_bytes = size;
	ifp->if_real_bytes = real_size;
	ifp->if_real_bytes = real_size;
@@ -632,7 +632,7 @@ xfs_iformat_btree(
	}
	}


	ifp->if_broot_bytes = size;
	ifp->if_broot_bytes = size;
	ifp->if_broot = kmem_alloc(size, KM_SLEEP);
	ifp->if_broot = kmem_alloc(size, KM_SLEEP | KM_NOFS);
	ASSERT(ifp->if_broot != NULL);
	ASSERT(ifp->if_broot != NULL);
	/*
	/*
	 * Copy and convert from the on-disk structure
	 * Copy and convert from the on-disk structure
@@ -2191,7 +2191,7 @@ xfs_iroot_realloc(
		 */
		 */
		if (ifp->if_broot_bytes == 0) {
		if (ifp->if_broot_bytes == 0) {
			new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(rec_diff);
			new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(rec_diff);
			ifp->if_broot = kmem_alloc(new_size, KM_SLEEP);
			ifp->if_broot = kmem_alloc(new_size, KM_SLEEP | KM_NOFS);
			ifp->if_broot_bytes = (int)new_size;
			ifp->if_broot_bytes = (int)new_size;
			return;
			return;
		}
		}
@@ -2207,7 +2207,7 @@ xfs_iroot_realloc(
		new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(new_max);
		new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(new_max);
		ifp->if_broot = kmem_realloc(ifp->if_broot, new_size,
		ifp->if_broot = kmem_realloc(ifp->if_broot, new_size,
				(size_t)XFS_BMAP_BROOT_SPACE_CALC(cur_max), /* old size */
				(size_t)XFS_BMAP_BROOT_SPACE_CALC(cur_max), /* old size */
				KM_SLEEP);
				KM_SLEEP | KM_NOFS);
		op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
		op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
						     ifp->if_broot_bytes);
						     ifp->if_broot_bytes);
		np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
		np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
@@ -2233,7 +2233,7 @@ xfs_iroot_realloc(
	else
	else
		new_size = 0;
		new_size = 0;
	if (new_size > 0) {
	if (new_size > 0) {
		new_broot = kmem_alloc(new_size, KM_SLEEP);
		new_broot = kmem_alloc(new_size, KM_SLEEP | KM_NOFS);
		/*
		/*
		 * First copy over the btree block header.
		 * First copy over the btree block header.
		 */
		 */
@@ -2337,7 +2337,8 @@ xfs_idata_realloc(
		real_size = roundup(new_size, 4);
		real_size = roundup(new_size, 4);
		if (ifp->if_u1.if_data == NULL) {
		if (ifp->if_u1.if_data == NULL) {
			ASSERT(ifp->if_real_bytes == 0);
			ASSERT(ifp->if_real_bytes == 0);
			ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP);
			ifp->if_u1.if_data = kmem_alloc(real_size,
							KM_SLEEP | KM_NOFS);
		} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
		} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
			/*
			/*
			 * Only do the realloc if the underlying size
			 * Only do the realloc if the underlying size
@@ -2348,11 +2349,12 @@ xfs_idata_realloc(
					kmem_realloc(ifp->if_u1.if_data,
					kmem_realloc(ifp->if_u1.if_data,
							real_size,
							real_size,
							ifp->if_real_bytes,
							ifp->if_real_bytes,
							KM_SLEEP);
							KM_SLEEP | KM_NOFS);
			}
			}
		} else {
		} else {
			ASSERT(ifp->if_real_bytes == 0);
			ASSERT(ifp->if_real_bytes == 0);
			ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP);
			ifp->if_u1.if_data = kmem_alloc(real_size,
							KM_SLEEP | KM_NOFS);
			memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
			memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
				ifp->if_bytes);
				ifp->if_bytes);
		}
		}