@@ -347,8 +347,7 @@ pnfs_layout_remove_lseg(struct pnfs_layout_hdr *lo,
/* Return true if layoutreturn is needed */
static bool
pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
- struct pnfs_layout_segment *lseg,
- nfs4_stateid *stateid, enum pnfs_iomode *iomode)
+ struct pnfs_layout_segment *lseg)
{
struct pnfs_layout_segment *s;
@@ -359,14 +358,52 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
if (s != lseg && test_bit(NFS_LSEG_LAYOUTRETURN, &s->pls_flags))
return false;
- *stateid = lo->plh_stateid;
- *iomode = lo->plh_return_iomode;
- /* decreased in pnfs_send_layoutreturn() */
- lo->plh_block_lgets++;
- lo->plh_return_iomode = 0;
return true;
}
+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() */
+ lo->plh_block_lgets++;
+ lo->plh_return_iomode = 0;
+ spin_unlock(&inode->i_lock);
+
+ pnfs_send_layoutreturn(lo, stateid, iomode, 0,
+ NFS4_MAX_UINT64, 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
pnfs_put_lseg(struct pnfs_layout_segment *lseg)
{
@@ -382,22 +419,12 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg)
lo = lseg->pls_layout;
inode = lo->plh_inode;
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);
- need_return = pnfs_layout_need_return(lo, lseg,
- &stateid, &iomode);
- if (need_return) {
+ if (pnfs_layout_need_return(lo, lseg)) {
spin_unlock(&inode->i_lock);
/* hdr reference dropped in nfs4_layoutreturn_release */
- pnfs_send_layoutreturn(lo, stateid, iomode, 0,
- NFS4_MAX_UINT64, true);
- spin_lock(&inode->i_lock);
- pnfs_layout_remove_lseg(lo, lseg);
- spin_unlock(&inode->i_lock);
- pnfs_free_lseg(lseg);
+ pnfs_get_layout_hdr(lo);
+ pnfs_layoutreturn_free_lseg_async(lseg);
} else {
pnfs_layout_remove_lseg(lo, lseg);
spin_unlock(&inode->i_lock);