diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2015-02-05 17:27:39 -0500 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2015-02-05 23:44:18 -0500 |
commit | 4ef2e4f84c523ebbc930ce05fa27b9b1350f4a4b (patch) | |
tree | a7c6f43fcf07eb6de7755152b38ae36d670910d5 /fs/nfs | |
parent | e4af440aaf390ac1d39b26ef6cf4a28bcb6a5979 (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.c | 53 |
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 | ||
364 | static void pnfs_layoutreturn_free_lseg(struct work_struct *work) | 364 | static 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 | |||
399 | static void | ||
400 | pnfs_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 | ||
406 | void | 389 | void |
@@ -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 | } |
435 | EXPORT_SYMBOL_GPL(pnfs_put_lseg); | 420 | EXPORT_SYMBOL_GPL(pnfs_put_lseg); |