diff options
Diffstat (limited to 'fs/nfs')
| -rw-r--r-- | fs/nfs/blocklayout/blocklayoutdm.c | 4 | ||||
| -rw-r--r-- | fs/nfs/idmap.c | 13 | ||||
| -rw-r--r-- | fs/nfs/nfs4client.c | 45 | ||||
| -rw-r--r-- | fs/nfs/nfs4filelayout.c | 1 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 17 | ||||
| -rw-r--r-- | fs/nfs/nfs4state.c | 8 | ||||
| -rw-r--r-- | fs/nfs/pnfs.c | 81 | ||||
| -rw-r--r-- | fs/nfs/pnfs.h | 6 | 
8 files changed, 126 insertions, 49 deletions
diff --git a/fs/nfs/blocklayout/blocklayoutdm.c b/fs/nfs/blocklayout/blocklayoutdm.c index 737d839bc17..6fc7b5cae92 100644 --- a/fs/nfs/blocklayout/blocklayoutdm.c +++ b/fs/nfs/blocklayout/blocklayoutdm.c @@ -55,7 +55,8 @@ static void dev_remove(struct net *net, dev_t dev)  	bl_pipe_msg.bl_wq = &nn->bl_wq;  	memset(msg, 0, sizeof(*msg)); -	msg->data = kzalloc(1 + sizeof(bl_umount_request), GFP_NOFS); +	msg->len = sizeof(bl_msg) + bl_msg.totallen; +	msg->data = kzalloc(msg->len, GFP_NOFS);  	if (!msg->data)  		goto out; @@ -66,7 +67,6 @@ static void dev_remove(struct net *net, dev_t dev)  	memcpy(msg->data, &bl_msg, sizeof(bl_msg));  	dataptr = (uint8_t *) msg->data;  	memcpy(&dataptr[sizeof(bl_msg)], &bl_umount_request, sizeof(bl_umount_request)); -	msg->len = sizeof(bl_msg) + bl_msg.totallen;  	add_wait_queue(&nn->bl_wq, &wq);  	if (rpc_queue_upcall(nn->bl_device_pipe, msg) < 0) { diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index dc0f98dfa71..c516da5873f 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -726,9 +726,9 @@ out1:  	return ret;  } -static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data) +static int nfs_idmap_instantiate(struct key *key, struct key *authkey, char *data, size_t datalen)  { -	return key_instantiate_and_link(key, data, strlen(data) + 1, +	return key_instantiate_and_link(key, data, datalen,  					id_resolver_cache->thread_keyring,  					authkey);  } @@ -738,6 +738,7 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,  		struct key *key, struct key *authkey)  {  	char id_str[NFS_UINT_MAXLEN]; +	size_t len;  	int ret = -ENOKEY;  	/* ret = -ENOKEY */ @@ -747,13 +748,15 @@ static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,  	case IDMAP_CONV_NAMETOID:  		if (strcmp(upcall->im_name, im->im_name) != 0)  			break; -		sprintf(id_str, "%d", im->im_id); -		ret = nfs_idmap_instantiate(key, authkey, id_str); +		/* Note: here we store the NUL terminator too */ +		len = sprintf(id_str, "%d", im->im_id) + 1; +		ret = nfs_idmap_instantiate(key, authkey, id_str, len);  		break;  	case IDMAP_CONV_IDTONAME:  		if (upcall->im_id != im->im_id)  			break; -		ret = nfs_idmap_instantiate(key, authkey, im->im_name); +		len = strlen(im->im_name); +		ret = nfs_idmap_instantiate(key, authkey, im->im_name, len);  		break;  	default:  		ret = -EINVAL; diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index ac4fc9a8fdb..66b6664dcd4 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -300,7 +300,7 @@ int nfs40_walk_client_list(struct nfs_client *new,  			   struct rpc_cred *cred)  {  	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); -	struct nfs_client *pos, *n, *prev = NULL; +	struct nfs_client *pos, *prev = NULL;  	struct nfs4_setclientid_res clid = {  		.clientid	= new->cl_clientid,  		.confirm	= new->cl_confirm, @@ -308,10 +308,23 @@ int nfs40_walk_client_list(struct nfs_client *new,  	int status = -NFS4ERR_STALE_CLIENTID;  	spin_lock(&nn->nfs_client_lock); -	list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) { +	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {  		/* If "pos" isn't marked ready, we can't trust the  		 * remaining fields in "pos" */ -		if (pos->cl_cons_state < NFS_CS_READY) +		if (pos->cl_cons_state > NFS_CS_READY) { +			atomic_inc(&pos->cl_count); +			spin_unlock(&nn->nfs_client_lock); + +			if (prev) +				nfs_put_client(prev); +			prev = pos; + +			status = nfs_wait_client_init_complete(pos); +			spin_lock(&nn->nfs_client_lock); +			if (status < 0) +				continue; +		} +		if (pos->cl_cons_state != NFS_CS_READY)  			continue;  		if (pos->rpc_ops != new->rpc_ops) @@ -423,16 +436,16 @@ int nfs41_walk_client_list(struct nfs_client *new,  			   struct rpc_cred *cred)  {  	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); -	struct nfs_client *pos, *n, *prev = NULL; +	struct nfs_client *pos, *prev = NULL;  	int status = -NFS4ERR_STALE_CLIENTID;  	spin_lock(&nn->nfs_client_lock); -	list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) { +	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {  		/* If "pos" isn't marked ready, we can't trust the  		 * remaining fields in "pos", especially the client  		 * ID and serverowner fields.  Wait for CREATE_SESSION  		 * to finish. */ -		if (pos->cl_cons_state < NFS_CS_READY) { +		if (pos->cl_cons_state > NFS_CS_READY) {  			atomic_inc(&pos->cl_count);  			spin_unlock(&nn->nfs_client_lock); @@ -440,18 +453,17 @@ int nfs41_walk_client_list(struct nfs_client *new,  				nfs_put_client(prev);  			prev = pos; -			nfs4_schedule_lease_recovery(pos);  			status = nfs_wait_client_init_complete(pos); -			if (status < 0) { -				nfs_put_client(pos); -				spin_lock(&nn->nfs_client_lock); -				continue; +			if (status == 0) { +				nfs4_schedule_lease_recovery(pos); +				status = nfs4_wait_clnt_recover(pos);  			} -			status = pos->cl_cons_state;  			spin_lock(&nn->nfs_client_lock);  			if (status < 0)  				continue;  		} +		if (pos->cl_cons_state != NFS_CS_READY) +			continue;  		if (pos->rpc_ops != new->rpc_ops)  			continue; @@ -469,17 +481,18 @@ int nfs41_walk_client_list(struct nfs_client *new,  			continue;  		atomic_inc(&pos->cl_count); -		spin_unlock(&nn->nfs_client_lock); +		*result = pos; +		status = 0;  		dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",  			__func__, pos, atomic_read(&pos->cl_count)); - -		*result = pos; -		return 0; +		break;  	}  	/* No matching nfs_client found. */  	spin_unlock(&nn->nfs_client_lock);  	dprintk("NFS: <-- %s status = %d\n", __func__, status); +	if (prev) +		nfs_put_client(prev);  	return status;  }  #endif	/* CONFIG_NFS_V4_1 */ diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 49eeb044c10..4fb234d3aef 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -129,7 +129,6 @@ static void filelayout_fenceme(struct inode *inode, struct pnfs_layout_hdr *lo)  {  	if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))  		return; -	clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags);  	pnfs_return_layout(inode);  } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b2671cb0f90..0ad025eb523 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1046,6 +1046,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)  		/* Save the delegation */  		nfs4_stateid_copy(&stateid, &delegation->stateid);  		rcu_read_unlock(); +		nfs_release_seqid(opendata->o_arg.seqid);  		ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);  		if (ret != 0)  			goto out; @@ -2632,7 +2633,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,  	int status;  	if (pnfs_ld_layoutret_on_setattr(inode)) -		pnfs_return_layout(inode); +		pnfs_commit_and_return_layout(inode);  	nfs_fattr_init(fattr); @@ -6416,22 +6417,8 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)  static void nfs4_layoutcommit_release(void *calldata)  {  	struct nfs4_layoutcommit_data *data = calldata; -	struct pnfs_layout_segment *lseg, *tmp; -	unsigned long *bitlock = &NFS_I(data->args.inode)->flags;  	pnfs_cleanup_layoutcommit(data); -	/* Matched by references in pnfs_set_layoutcommit */ -	list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) { -		list_del_init(&lseg->pls_lc_list); -		if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, -				       &lseg->pls_flags)) -			pnfs_put_lseg(lseg); -	} - -	clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock); -	smp_mb__after_clear_bit(); -	wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING); -  	put_rpccred(data->cred);  	kfree(data);  } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6ace365c633..d41a3518509 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1886,7 +1886,13 @@ again:  			status = PTR_ERR(clnt);  			break;  		} -		clp->cl_rpcclient = clnt; +		/* Note: this is safe because we haven't yet marked the +		 * client as ready, so we are the only user of +		 * clp->cl_rpcclient +		 */ +		clnt = xchg(&clp->cl_rpcclient, clnt); +		rpc_shutdown_client(clnt); +		clnt = clp->cl_rpcclient;  		goto again;  	case -NFS4ERR_MINOR_VERS_MISMATCH: diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 48ac5aad625..4bdffe0ba02 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -417,6 +417,16 @@ should_free_lseg(struct pnfs_layout_range *lseg_range,  	       lo_seg_intersecting(lseg_range, recall_range);  } +static bool pnfs_lseg_dec_and_remove_zero(struct pnfs_layout_segment *lseg, +		struct list_head *tmp_list) +{ +	if (!atomic_dec_and_test(&lseg->pls_refcount)) +		return false; +	pnfs_layout_remove_lseg(lseg->pls_layout, lseg); +	list_add(&lseg->pls_list, tmp_list); +	return true; +} +  /* Returns 1 if lseg is removed from list, 0 otherwise */  static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,  			     struct list_head *tmp_list) @@ -430,11 +440,8 @@ static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,  		 */  		dprintk("%s: lseg %p ref %d\n", __func__, lseg,  			atomic_read(&lseg->pls_refcount)); -		if (atomic_dec_and_test(&lseg->pls_refcount)) { -			pnfs_layout_remove_lseg(lseg->pls_layout, lseg); -			list_add(&lseg->pls_list, tmp_list); +		if (pnfs_lseg_dec_and_remove_zero(lseg, tmp_list))  			rv = 1; -		}  	}  	return rv;  } @@ -777,6 +784,21 @@ send_layoutget(struct pnfs_layout_hdr *lo,  	return lseg;  } +static void pnfs_clear_layoutcommit(struct inode *inode, +		struct list_head *head) +{ +	struct nfs_inode *nfsi = NFS_I(inode); +	struct pnfs_layout_segment *lseg, *tmp; + +	if (!test_and_clear_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) +		return; +	list_for_each_entry_safe(lseg, tmp, &nfsi->layout->plh_segs, pls_list) { +		if (!test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) +			continue; +		pnfs_lseg_dec_and_remove_zero(lseg, head); +	} +} +  /*   * Initiates a LAYOUTRETURN(FILE), and removes the pnfs_layout_hdr   * when the layout segment list is empty. @@ -808,6 +830,7 @@ _pnfs_return_layout(struct inode *ino)  	/* Reference matched in nfs4_layoutreturn_release */  	pnfs_get_layout_hdr(lo);  	empty = list_empty(&lo->plh_segs); +	pnfs_clear_layoutcommit(ino, &tmp_list);  	pnfs_mark_matching_lsegs_invalid(lo, &tmp_list, NULL);  	/* Don't send a LAYOUTRETURN if list was initially empty */  	if (empty) { @@ -820,8 +843,6 @@ _pnfs_return_layout(struct inode *ino)  	spin_unlock(&ino->i_lock);  	pnfs_free_lseg_list(&tmp_list); -	WARN_ON(test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)); -  	lrp = kzalloc(sizeof(*lrp), GFP_KERNEL);  	if (unlikely(lrp == NULL)) {  		status = -ENOMEM; @@ -845,6 +866,33 @@ out:  }  EXPORT_SYMBOL_GPL(_pnfs_return_layout); +int +pnfs_commit_and_return_layout(struct inode *inode) +{ +	struct pnfs_layout_hdr *lo; +	int ret; + +	spin_lock(&inode->i_lock); +	lo = NFS_I(inode)->layout; +	if (lo == NULL) { +		spin_unlock(&inode->i_lock); +		return 0; +	} +	pnfs_get_layout_hdr(lo); +	/* Block new layoutgets and read/write to ds */ +	lo->plh_block_lgets++; +	spin_unlock(&inode->i_lock); +	filemap_fdatawait(inode->i_mapping); +	ret = pnfs_layoutcommit_inode(inode, true); +	if (ret == 0) +		ret = _pnfs_return_layout(inode); +	spin_lock(&inode->i_lock); +	lo->plh_block_lgets--; +	spin_unlock(&inode->i_lock); +	pnfs_put_layout_hdr(lo); +	return ret; +} +  bool pnfs_roc(struct inode *ino)  {  	struct pnfs_layout_hdr *lo; @@ -1458,7 +1506,6 @@ static void pnfs_ld_handle_write_error(struct nfs_write_data *data)  	dprintk("pnfs write error = %d\n", hdr->pnfs_error);  	if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &  	    PNFS_LAYOUTRET_ON_ERROR) { -		clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);  		pnfs_return_layout(hdr->inode);  	}  	if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) @@ -1613,7 +1660,6 @@ static void pnfs_ld_handle_read_error(struct nfs_read_data *data)  	dprintk("pnfs read error = %d\n", hdr->pnfs_error);  	if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &  	    PNFS_LAYOUTRET_ON_ERROR) { -		clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(hdr->inode)->flags);  		pnfs_return_layout(hdr->inode);  	}  	if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) @@ -1746,11 +1792,27 @@ static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp)  	list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) {  		if (lseg->pls_range.iomode == IOMODE_RW && -		    test_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) +		    test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags))  			list_add(&lseg->pls_lc_list, listp);  	}  } +static void pnfs_list_write_lseg_done(struct inode *inode, struct list_head *listp) +{ +	struct pnfs_layout_segment *lseg, *tmp; +	unsigned long *bitlock = &NFS_I(inode)->flags; + +	/* Matched by references in pnfs_set_layoutcommit */ +	list_for_each_entry_safe(lseg, tmp, listp, pls_lc_list) { +		list_del_init(&lseg->pls_lc_list); +		pnfs_put_lseg(lseg); +	} + +	clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock); +	smp_mb__after_clear_bit(); +	wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING); +} +  void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg)  {  	pnfs_layout_io_set_failed(lseg->pls_layout, lseg->pls_range.iomode); @@ -1795,6 +1857,7 @@ void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data)  	if (nfss->pnfs_curr_ld->cleanup_layoutcommit)  		nfss->pnfs_curr_ld->cleanup_layoutcommit(data); +	pnfs_list_write_lseg_done(data->args.inode, &data->lseg_list);  }  /* diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 94ba8041774..f5f8a470a64 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -219,6 +219,7 @@ void pnfs_set_layoutcommit(struct nfs_write_data *wdata);  void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data);  int pnfs_layoutcommit_inode(struct inode *inode, bool sync);  int _pnfs_return_layout(struct inode *); +int pnfs_commit_and_return_layout(struct inode *);  void pnfs_ld_write_done(struct nfs_write_data *);  void pnfs_ld_read_done(struct nfs_read_data *);  struct pnfs_layout_segment *pnfs_update_layout(struct inode *ino, @@ -407,6 +408,11 @@ static inline int pnfs_return_layout(struct inode *ino)  	return 0;  } +static inline int pnfs_commit_and_return_layout(struct inode *inode) +{ +	return 0; +} +  static inline bool  pnfs_ld_layoutret_on_setattr(struct inode *inode)  {  |