mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 16:54:20 +08:00
pNFS: Avoid a live lock condition in pnfs_update_layout()
[ Upstream commit880265c77a
] 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:0b77f97a7e
("NFSv4/pnfs: Fix layoutget behaviour after invalidation") Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
b2bb8b6ec8
commit
ec23a86e06
@ -288,6 +288,7 @@ static u32 initiate_file_draining(struct nfs_client *clp,
|
||||
rv = NFS4_OK;
|
||||
break;
|
||||
case -ENOENT:
|
||||
set_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags);
|
||||
/* Embrace your forgetfulness! */
|
||||
rv = NFS4ERR_NOMATCHING_LAYOUT;
|
||||
|
||||
|
@ -469,6 +469,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
|
||||
pnfs_clear_lseg_state(lseg, lseg_list);
|
||||
pnfs_clear_layoutreturn_info(lo);
|
||||
pnfs_free_returned_lsegs(lo, lseg_list, &range, 0);
|
||||
set_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags);
|
||||
if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags) &&
|
||||
!test_and_set_bit(NFS_LAYOUT_RETURN_LOCK, &lo->plh_flags))
|
||||
pnfs_clear_layoutreturn_waitbit(lo);
|
||||
@ -1917,8 +1918,9 @@ static void nfs_layoutget_begin(struct pnfs_layout_hdr *lo)
|
||||
|
||||
static void nfs_layoutget_end(struct pnfs_layout_hdr *lo)
|
||||
{
|
||||
if (atomic_dec_and_test(&lo->plh_outstanding))
|
||||
wake_up_var(&lo->plh_outstanding);
|
||||
if (atomic_dec_and_test(&lo->plh_outstanding) &&
|
||||
test_and_clear_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags))
|
||||
wake_up_bit(&lo->plh_flags, NFS_LAYOUT_DRAIN);
|
||||
}
|
||||
|
||||
static bool pnfs_is_first_layoutget(struct pnfs_layout_hdr *lo)
|
||||
@ -2025,11 +2027,11 @@ lookup_again:
|
||||
* If the layout segment list is empty, but there are outstanding
|
||||
* layoutget calls, then they might be subject to a layoutrecall.
|
||||
*/
|
||||
if ((list_empty(&lo->plh_segs) || !pnfs_layout_is_valid(lo)) &&
|
||||
if (test_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags) &&
|
||||
atomic_read(&lo->plh_outstanding) != 0) {
|
||||
spin_unlock(&ino->i_lock);
|
||||
lseg = ERR_PTR(wait_var_event_killable(&lo->plh_outstanding,
|
||||
!atomic_read(&lo->plh_outstanding)));
|
||||
lseg = ERR_PTR(wait_on_bit(&lo->plh_flags, NFS_LAYOUT_DRAIN,
|
||||
TASK_KILLABLE));
|
||||
if (IS_ERR(lseg))
|
||||
goto out_put_layout_hdr;
|
||||
pnfs_put_layout_hdr(lo);
|
||||
@ -2413,7 +2415,8 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
|
||||
goto out_forget;
|
||||
}
|
||||
|
||||
if (!pnfs_layout_is_valid(lo) && !pnfs_is_first_layoutget(lo))
|
||||
if (test_bit(NFS_LAYOUT_DRAIN, &lo->plh_flags) &&
|
||||
!pnfs_is_first_layoutget(lo))
|
||||
goto out_forget;
|
||||
|
||||
if (nfs4_stateid_match_other(&lo->plh_stateid, &res->stateid)) {
|
||||
|
@ -109,6 +109,7 @@ enum {
|
||||
NFS_LAYOUT_FIRST_LAYOUTGET, /* Serialize first layoutget */
|
||||
NFS_LAYOUT_INODE_FREEING, /* The inode is being freed */
|
||||
NFS_LAYOUT_HASHED, /* The layout visible */
|
||||
NFS_LAYOUT_DRAIN,
|
||||
};
|
||||
|
||||
enum layoutdriver_policy_flags {
|
||||
|
Loading…
Reference in New Issue
Block a user