diff options
| author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-02-12 09:48:42 -0500 | 
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-02-14 13:22:50 -0500 | 
| commit | fd9a8d7160937f94aad36ac80d7255b4988740ac (patch) | |
| tree | e437737b8f918134b2ab26bfb74883b0fc47092d /fs/nfs/callback_proc.c | |
| parent | c8da19b9866ea84e9ad1c369393ea95d54ee7845 (diff) | |
| download | olio-linux-3.10-fd9a8d7160937f94aad36ac80d7255b4988740ac.tar.xz olio-linux-3.10-fd9a8d7160937f94aad36ac80d7255b4988740ac.zip  | |
NFSv4.1: Fix bulk recall and destroy of layouts
The current code in pnfs_destroy_all_layouts() assumes that removing
the layout from the server->layouts list is sufficient to make it
invisible to other processes. This ignores the fact that most
users access the layout through the nfs_inode->layout...
There is further breakage due to lack of reference counting of the
layouts, meaning that the whole thing Oopses at the drop of a hat.
The code in initiate_bulk_draining() is almost correct, and can be
used as a model for pnfs_destroy_all_layouts(), so move that
code to pnfs.c, and refactor the code to allow us to choose between
a single filesystem bulk recall, and a recall of all layouts.
Also note that initiate_bulk_draining() currently calls iput() while
holding locks. Fix that too.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/nfs/callback_proc.c')
| -rw-r--r-- | fs/nfs/callback_proc.c | 61 | 
1 files changed, 8 insertions, 53 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 264d1aa935f..2960512792c 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -183,60 +183,15 @@ static u32 initiate_file_draining(struct nfs_client *clp,  static u32 initiate_bulk_draining(struct nfs_client *clp,  				  struct cb_layoutrecallargs *args)  { -	struct nfs_server *server; -	struct pnfs_layout_hdr *lo; -	struct inode *ino; -	u32 rv = NFS4ERR_NOMATCHING_LAYOUT; -	struct pnfs_layout_hdr *tmp; -	LIST_HEAD(recall_list); -	LIST_HEAD(free_me_list); -	struct pnfs_layout_range range = { -		.iomode = IOMODE_ANY, -		.offset = 0, -		.length = NFS4_MAX_UINT64, -	}; - -	spin_lock(&clp->cl_lock); -	rcu_read_lock(); -	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { -		if ((args->cbl_recall_type == RETURN_FSID) && -		    memcmp(&server->fsid, &args->cbl_fsid, -			   sizeof(struct nfs_fsid))) -			continue; +	int stat; -		list_for_each_entry(lo, &server->layouts, plh_layouts) { -			ino = igrab(lo->plh_inode); -			if (!ino) -				continue; -			spin_lock(&ino->i_lock); -			/* Is this layout in the process of being freed? */ -			if (NFS_I(ino)->layout != lo) { -				spin_unlock(&ino->i_lock); -				iput(ino); -				continue; -			} -			pnfs_get_layout_hdr(lo); -			spin_unlock(&ino->i_lock); -			list_add(&lo->plh_bulk_recall, &recall_list); -		} -	} -	rcu_read_unlock(); -	spin_unlock(&clp->cl_lock); - -	list_for_each_entry_safe(lo, tmp, -				 &recall_list, plh_bulk_recall) { -		ino = lo->plh_inode; -		spin_lock(&ino->i_lock); -		set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags); -		if (pnfs_mark_matching_lsegs_invalid(lo, &free_me_list, &range)) -			rv = NFS4ERR_DELAY; -		list_del_init(&lo->plh_bulk_recall); -		spin_unlock(&ino->i_lock); -		pnfs_free_lseg_list(&free_me_list); -		pnfs_put_layout_hdr(lo); -		iput(ino); -	} -	return rv; +	if (args->cbl_recall_type == RETURN_FSID) +		stat = pnfs_destroy_layouts_byfsid(clp, &args->cbl_fsid, true); +	else +		stat = pnfs_destroy_layouts_byclid(clp, true); +	if (stat != 0) +		return NFS4ERR_DELAY; +	return NFS4ERR_NOMATCHING_LAYOUT;  }  static u32 do_callback_layoutrecall(struct nfs_client *clp,  |