Skip to content

Commit 880265c

Browse files
Trond Myklebustamschuma-ntap
authored andcommitted
pNFS: Avoid a live lock condition in pnfs_update_layout()
If we're about to send the first layoutget for an empty layout, we want to make sure that we drain out the existing pending layoutget calls first. The reason is that these layouts may have been already implicitly returned to the server by a recall to which the client gave a NFS4ERR_NOMATCHING_LAYOUT response. The problem is that wait_var_event_killable() could in principle see the plh_outstanding count go back to '1' when the first process to wake up starts sending a new layoutget. If it fails to get a layout, then this loop can continue ad infinitum... Fixes: 0b77f97 ("NFSv4/pnfs: Fix layoutget behaviour after invalidation") Signed-off-by: Trond Myklebust <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent fe44fb2 commit 880265c

File tree

3 files changed

+11
-6
lines changed

3 files changed

+11
-6
lines changed

fs/nfs/callback_proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
288288
rv = NFS4_OK;
289289
break;
290290
case -ENOENT:
291+
set_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags);
291292
/* Embrace your forgetfulness! */
292293
rv = NFS4ERR_NOMATCHING_LAYOUT;
293294

fs/nfs/pnfs.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
469469
pnfs_clear_lseg_state(lseg, lseg_list);
470470
pnfs_clear_layoutreturn_info(lo);
471471
pnfs_free_returned_lsegs(lo, lseg_list, &range, 0);
472+
set_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags);
472473
if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags) &&
473474
!test_and_set_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags))
474475
pnfs_clear_layoutreturn_waitbit(lo);
@@ -1917,8 +1918,9 @@ static void nfs_layoutget_begin(struct pnfs_layout_hdr *lo)
19171918

19181919
static void nfs_layoutget_end(struct pnfs_layout_hdr *lo)
19191920
{
1920-
if (atomic_dec_and_test(&lo->plh_outstanding))
1921-
wake_up_var(&lo->plh_outstanding);
1921+
if (atomic_dec_and_test(&lo->plh_outstanding) &&
1922+
test_and_clear_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags))
1923+
wake_up_bit(&lo->plh_flags, NFS_LAYOUT_DRAIN);
19221924
}
19231925

19241926
static bool pnfs_is_first_layoutget(struct pnfs_layout_hdr *lo)
@@ -2025,11 +2027,11 @@ pnfs_update_layout(struct inode *ino,
20252027
* If the layout segment list is empty, but there are outstanding
20262028
* layoutget calls, then they might be subject to a layoutrecall.
20272029
*/
2028-
if ((list_empty(&lo->plh_segs) || !pnfs_layout_is_valid(lo)) &&
2030+
if (test_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags) &&
20292031
atomic_read(&lo->plh_outstanding) != 0) {
20302032
spin_unlock(&ino->i_lock);
2031-
lseg = ERR_PTR(wait_var_event_killable(&lo->plh_outstanding,
2032-
!atomic_read(&lo->plh_outstanding)));
2033+
lseg = ERR_PTR(wait_on_bit(&lo->plh_flags, NFS_LAYOUT_DRAIN,
2034+
TASK_KILLABLE));
20332035
if (IS_ERR(lseg))
20342036
goto out_put_layout_hdr;
20352037
pnfs_put_layout_hdr(lo);
@@ -2413,7 +2415,8 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
24132415
goto out_forget;
24142416
}
24152417

2416-
if (!pnfs_layout_is_valid(lo) && !pnfs_is_first_layoutget(lo))
2418+
if (test_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags) &&
2419+
!pnfs_is_first_layoutget(lo))
24172420
goto out_forget;
24182421

24192422
if (nfs4_stateid_match_other(&lo->plh_stateid, &res->stateid)) {

fs/nfs/pnfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ enum {
105105
NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */
106106
NFS_LAYOUT_INODE_FREEING, /* The inode is being freed */
107107
NFS_LAYOUT_HASHED, /* The layout visible */
108+
NFS_LAYOUT_DRAIN,
108109
};
109110

110111
enum layoutdriver_policy_flags {

0 commit comments

Comments
 (0)