aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2015-02-05 17:27:39 -0500
committerTrond Myklebust <trond.myklebust@primarydata.com>2015-02-05 23:44:18 -0500
commit4ef2e4f84c523ebbc930ce05fa27b9b1350f4a4b (patch)
treea7c6f43fcf07eb6de7755152b38ae36d670910d5 /fs/nfs
parente4af440aaf390ac1d39b26ef6cf4a28bcb6a5979 (diff)
NFSv4.1: Fix pnfs_put_lseg races
pnfs_layoutreturn_free_lseg_async() can also race with inode put in the general case. We can now fix this, and also simplify the code. Cc: Peng Tao <tao.peng@primarydata.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/pnfs.c53
1 files changed, 19 insertions, 34 deletions
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index a1d8620e8cb7..107b321be7d4 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -361,14 +361,9 @@ pnfs_layout_need_return(struct pnfs_layout_hdr *lo,
361 return true; 361 return true;
362} 362}
363 363
364static void pnfs_layoutreturn_free_lseg(struct work_struct *work) 364static void pnfs_layoutreturn_before_put_lseg(struct pnfs_layout_segment *lseg,
365 struct pnfs_layout_hdr *lo, struct inode *inode)
365{ 366{
366 struct pnfs_layout_segment *lseg;
367 struct pnfs_layout_hdr *lo;
368 struct inode *inode;
369
370 lseg = container_of(work, struct pnfs_layout_segment, pls_work);
371 WARN_ON(atomic_read(&lseg->pls_refcount));
372 lo = lseg->pls_layout; 367 lo = lseg->pls_layout;
373 inode = lo->plh_inode; 368 inode = lo->plh_inode;
374 369
@@ -383,24 +378,12 @@ static void pnfs_layoutreturn_free_lseg(struct work_struct *work)
383 lo->plh_block_lgets++; 378 lo->plh_block_lgets++;
384 lo->plh_return_iomode = 0; 379 lo->plh_return_iomode = 0;
385 spin_unlock(&inode->i_lock); 380 spin_unlock(&inode->i_lock);
381 pnfs_get_layout_hdr(lo);
386 382
387 pnfs_send_layoutreturn(lo, stateid, iomode, true); 383 /* Send an async layoutreturn so we dont deadlock */
388 spin_lock(&inode->i_lock); 384 pnfs_send_layoutreturn(lo, stateid, iomode, false);
389 } else 385 } else
390 /* match pnfs_get_layout_hdr #2 in pnfs_put_lseg */ 386 spin_unlock(&inode->i_lock);
391 pnfs_put_layout_hdr(lo);
392 pnfs_layout_remove_lseg(lo, lseg);
393 spin_unlock(&inode->i_lock);
394 pnfs_free_lseg(lseg);
395 /* match pnfs_get_layout_hdr #1 in pnfs_put_lseg */
396 pnfs_put_layout_hdr(lo);
397}
398
399static void
400pnfs_layoutreturn_free_lseg_async(struct pnfs_layout_segment *lseg)
401{
402 INIT_WORK(&lseg->pls_work, pnfs_layoutreturn_free_lseg);
403 queue_work(nfsiod_workqueue, &lseg->pls_work);
404} 387}
405 388
406void 389void
@@ -415,21 +398,23 @@ pnfs_put_lseg(struct pnfs_layout_segment *lseg)
415 dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg, 398 dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg,
416 atomic_read(&lseg->pls_refcount), 399 atomic_read(&lseg->pls_refcount),
417 test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); 400 test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
401
402 /* Handle the case where refcount != 1 */
403 if (atomic_add_unless(&lseg->pls_refcount, -1, 1))
404 return;
405
418 lo = lseg->pls_layout; 406 lo = lseg->pls_layout;
419 inode = lo->plh_inode; 407 inode = lo->plh_inode;
408 /* Do we need a layoutreturn? */
409 if (test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags))
410 pnfs_layoutreturn_before_put_lseg(lseg, lo, inode);
411
420 if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) { 412 if (atomic_dec_and_lock(&lseg->pls_refcount, &inode->i_lock)) {
421 pnfs_get_layout_hdr(lo); 413 pnfs_get_layout_hdr(lo);
422 if (pnfs_layout_need_return(lo, lseg)) { 414 pnfs_layout_remove_lseg(lo, lseg);
423 spin_unlock(&inode->i_lock); 415 spin_unlock(&inode->i_lock);
424 /* hdr reference dropped in nfs4_layoutreturn_release */ 416 pnfs_free_lseg(lseg);
425 pnfs_get_layout_hdr(lo); 417 pnfs_put_layout_hdr(lo);
426 pnfs_layoutreturn_free_lseg_async(lseg);
427 } else {
428 pnfs_layout_remove_lseg(lo, lseg);
429 spin_unlock(&inode->i_lock);
430 pnfs_free_lseg(lseg);
431 pnfs_put_layout_hdr(lo);
432 }
433 } 418 }
434} 419}
435EXPORT_SYMBOL_GPL(pnfs_put_lseg); 420EXPORT_SYMBOL_GPL(pnfs_put_lseg);