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

Commit 27b6f539 authored by Peng Tao's avatar Peng Tao Committed by Tom Haynes
Browse files

nfs/flexfiles: send layoutreturn before freeing lseg



Otherwise we'll lose error tracking information when
encoding layoutreturn.

pnfs_put_lseg may be called from rpc callbacks. So we should not
call pnfs_send_layoutreturn directly because it can deadlock in
the rpc layer.

Signed-off-by: default avatarPeng Tao <tao.peng@primarydata.com>
Signed-off-by: default avatarTom Haynes <loghyr@primarydata.com>
parent 193e3aa2
Loading
Loading
Loading
Loading
+56 −25
Original line number Original line Diff line number Diff line
@@ -346,8 +346,7 @@ pnfs_layout_remove_lseg(struct pnfs_layout_hdr *lo,
/* Return true if layoutreturn is needed */
/* Return true if layoutreturn is needed */
static bool
static bool
pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
			struct pnfs_layout_segment *lseg,
			struct pnfs_layout_segment *lseg)
			nfs4_stateid *stateid, enum pnfs_iomode *iomode)
{
{
	struct pnfs_layout_segment *s;
	struct pnfs_layout_segment *s;


@@ -355,15 +354,52 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
		return false;
		return false;


	list_for_each_entry(s, &lo->plh_segs, pls_list)
	list_for_each_entry(s, &lo->plh_segs, pls_list)
		if (test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags))
		if (s != lseg && test_bit(NFS_LSEG_LAYOUTRETURN, &s->pls_flags))
			return false;
			return false;


	*stateid = lo->plh_stateid;
	return true;
	*iomode = lo->plh_return_iomode;
}

static void pnfs_layoutreturn_free_lseg(struct work_struct *work)
{
	struct pnfs_layout_segment *lseg;
	struct pnfs_layout_hdr *lo;
	struct inode *inode;

	lseg = container_of(work, struct pnfs_layout_segment, pls_work);
	WARN_ON(atomic_read(&lseg->pls_refcount));
	lo = lseg->pls_layout;
	inode = lo->plh_inode;

	spin_lock(&inode->i_lock);
	if (pnfs_layout_need_return(lo, lseg)) {
		nfs4_stateid stateid;
		enum pnfs_iomode iomode;

		stateid = lo->plh_stateid;
		iomode = lo->plh_return_iomode;
		/* decreased in pnfs_send_layoutreturn() */
		/* decreased in pnfs_send_layoutreturn() */
		lo->plh_block_lgets++;
		lo->plh_block_lgets++;
		lo->plh_return_iomode = 0;
		lo->plh_return_iomode = 0;
	return true;
		spin_unlock(&inode->i_lock);

		pnfs_send_layoutreturn(lo, stateid, iomode, true);
		spin_lock(&inode->i_lock);
	} else
		/* match pnfs_get_layout_hdr #2 in pnfs_put_lseg */
		pnfs_put_layout_hdr(lo);
	pnfs_layout_remove_lseg(lo, lseg);
	spin_unlock(&inode->i_lock);
	pnfs_free_lseg(lseg);
	/* match pnfs_get_layout_hdr #1 in pnfs_put_lseg */
	pnfs_put_layout_hdr(lo);
}

static void
pnfs_layoutreturn_free_lseg_async(struct pnfs_layout_segment *lseg)
{
	INIT_WORK(&lseg->pls_work, pnfs_layoutreturn_free_lseg);
	queue_work(nfsiod_workqueue, &lseg->pls_work);
}
}


void
void
@@ -381,23 +417,20 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg)
	lo = lseg->pls_layout;
	lo = lseg->pls_layout;
	inode = lo->plh_inode;
	inode = lo->plh_inode;
	if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) {
	if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) {
		bool need_return;
		nfs4_stateid stateid;
		enum pnfs_iomode iomode;

		pnfs_get_layout_hdr(lo);
		pnfs_get_layout_hdr(lo);
		if (pnfs_layout_need_return(lo, lseg)) {
			spin_unlock(&inode->i_lock);
			/* hdr reference dropped in nfs4_layoutreturn_release */
			pnfs_get_layout_hdr(lo);
			pnfs_layoutreturn_free_lseg_async(lseg);
		} else {
			pnfs_layout_remove_lseg(lo, lseg);
			pnfs_layout_remove_lseg(lo, lseg);
		need_return = pnfs_layout_need_return(lo, lseg,
						      &stateid, &iomode);
			spin_unlock(&inode->i_lock);
			spin_unlock(&inode->i_lock);
			pnfs_free_lseg(lseg);
			pnfs_free_lseg(lseg);
		if (need_return)
			pnfs_send_layoutreturn(lo, stateid, iomode,
					       true);
		else
			pnfs_put_layout_hdr(lo);
			pnfs_put_layout_hdr(lo);
		}
		}
	}
	}
}
EXPORT_SYMBOL_GPL(pnfs_put_lseg);
EXPORT_SYMBOL_GPL(pnfs_put_lseg);


static void pnfs_free_lseg_async_work(struct work_struct *work)
static void pnfs_free_lseg_async_work(struct work_struct *work)
@@ -1059,8 +1092,7 @@ bool pnfs_roc(struct inode *ino)
	}
	}
	spin_unlock(&ino->i_lock);
	spin_unlock(&ino->i_lock);
	if (layoutreturn)
	if (layoutreturn)
		pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, 0,
		pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, true);
				       NFS4_MAX_UINT64, true);
	return false;
	return false;
}
}


@@ -1127,8 +1159,7 @@ bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task)
	spin_unlock(&ino->i_lock);
	spin_unlock(&ino->i_lock);
	if (layoutreturn) {
	if (layoutreturn) {
		rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
		rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
		pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, 0,
		pnfs_send_layoutreturn(lo, stateid, IOMODE_ANY, false);
				       NFS4_MAX_UINT64, false);
	}
	}
	return found;
	return found;
}
}