diff options
Diffstat (limited to 'fs/nfs')
| -rw-r--r-- | fs/nfs/Kconfig | 1 | ||||
| -rw-r--r-- | fs/nfs/callback.c | 9 | ||||
| -rw-r--r-- | fs/nfs/callback_xdr.c | 2 | ||||
| -rw-r--r-- | fs/nfs/client.c | 17 | ||||
| -rw-r--r-- | fs/nfs/delegation.c | 12 | ||||
| -rw-r--r-- | fs/nfs/delegation.h | 1 | ||||
| -rw-r--r-- | fs/nfs/dir.c | 7 | ||||
| -rw-r--r-- | fs/nfs/direct.c | 11 | ||||
| -rw-r--r-- | fs/nfs/dns_resolve.c | 2 | ||||
| -rw-r--r-- | fs/nfs/file.c | 31 | ||||
| -rw-r--r-- | fs/nfs/filelayout/filelayout.c | 6 | ||||
| -rw-r--r-- | fs/nfs/flexfilelayout/flexfilelayout.c | 14 | ||||
| -rw-r--r-- | fs/nfs/inode.c | 23 | ||||
| -rw-r--r-- | fs/nfs/internal.h | 10 | ||||
| -rw-r--r-- | fs/nfs/mount_clnt.c | 2 | ||||
| -rw-r--r-- | fs/nfs/nfs2xdr.c | 58 | ||||
| -rw-r--r-- | fs/nfs/nfs3client.c | 1 | ||||
| -rw-r--r-- | fs/nfs/nfs3xdr.c | 142 | ||||
| -rw-r--r-- | fs/nfs/nfs4_fs.h | 1 | ||||
| -rw-r--r-- | fs/nfs/nfs4client.c | 6 | ||||
| -rw-r--r-- | fs/nfs/nfs4file.c | 4 | ||||
| -rw-r--r-- | fs/nfs/nfs4idmap.c | 27 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 159 | ||||
| -rw-r--r-- | fs/nfs/nfs4state.c | 7 | ||||
| -rw-r--r-- | fs/nfs/nfs4super.c | 2 | ||||
| -rw-r--r-- | fs/nfs/pagelist.c | 123 | ||||
| -rw-r--r-- | fs/nfs/pnfs.c | 4 | ||||
| -rw-r--r-- | fs/nfs/pnfs.h | 4 | ||||
| -rw-r--r-- | fs/nfs/read.c | 6 | ||||
| -rw-r--r-- | fs/nfs/super.c | 34 | ||||
| -rw-r--r-- | fs/nfs/symlink.c | 7 | ||||
| -rw-r--r-- | fs/nfs/write.c | 70 | 
32 files changed, 522 insertions, 281 deletions
| diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 5f93cfacb3d1..69d02cf8cf37 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -121,7 +121,6 @@ config PNFS_FILE_LAYOUT  config PNFS_BLOCK  	tristate  	depends on NFS_V4_1 && BLK_DEV_DM -	depends on 64BIT || LBDAF  	default NFS_V4  config PNFS_FLEXFILE_LAYOUT diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 0b602a39dd71..7817ad94a6ba 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -41,11 +41,13 @@ static struct svc_program nfs4_callback_program;  static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)  { +	const struct cred *cred = current_cred();  	int ret;  	struct nfs_net *nn = net_generic(net, nfs_net_id);  	ret = svc_create_xprt(serv, "tcp", net, PF_INET, -				nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); +				nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, +				cred);  	if (ret <= 0)  		goto out_err;  	nn->nfs_callback_tcpport = ret; @@ -53,7 +55,8 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)  		nn->nfs_callback_tcpport, PF_INET, net->ns.inum);  	ret = svc_create_xprt(serv, "tcp", net, PF_INET6, -				nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); +				nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS, +				cred);  	if (ret > 0) {  		nn->nfs_callback_tcpport6 = ret;  		dprintk("NFS: Callback listener port = %u (af %u, net %x)\n", @@ -457,4 +460,6 @@ static struct svc_program nfs4_callback_program = {  	.pg_class = "nfs",				/* authentication class */  	.pg_stats = &nfs4_callback_stats,  	.pg_authenticate = nfs_callback_authenticate, +	.pg_init_request = svc_generic_init_request, +	.pg_rpcbind_set	= svc_generic_rpcbind_set,  }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 06233bfa6d73..73a5a5ea2976 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -983,7 +983,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)  out_invalidcred:  	pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n"); -	return rpc_autherr_badcred; +	return svc_return_autherr(rqstp, rpc_autherr_badcred);  }  /* diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 90d71fda65ce..3d04cb0b839e 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -284,6 +284,7 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat  	struct nfs_client *clp;  	const struct sockaddr *sap = data->addr;  	struct nfs_net *nn = net_generic(data->net, nfs_net_id); +	int error;  again:  	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { @@ -296,9 +297,11 @@ again:  		if (clp->cl_cons_state > NFS_CS_READY) {  			refcount_inc(&clp->cl_count);  			spin_unlock(&nn->nfs_client_lock); -			nfs_wait_client_init_complete(clp); +			error = nfs_wait_client_init_complete(clp);  			nfs_put_client(clp);  			spin_lock(&nn->nfs_client_lock); +			if (error < 0) +				return ERR_PTR(error);  			goto again;  		} @@ -407,6 +410,8 @@ struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)  		clp = nfs_match_client(cl_init);  		if (clp) {  			spin_unlock(&nn->nfs_client_lock); +			if (IS_ERR(clp)) +				return clp;  			if (new)  				new->rpc_ops->free_client(new);  			return nfs_found_client(cl_init, clp); @@ -500,6 +505,7 @@ int nfs_create_rpc_client(struct nfs_client *clp,  		.program	= &nfs_program,  		.version	= clp->rpc_ops->version,  		.authflavor	= flavor, +		.cred		= cl_init->cred,  	};  	if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags)) @@ -552,6 +558,7 @@ static int nfs_start_lockd(struct nfs_server *server)  					1 : 0,  		.net		= clp->cl_net,  		.nlmclnt_ops 	= clp->cl_nfs_mod->rpc_ops->nlmclnt_ops, +		.cred		= current_cred(),  	};  	if (nlm_init.nfs_version > 3) @@ -598,6 +605,8 @@ int nfs_init_server_rpcclient(struct nfs_server *server,  			sizeof(server->client->cl_timeout_default));  	server->client->cl_timeout = &server->client->cl_timeout_default;  	server->client->cl_softrtry = 0; +	if (server->flags & NFS_MOUNT_SOFTERR) +		server->client->cl_softerr = 1;  	if (server->flags & NFS_MOUNT_SOFT)  		server->client->cl_softrtry = 1; @@ -652,6 +661,7 @@ static int nfs_init_server(struct nfs_server *server,  		.proto = data->nfs_server.protocol,  		.net = data->net,  		.timeparms = &timeparms, +		.cred = server->cred,  	};  	struct nfs_client *clp;  	int error; @@ -920,6 +930,7 @@ void nfs_free_server(struct nfs_server *server)  	ida_destroy(&server->lockowner_id);  	ida_destroy(&server->openowner_id);  	nfs_free_iostats(server->io_stats); +	put_cred(server->cred);  	kfree(server);  	nfs_release_automount_timer();  } @@ -940,6 +951,8 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,  	if (!server)  		return ERR_PTR(-ENOMEM); +	server->cred = get_cred(current_cred()); +  	error = -ENOMEM;  	fattr = nfs_alloc_fattr();  	if (fattr == NULL) @@ -1006,6 +1019,8 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,  	if (!server)  		return ERR_PTR(-ENOMEM); +	server->cred = get_cred(source->cred); +  	error = -ENOMEM;  	fattr_fsinfo = nfs_alloc_fattr();  	if (fattr_fsinfo == NULL) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 2f6b447cdd82..8b78274e3e56 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -1034,6 +1034,18 @@ void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)  }  /** + * nfs_test_expired_all_delegations - test all delegations for a client + * @clp: nfs_client to process + * + * Helper for handling "recallable state revoked" status from server. + */ +void nfs_test_expired_all_delegations(struct nfs_client *clp) +{ +	nfs_mark_test_expired_all_delegations(clp); +	nfs4_schedule_state_manager(clp); +} + +/**   * nfs_reap_expired_delegations - reap expired delegations   * @clp: nfs_client to process   * diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 35b4b02c1ae0..5799777df5ec 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -58,6 +58,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp);  void nfs_delegation_reap_unclaimed(struct nfs_client *clp);  void nfs_mark_test_expired_all_delegations(struct nfs_client *clp); +void nfs_test_expired_all_delegations(struct nfs_client *clp);  void nfs_reap_expired_delegations(struct nfs_client *clp);  /* NFSv4 delegation-related procedures */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index a71d0b42d160..47d445bec8c9 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -714,8 +714,9 @@ out:   * We only need to convert from xdr once so future lookups are much simpler   */  static -int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) +int nfs_readdir_filler(void *data, struct page* page)  { +	nfs_readdir_descriptor_t *desc = data;  	struct inode	*inode = file_inode(desc->file);  	int ret; @@ -762,8 +763,8 @@ void cache_page_release(nfs_readdir_descriptor_t *desc)  static  struct page *get_cache_page(nfs_readdir_descriptor_t *desc)  { -	return read_cache_page(desc->file->f_mapping, -			desc->page_index, (filler_t *)nfs_readdir_filler, desc); +	return read_cache_page(desc->file->f_mapping, desc->page_index, +			nfs_readdir_filler, desc);  }  /* diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 0fd811ac08b5..2436bd92bc00 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -492,7 +492,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,  			struct nfs_page *req;  			unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase);  			/* XXX do we need to do the eof zeroing found in async_filler? */ -			req = nfs_create_request(dreq->ctx, pagevec[i], NULL, +			req = nfs_create_request(dreq->ctx, pagevec[i],  						 pgbase, req_len);  			if (IS_ERR(req)) {  				result = PTR_ERR(req); @@ -663,6 +663,8 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)  	}  	list_for_each_entry_safe(req, tmp, &reqs, wb_list) { +		/* Bump the transmission count */ +		req->wb_nio++;  		if (!nfs_pageio_add_request(&desc, req)) {  			nfs_list_move_request(req, &failed);  			spin_lock(&cinfo.inode->i_lock); @@ -703,6 +705,11 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)  		req = nfs_list_entry(data->pages.next);  		nfs_list_remove_request(req);  		if (dreq->flags == NFS_ODIRECT_RESCHED_WRITES) { +			/* +			 * Despite the reboot, the write was successful, +			 * so reset wb_nio. +			 */ +			req->wb_nio = 0;  			/* Note the rewrite will go through mds */  			nfs_mark_request_commit(req, NULL, &cinfo, 0);  		} else @@ -899,7 +906,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,  			struct nfs_page *req;  			unsigned int req_len = min_t(size_t, bytes, PAGE_SIZE - pgbase); -			req = nfs_create_request(dreq->ctx, pagevec[i], NULL, +			req = nfs_create_request(dreq->ctx, pagevec[i],  						 pgbase, req_len);  			if (IS_ERR(req)) {  				result = PTR_ERR(req); diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index a7d3df85736d..e6a700f01452 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -22,7 +22,7 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, size_t namelen,  	char *ip_addr = NULL;  	int ip_len; -	ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL); +	ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL, false);  	if (ip_len > 0)  		ret = rpc_pton(net, ip_addr, ip_len, sa, salen);  	else diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 4899b85f9b3c..144e183250c3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -147,7 +147,7 @@ nfs_file_flush(struct file *file, fl_owner_t id)  		return 0;  	/* Flush writes to the server and return any errors */ -	return vfs_fsync(file, 0); +	return nfs_wb_all(inode);  }  ssize_t @@ -199,13 +199,6 @@ EXPORT_SYMBOL_GPL(nfs_file_mmap);   * Flush any dirty pages for this process, and check for write errors.   * The return status from this call provides a reliable indication of   * whether any write errors occurred for this process. - * - * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to - * disk, but it retrieves and clears ctx->error after synching, despite - * the two being set at the same time in nfs_context_set_write_error(). - * This is because the former is used to notify the _next_ call to - * nfs_file_write() that a write error occurred, and hence cause it to - * fall back to doing a synchronous write.   */  static int  nfs_file_fsync_commit(struct file *file, int datasync) @@ -220,11 +213,8 @@ nfs_file_fsync_commit(struct file *file, int datasync)  	nfs_inc_stats(inode, NFSIOS_VFSFSYNC);  	do_resend = test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);  	status = nfs_commit_inode(inode, FLUSH_SYNC); -	if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) { -		ret = xchg(&ctx->error, 0); -		if (ret) -			goto out; -	} +	if (status == 0) +		status = file_check_and_advance_wb_err(file);  	if (status < 0) {  		ret = status;  		goto out; @@ -245,13 +235,7 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)  	trace_nfs_fsync_enter(inode);  	do { -		struct nfs_open_context *ctx = nfs_file_open_context(file); -		ret = filemap_write_and_wait_range(inode->i_mapping, start, end); -		if (test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags)) { -			int ret2 = xchg(&ctx->error, 0); -			if (ret2) -				ret = ret2; -		} +		ret = file_write_and_wait_range(file, start, end);  		if (ret != 0)  			break;  		ret = nfs_file_fsync_commit(file, datasync); @@ -600,8 +584,7 @@ static int nfs_need_check_write(struct file *filp, struct inode *inode)  	struct nfs_open_context *ctx;  	ctx = nfs_file_open_context(filp); -	if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags) || -	    nfs_ctx_key_to_expire(ctx, inode)) +	if (nfs_ctx_key_to_expire(ctx, inode))  		return 1;  	return 0;  } @@ -655,7 +638,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)  	/* Return error values */  	if (nfs_need_check_write(file, inode)) { -		int err = vfs_fsync(file, 0); +		int err = nfs_wb_all(inode);  		if (err < 0)  			result = err;  	} @@ -709,7 +692,7 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)  	 * Flush all pending writes before doing anything  	 * with locks..  	 */ -	vfs_fsync(filp, 0); +	nfs_wb_all(inode);  	l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));  	if (!IS_ERR(l_ctx)) { diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c index 61f46facb39c..3cb073c50fa6 100644 --- a/fs/nfs/filelayout/filelayout.c +++ b/fs/nfs/filelayout/filelayout.c @@ -904,7 +904,7 @@ fl_pnfs_update_layout(struct inode *ino,  	status = filelayout_check_deviceid(lo, fl, gfp_flags);  	if (status) {  		pnfs_put_lseg(lseg); -		lseg = ERR_PTR(status); +		lseg = NULL;  	}  out:  	return lseg; @@ -917,7 +917,7 @@ filelayout_pg_init_read(struct nfs_pageio_descriptor *pgio,  	pnfs_generic_pg_check_layout(pgio);  	if (!pgio->pg_lseg) {  		pgio->pg_lseg = fl_pnfs_update_layout(pgio->pg_inode, -						      req->wb_context, +						      nfs_req_openctx(req),  						      0,  						      NFS4_MAX_UINT64,  						      IOMODE_READ, @@ -944,7 +944,7 @@ filelayout_pg_init_write(struct nfs_pageio_descriptor *pgio,  	pnfs_generic_pg_check_layout(pgio);  	if (!pgio->pg_lseg) {  		pgio->pg_lseg = fl_pnfs_update_layout(pgio->pg_inode, -						      req->wb_context, +						      nfs_req_openctx(req),  						      0,  						      NFS4_MAX_UINT64,  						      IOMODE_RW, diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 6673d4ff5a2a..9920c52bd0cd 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -28,6 +28,8 @@  #define FF_LAYOUT_POLL_RETRY_MAX     (15*HZ)  #define FF_LAYOUTRETURN_MAXERR 20 +static unsigned short io_maxretrans; +  static void ff_layout_read_record_layoutstats_done(struct rpc_task *task,  		struct nfs_pgio_header *hdr);  static int ff_layout_mirror_prepare_stats(struct pnfs_layout_hdr *lo, @@ -871,7 +873,7 @@ ff_layout_pg_get_read(struct nfs_pageio_descriptor *pgio,  {  	pnfs_put_lseg(pgio->pg_lseg);  	pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, -					   req->wb_context, +					   nfs_req_openctx(req),  					   0,  					   NFS4_MAX_UINT64,  					   IOMODE_READ, @@ -925,6 +927,7 @@ retry:  	pgm = &pgio->pg_mirrors[0];  	pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].rsize; +	pgio->pg_maxretrans = io_maxretrans;  	return;  out_nolseg:  	if (pgio->pg_error < 0) @@ -950,7 +953,7 @@ retry:  	pnfs_generic_pg_check_layout(pgio);  	if (!pgio->pg_lseg) {  		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, -						   req->wb_context, +						   nfs_req_openctx(req),  						   0,  						   NFS4_MAX_UINT64,  						   IOMODE_RW, @@ -992,6 +995,7 @@ retry:  		pgm->pg_bsize = mirror->mirror_ds->ds_versions[0].wsize;  	} +	pgio->pg_maxretrans = io_maxretrans;  	return;  out_mds: @@ -1006,7 +1010,7 @@ ff_layout_pg_get_mirror_count_write(struct nfs_pageio_descriptor *pgio,  {  	if (!pgio->pg_lseg) {  		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, -						   req->wb_context, +						   nfs_req_openctx(req),  						   0,  						   NFS4_MAX_UINT64,  						   IOMODE_RW, @@ -2515,3 +2519,7 @@ MODULE_DESCRIPTION("The NFSv4 flexfile layout driver");  module_init(nfs4flexfilelayout_init);  module_exit(nfs4flexfilelayout_exit); + +module_param(io_maxretrans, ushort, 0644); +MODULE_PARM_DESC(io_maxretrans, "The  number of times the NFSv4.1 client " +			"retries an I/O request before returning an error. "); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 414a90d48493..3bc2550cfe4e 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -885,10 +885,14 @@ struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)  		spin_lock(&inode->i_lock);  		res = __nfs_find_lock_context(ctx);  		if (res == NULL) { -			list_add_tail_rcu(&new->list, &ctx->lock_context.list); -			new->open_context = ctx; -			res = new; -			new = NULL; +			new->open_context = get_nfs_open_context(ctx); +			if (new->open_context) { +				list_add_tail_rcu(&new->list, +						&ctx->lock_context.list); +				res = new; +				new = NULL; +			} else +				res = ERR_PTR(-EBADF);  		}  		spin_unlock(&inode->i_lock);  		kfree(new); @@ -906,6 +910,7 @@ void nfs_put_lock_context(struct nfs_lock_context *l_ctx)  		return;  	list_del_rcu(&l_ctx->list);  	spin_unlock(&inode->i_lock); +	put_nfs_open_context(ctx);  	kfree_rcu(l_ctx, rcu_head);  }  EXPORT_SYMBOL_GPL(nfs_put_lock_context); @@ -2055,17 +2060,11 @@ struct inode *nfs_alloc_inode(struct super_block *sb)  }  EXPORT_SYMBOL_GPL(nfs_alloc_inode); -static void nfs_i_callback(struct rcu_head *head) +void nfs_free_inode(struct inode *inode)  { -	struct inode *inode = container_of(head, struct inode, i_rcu);  	kmem_cache_free(nfs_inode_cachep, NFS_I(inode));  } - -void nfs_destroy_inode(struct inode *inode) -{ -	call_rcu(&inode->i_rcu, nfs_i_callback); -} -EXPORT_SYMBOL_GPL(nfs_destroy_inode); +EXPORT_SYMBOL_GPL(nfs_free_inode);  static inline void nfs4_init_once(struct nfs_inode *nfsi)  { diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index c7cf23ae6597..498fab72f70b 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -84,6 +84,7 @@ struct nfs_client_initdata {  	u32 minorversion;  	struct net *net;  	const struct rpc_timeout *timeparms; +	const struct cred *cred;  };  /* @@ -381,7 +382,7 @@ int nfs_check_flags(int);  /* inode.c */  extern struct workqueue_struct *nfsiod_workqueue;  extern struct inode *nfs_alloc_inode(struct super_block *sb); -extern void nfs_destroy_inode(struct inode *); +extern void nfs_free_inode(struct inode *);  extern int nfs_write_inode(struct inode *, struct writeback_control *);  extern int nfs_drop_inode(struct inode *);  extern void nfs_clear_inode(struct inode *); @@ -766,15 +767,10 @@ static inline bool nfs_error_is_fatal(int err)  	case -ESTALE:  	case -E2BIG:  	case -ENOMEM: +	case -ETIMEDOUT:  		return true;  	default:  		return false;  	}  } -static inline void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) -{ -	ctx->error = error; -	smp_wmb(); -	set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); -} diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index d979ff4fee7e..cb7c10e9721e 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -163,6 +163,7 @@ int nfs_mount(struct nfs_mount_request *info)  		.program	= &mnt_program,  		.version	= info->version,  		.authflavor	= RPC_AUTH_UNIX, +		.cred		= current_cred(),  	};  	struct rpc_clnt		*mnt_clnt;  	int			status; @@ -249,6 +250,7 @@ void nfs_umount(const struct nfs_mount_request *info)  		.version	= info->version,  		.authflavor	= RPC_AUTH_UNIX,  		.flags		= RPC_CLNT_CREATE_NOPING, +		.cred		= current_cred(),  	};  	struct rpc_message msg	= {  		.rpc_argp	= info->dirpath, diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index a7ed29de0a40..572794dab4b1 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -76,6 +76,20 @@ static int nfs_stat_to_errno(enum nfs_stat);   * or decoded inline.   */ +static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt) +{ +	if (clnt && clnt->cl_cred) +		return clnt->cl_cred->user_ns; +	return &init_user_ns; +} + +static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp) +{ +	if (rqstp->rq_task) +		return rpc_userns(rqstp->rq_task->tk_client); +	return &init_user_ns; +} +  /*   *	typedef opaque	nfsdata<>;   */ @@ -248,7 +262,8 @@ static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)   *	};   *   */ -static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) +static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, +		struct user_namespace *userns)  {  	u32 rdev, type;  	__be32 *p; @@ -263,10 +278,10 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)  	fattr->mode = be32_to_cpup(p++);  	fattr->nlink = be32_to_cpup(p++); -	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++)); +	fattr->uid = make_kuid(userns, be32_to_cpup(p++));  	if (!uid_valid(fattr->uid))  		goto out_uid; -	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++)); +	fattr->gid = make_kgid(userns, be32_to_cpup(p++));  	if (!gid_valid(fattr->gid))  		goto out_gid; @@ -321,7 +336,8 @@ static __be32 *xdr_time_not_set(__be32 *p)  	return p;  } -static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) +static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr, +		struct user_namespace *userns)  {  	struct timespec ts;  	__be32 *p; @@ -333,11 +349,11 @@ static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)  	else  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);  	if (attr->ia_valid & ATTR_UID) -		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid)); +		*p++ = cpu_to_be32(from_kuid_munged(userns, attr->ia_uid));  	else  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);  	if (attr->ia_valid & ATTR_GID) -		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid)); +		*p++ = cpu_to_be32(from_kgid_munged(userns, attr->ia_gid));  	else  		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);  	if (attr->ia_valid & ATTR_SIZE) @@ -451,7 +467,8 @@ out_cheating:   *	};   */  static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result, -			   __u32 *op_status) +			   __u32 *op_status, +			   struct user_namespace *userns)  {  	enum nfs_stat status;  	int error; @@ -463,7 +480,7 @@ static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result,  		*op_status = status;  	if (status != NFS_OK)  		goto out_default; -	error = decode_fattr(xdr, result); +	error = decode_fattr(xdr, result, userns);  out:  	return error;  out_default: @@ -498,19 +515,21 @@ static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,   *		void;   *	};   */ -static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result) +static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result, +		struct user_namespace *userns)  {  	int error;  	error = decode_fhandle(xdr, result->fh);  	if (unlikely(error))  		goto out; -	error = decode_fattr(xdr, result->fattr); +	error = decode_fattr(xdr, result->fattr, userns);  out:  	return error;  } -static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result) +static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result, +		struct user_namespace *userns)  {  	enum nfs_stat status;  	int error; @@ -520,7 +539,7 @@ static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)  		goto out;  	if (status != NFS_OK)  		goto out_default; -	error = decode_diropok(xdr, result); +	error = decode_diropok(xdr, result, userns);  out:  	return error;  out_default: @@ -559,7 +578,7 @@ static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,  	const struct nfs_sattrargs *args = data;  	encode_fhandle(xdr, args->fh); -	encode_sattr(xdr, args->sattr); +	encode_sattr(xdr, args->sattr, rpc_rqst_userns(req));  }  static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req, @@ -674,7 +693,7 @@ static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,  	const struct nfs_createargs *args = data;  	encode_diropargs(xdr, args->fh, args->name, args->len); -	encode_sattr(xdr, args->sattr); +	encode_sattr(xdr, args->sattr, rpc_rqst_userns(req));  }  static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req, @@ -741,7 +760,7 @@ static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,  	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);  	encode_path(xdr, args->pages, args->pathlen); -	encode_sattr(xdr, args->sattr); +	encode_sattr(xdr, args->sattr, rpc_rqst_userns(req));  }  /* @@ -803,13 +822,13 @@ out_default:  static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,  				 void *result)  { -	return decode_attrstat(xdr, result, NULL); +	return decode_attrstat(xdr, result, NULL, rpc_rqst_userns(req));  }  static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,  				 void *result)  { -	return decode_diropres(xdr, result); +	return decode_diropres(xdr, result, rpc_rqst_userns(req));  }  /* @@ -864,7 +883,7 @@ static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,  	result->op_status = status;  	if (status != NFS_OK)  		goto out_default; -	error = decode_fattr(xdr, result->fattr); +	error = decode_fattr(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	error = decode_nfsdata(xdr, result); @@ -881,7 +900,8 @@ static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,  	/* All NFSv2 writes are "file sync" writes */  	result->verf->committed = NFS_FILE_SYNC; -	return decode_attrstat(xdr, result->fattr, &result->op_status); +	return decode_attrstat(xdr, result->fattr, &result->op_status, +			rpc_rqst_userns(req));  }  /** diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c index 7879f2a0fcfd..1afdb0f7473f 100644 --- a/fs/nfs/nfs3client.c +++ b/fs/nfs/nfs3client.c @@ -91,6 +91,7 @@ struct nfs_client *nfs3_set_ds_client(struct nfs_server *mds_srv,  		.proto = ds_proto,  		.net = mds_clp->cl_net,  		.timeparms = &ds_timeout, +		.cred = mds_srv->cred,  	};  	struct nfs_client *clp;  	char buf[INET6_ADDRSTRLEN + 1]; diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 110358f4986d..abbbdde97e31 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -104,6 +104,20 @@ static const umode_t nfs_type2fmt[] = {  	[NF3FIFO] = S_IFIFO,  }; +static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt) +{ +	if (clnt && clnt->cl_cred) +		return clnt->cl_cred->user_ns; +	return &init_user_ns; +} + +static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp) +{ +	if (rqstp->rq_task) +		return rpc_userns(rqstp->rq_task->tk_client); +	return &init_user_ns; +} +  /*   * Encode/decode NFSv3 basic data types   * @@ -516,7 +530,8 @@ static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec *timep)   *		set_mtime	mtime;   *	};   */ -static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr) +static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr, +		struct user_namespace *userns)  {  	struct timespec ts;  	u32 nbytes; @@ -551,13 +566,13 @@ static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)  	if (attr->ia_valid & ATTR_UID) {  		*p++ = xdr_one; -		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid)); +		*p++ = cpu_to_be32(from_kuid_munged(userns, attr->ia_uid));  	} else  		*p++ = xdr_zero;  	if (attr->ia_valid & ATTR_GID) {  		*p++ = xdr_one; -		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid)); +		*p++ = cpu_to_be32(from_kgid_munged(userns, attr->ia_gid));  	} else  		*p++ = xdr_zero; @@ -606,7 +621,8 @@ static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)   *		nfstime3	ctime;   *	};   */ -static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) +static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr, +		struct user_namespace *userns)  {  	umode_t fmode;  	__be32 *p; @@ -619,10 +635,10 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)  	fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;  	fattr->nlink = be32_to_cpup(p++); -	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++)); +	fattr->uid = make_kuid(userns, be32_to_cpup(p++));  	if (!uid_valid(fattr->uid))  		goto out_uid; -	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++)); +	fattr->gid = make_kgid(userns, be32_to_cpup(p++));  	if (!gid_valid(fattr->gid))  		goto out_gid; @@ -659,7 +675,8 @@ out_gid:   *		void;   *	};   */ -static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr) +static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr, +		struct user_namespace *userns)  {  	__be32 *p; @@ -667,7 +684,7 @@ static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)  	if (unlikely(!p))  		return -EIO;  	if (*p != xdr_zero) -		return decode_fattr3(xdr, fattr); +		return decode_fattr3(xdr, fattr, userns);  	return 0;  } @@ -728,14 +745,15 @@ static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)  	return 0;  } -static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr) +static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr, +		struct user_namespace *userns)  {  	int error;  	error = decode_pre_op_attr(xdr, fattr);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, fattr); +	error = decode_post_op_attr(xdr, fattr, userns);  out:  	return error;  } @@ -837,7 +855,7 @@ static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,  {  	const struct nfs3_sattrargs *args = data;  	encode_nfs_fh3(xdr, args->fh); -	encode_sattr3(xdr, args->sattr); +	encode_sattr3(xdr, args->sattr, rpc_rqst_userns(req));  	encode_sattrguard3(xdr, args);  } @@ -998,13 +1016,14 @@ static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,   *	};   */  static void encode_createhow3(struct xdr_stream *xdr, -			      const struct nfs3_createargs *args) +			      const struct nfs3_createargs *args, +			      struct user_namespace *userns)  {  	encode_uint32(xdr, args->createmode);  	switch (args->createmode) {  	case NFS3_CREATE_UNCHECKED:  	case NFS3_CREATE_GUARDED: -		encode_sattr3(xdr, args->sattr); +		encode_sattr3(xdr, args->sattr, userns);  		break;  	case NFS3_CREATE_EXCLUSIVE:  		encode_createverf3(xdr, args->verifier); @@ -1021,7 +1040,7 @@ static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,  	const struct nfs3_createargs *args = data;  	encode_diropargs3(xdr, args->fh, args->name, args->len); -	encode_createhow3(xdr, args); +	encode_createhow3(xdr, args, rpc_rqst_userns(req));  }  /* @@ -1039,7 +1058,7 @@ static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,  	const struct nfs3_mkdirargs *args = data;  	encode_diropargs3(xdr, args->fh, args->name, args->len); -	encode_sattr3(xdr, args->sattr); +	encode_sattr3(xdr, args->sattr, rpc_rqst_userns(req));  }  /* @@ -1056,11 +1075,12 @@ static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,   *	};   */  static void encode_symlinkdata3(struct xdr_stream *xdr, -				const void *data) +				const void *data, +				struct user_namespace *userns)  {  	const struct nfs3_symlinkargs *args = data; -	encode_sattr3(xdr, args->sattr); +	encode_sattr3(xdr, args->sattr, userns);  	encode_nfspath3(xdr, args->pages, args->pathlen);  } @@ -1071,7 +1091,7 @@ static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,  	const struct nfs3_symlinkargs *args = data;  	encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen); -	encode_symlinkdata3(xdr, args); +	encode_symlinkdata3(xdr, args, rpc_rqst_userns(req));  	xdr->buf->flags |= XDRBUF_WRITE;  } @@ -1100,24 +1120,26 @@ static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,   *	};   */  static void encode_devicedata3(struct xdr_stream *xdr, -			       const struct nfs3_mknodargs *args) +			       const struct nfs3_mknodargs *args, +			       struct user_namespace *userns)  { -	encode_sattr3(xdr, args->sattr); +	encode_sattr3(xdr, args->sattr, userns);  	encode_specdata3(xdr, args->rdev);  }  static void encode_mknoddata3(struct xdr_stream *xdr, -			      const struct nfs3_mknodargs *args) +			      const struct nfs3_mknodargs *args, +			      struct user_namespace *userns)  {  	encode_ftype3(xdr, args->type);  	switch (args->type) {  	case NF3CHR:  	case NF3BLK: -		encode_devicedata3(xdr, args); +		encode_devicedata3(xdr, args, userns);  		break;  	case NF3SOCK:  	case NF3FIFO: -		encode_sattr3(xdr, args->sattr); +		encode_sattr3(xdr, args->sattr, userns);  		break;  	case NF3REG:  	case NF3DIR: @@ -1134,7 +1156,7 @@ static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,  	const struct nfs3_mknodargs *args = data;  	encode_diropargs3(xdr, args->fh, args->name, args->len); -	encode_mknoddata3(xdr, args); +	encode_mknoddata3(xdr, args, rpc_rqst_userns(req));  }  /* @@ -1379,7 +1401,7 @@ static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,  		goto out;  	if (status != NFS3_OK)  		goto out_default; -	error = decode_fattr3(xdr, result); +	error = decode_fattr3(xdr, result, rpc_rqst_userns(req));  out:  	return error;  out_default: @@ -1414,7 +1436,7 @@ static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result); +	error = decode_wcc_data(xdr, result, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -1449,6 +1471,7 @@ static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,  				   struct xdr_stream *xdr,  				   void *data)  { +	struct user_namespace *userns = rpc_rqst_userns(req);  	struct nfs3_diropres *result = data;  	enum nfs_stat status;  	int error; @@ -1461,14 +1484,14 @@ static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,  	error = decode_nfs_fh3(xdr, result->fh);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, userns);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->dir_attr); +	error = decode_post_op_attr(xdr, result->dir_attr, userns);  out:  	return error;  out_default: -	error = decode_post_op_attr(xdr, result->dir_attr); +	error = decode_post_op_attr(xdr, result->dir_attr, userns);  	if (unlikely(error))  		goto out;  	return nfs3_stat_to_errno(status); @@ -1504,7 +1527,7 @@ static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -1545,7 +1568,7 @@ static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result); +	error = decode_post_op_attr(xdr, result, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -1623,7 +1646,7 @@ static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	result->op_status = status; @@ -1694,7 +1717,7 @@ static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result->fattr); +	error = decode_wcc_data(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	result->op_status = status; @@ -1728,14 +1751,15 @@ out_status:   *	};   */  static int decode_create3resok(struct xdr_stream *xdr, -			       struct nfs3_diropres *result) +			       struct nfs3_diropres *result, +			       struct user_namespace *userns)  {  	int error;  	error = decode_post_op_fh3(xdr, result->fh);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, userns);  	if (unlikely(error))  		goto out;  	/* The server isn't required to return a file handle. @@ -1744,7 +1768,7 @@ static int decode_create3resok(struct xdr_stream *xdr,  	 * values for the new object. */  	if (result->fh->size == 0)  		result->fattr->valid = 0; -	error = decode_wcc_data(xdr, result->dir_attr); +	error = decode_wcc_data(xdr, result->dir_attr, userns);  out:  	return error;  } @@ -1753,6 +1777,7 @@ static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,  				   struct xdr_stream *xdr,  				   void *data)  { +	struct user_namespace *userns = rpc_rqst_userns(req);  	struct nfs3_diropres *result = data;  	enum nfs_stat status;  	int error; @@ -1762,11 +1787,11 @@ static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,  		goto out;  	if (status != NFS3_OK)  		goto out_default; -	error = decode_create3resok(xdr, result); +	error = decode_create3resok(xdr, result, userns);  out:  	return error;  out_default: -	error = decode_wcc_data(xdr, result->dir_attr); +	error = decode_wcc_data(xdr, result->dir_attr, userns);  	if (unlikely(error))  		goto out;  	return nfs3_stat_to_errno(status); @@ -1801,7 +1826,7 @@ static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result->dir_attr); +	error = decode_wcc_data(xdr, result->dir_attr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -1836,6 +1861,7 @@ static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,  				   struct xdr_stream *xdr,  				   void *data)  { +	struct user_namespace *userns = rpc_rqst_userns(req);  	struct nfs_renameres *result = data;  	enum nfs_stat status;  	int error; @@ -1843,10 +1869,10 @@ static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result->old_fattr); +	error = decode_wcc_data(xdr, result->old_fattr, userns);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result->new_fattr); +	error = decode_wcc_data(xdr, result->new_fattr, userns);  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -1880,6 +1906,7 @@ out_status:  static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,  				 void *data)  { +	struct user_namespace *userns = rpc_rqst_userns(req);  	struct nfs3_linkres *result = data;  	enum nfs_stat status;  	int error; @@ -1887,10 +1914,10 @@ static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, userns);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result->dir_attr); +	error = decode_wcc_data(xdr, result->dir_attr, userns);  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -1939,6 +1966,7 @@ out_status:  int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,  		       bool plus)  { +	struct user_namespace *userns = rpc_userns(entry->server->client);  	struct nfs_entry old = *entry;  	__be32 *p;  	int error; @@ -1973,7 +2001,7 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,  	if (plus) {  		entry->fattr->valid = 0; -		error = decode_post_op_attr(xdr, entry->fattr); +		error = decode_post_op_attr(xdr, entry->fattr, userns);  		if (unlikely(error))  			return error;  		if (entry->fattr->valid & NFS_ATTR_FATTR_V3) @@ -2045,11 +2073,12 @@ static int decode_dirlist3(struct xdr_stream *xdr)  }  static int decode_readdir3resok(struct xdr_stream *xdr, -				struct nfs3_readdirres *result) +				struct nfs3_readdirres *result, +				struct user_namespace *userns)  {  	int error; -	error = decode_post_op_attr(xdr, result->dir_attr); +	error = decode_post_op_attr(xdr, result->dir_attr, userns);  	if (unlikely(error))  		goto out;  	/* XXX: do we need to check if result->verf != NULL ? */ @@ -2074,11 +2103,11 @@ static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,  		goto out;  	if (status != NFS3_OK)  		goto out_default; -	error = decode_readdir3resok(xdr, result); +	error = decode_readdir3resok(xdr, result, rpc_rqst_userns(req));  out:  	return error;  out_default: -	error = decode_post_op_attr(xdr, result->dir_attr); +	error = decode_post_op_attr(xdr, result->dir_attr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	return nfs3_stat_to_errno(status); @@ -2138,7 +2167,7 @@ static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -2212,7 +2241,7 @@ static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -2273,7 +2302,7 @@ static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	if (status != NFS3_OK) @@ -2315,7 +2344,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,  	error = decode_nfsstat3(xdr, &status);  	if (unlikely(error))  		goto out; -	error = decode_wcc_data(xdr, result->fattr); +	error = decode_wcc_data(xdr, result->fattr, rpc_rqst_userns(req));  	if (unlikely(error))  		goto out;  	result->op_status = status; @@ -2331,14 +2360,15 @@ out_status:  #ifdef CONFIG_NFS_V3_ACL  static inline int decode_getacl3resok(struct xdr_stream *xdr, -				      struct nfs3_getaclres *result) +				      struct nfs3_getaclres *result, +				      struct user_namespace *userns)  {  	struct posix_acl **acl;  	unsigned int *aclcnt;  	size_t hdrlen;  	int error; -	error = decode_post_op_attr(xdr, result->fattr); +	error = decode_post_op_attr(xdr, result->fattr, userns);  	if (unlikely(error))  		goto out;  	error = decode_uint32(xdr, &result->mask); @@ -2386,7 +2416,7 @@ static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,  		goto out;  	if (status != NFS3_OK)  		goto out_default; -	error = decode_getacl3resok(xdr, result); +	error = decode_getacl3resok(xdr, result, rpc_rqst_userns(req));  out:  	return error;  out_default: @@ -2405,7 +2435,7 @@ static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,  		goto out;  	if (status != NFS3_OK)  		goto out_default; -	error = decode_post_op_attr(xdr, result); +	error = decode_post_op_attr(xdr, result, rpc_rqst_userns(req));  out:  	return error;  out_default: diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 06ac3d9ac7c6..8a38a254f516 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -206,6 +206,7 @@ struct nfs4_exception {  	unsigned char delay : 1,  		      recovering : 1,  		      retry : 1; +	bool interruptible;  };  struct nfs4_state_recovery_ops { diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 1339ede979af..3ce246346f02 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -870,6 +870,7 @@ static int nfs4_set_client(struct nfs_server *server,  		.minorversion = minorversion,  		.net = net,  		.timeparms = timeparms, +		.cred = server->cred,  	};  	struct nfs_client *clp; @@ -931,6 +932,7 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,  		.minorversion = minor_version,  		.net = mds_clp->cl_net,  		.timeparms = &ds_timeout, +		.cred = mds_srv->cred,  	};  	char buf[INET6_ADDRSTRLEN + 1]; @@ -1107,6 +1109,8 @@ struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,  	if (!server)  		return ERR_PTR(-ENOMEM); +	server->cred = get_cred(current_cred()); +  	auth_probe = mount_info->parsed->auth_info.flavor_len < 1;  	/* set up the general RPC client */ @@ -1143,6 +1147,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,  	parent_server = NFS_SB(data->sb);  	parent_client = parent_server->nfs_client; +	server->cred = get_cred(parent_server->cred); +  	/* Initialise the client representation from the parent server */  	nfs_server_copy_userdata(server, parent_server); diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 00d17198ee12..cf42a8b939e3 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -125,7 +125,7 @@ nfs4_file_flush(struct file *file, fl_owner_t id)  		return filemap_fdatawrite(file->f_mapping);  	/* Flush writes to the server and return any errors */ -	return vfs_fsync(file, 0); +	return nfs_wb_all(inode);  }  #ifdef CONFIG_NFS_V4_2 @@ -187,7 +187,7 @@ static loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off,  	bool same_inode = false;  	int ret; -	if (remap_flags & ~REMAP_FILE_ADVISORY) +	if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))  		return -EINVAL;  	/* check alignment w.r.t. clone_blksize */ diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index bf34ddaa2ad7..4884fdae28fb 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -69,8 +69,16 @@ struct idmap {  	struct rpc_pipe		*idmap_pipe;  	struct idmap_legacy_upcalldata *idmap_upcall_data;  	struct mutex		idmap_mutex; +	const struct cred	*cred;  }; +static struct user_namespace *idmap_userns(const struct idmap *idmap) +{ +	if (idmap && idmap->cred) +		return idmap->cred->user_ns; +	return &init_user_ns; +} +  /**   * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields   * @fattr: fully initialised struct nfs_fattr @@ -271,14 +279,15 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen,  					 const char *type, struct idmap *idmap)  {  	char *desc; -	struct key *rkey; +	struct key *rkey = ERR_PTR(-EAGAIN);  	ssize_t ret;  	ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc);  	if (ret < 0)  		return ERR_PTR(ret); -	rkey = request_key(&key_type_id_resolver, desc, ""); +	if (!idmap->cred || idmap->cred->user_ns == &init_user_ns) +		rkey = request_key(&key_type_id_resolver, desc, "");  	if (IS_ERR(rkey)) {  		mutex_lock(&idmap->idmap_mutex);  		rkey = request_key_with_auxdata(&key_type_id_resolver_legacy, @@ -452,6 +461,9 @@ nfs_idmap_new(struct nfs_client *clp)  	if (idmap == NULL)  		return -ENOMEM; +	mutex_init(&idmap->idmap_mutex); +	idmap->cred = get_cred(clp->cl_rpcclient->cl_cred); +  	rpc_init_pipe_dir_object(&idmap->idmap_pdo,  			&nfs_idmap_pipe_dir_object_ops,  			idmap); @@ -462,7 +474,6 @@ nfs_idmap_new(struct nfs_client *clp)  		goto err;  	}  	idmap->idmap_pipe = pipe; -	mutex_init(&idmap->idmap_mutex);  	error = rpc_add_pipe_dir_object(clp->cl_net,  			&clp->cl_rpcclient->cl_pipedir_objects, @@ -475,6 +486,7 @@ nfs_idmap_new(struct nfs_client *clp)  err_destroy_pipe:  	rpc_destroy_pipe_data(idmap->idmap_pipe);  err: +	put_cred(idmap->cred);  	kfree(idmap);  	return error;  } @@ -491,6 +503,7 @@ nfs_idmap_delete(struct nfs_client *clp)  			&clp->cl_rpcclient->cl_pipedir_objects,  			&idmap->idmap_pdo);  	rpc_destroy_pipe_data(idmap->idmap_pipe); +	put_cred(idmap->cred);  	kfree(idmap);  } @@ -735,7 +748,7 @@ int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_  	if (!nfs_map_string_to_numeric(name, namelen, &id))  		ret = nfs_idmap_lookup_id(name, namelen, "uid", &id, idmap);  	if (ret == 0) { -		*uid = make_kuid(&init_user_ns, id); +		*uid = make_kuid(idmap_userns(idmap), id);  		if (!uid_valid(*uid))  			ret = -ERANGE;  	} @@ -752,7 +765,7 @@ int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size  	if (!nfs_map_string_to_numeric(name, namelen, &id))  		ret = nfs_idmap_lookup_id(name, namelen, "gid", &id, idmap);  	if (ret == 0) { -		*gid = make_kgid(&init_user_ns, id); +		*gid = make_kgid(idmap_userns(idmap), id);  		if (!gid_valid(*gid))  			ret = -ERANGE;  	} @@ -766,7 +779,7 @@ int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf,  	int ret = -EINVAL;  	__u32 id; -	id = from_kuid(&init_user_ns, uid); +	id = from_kuid_munged(idmap_userns(idmap), uid);  	if (!(server->caps & NFS_CAP_UIDGID_NOMAP))  		ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap);  	if (ret < 0) @@ -780,7 +793,7 @@ int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf,  	int ret = -EINVAL;  	__u32 id; -	id = from_kgid(&init_user_ns, gid); +	id = from_kgid_munged(idmap_userns(idmap), gid);  	if (!(server->caps & NFS_CAP_UIDGID_NOMAP))  		ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap);  	if (ret < 0) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 741ff8c9c6ed..c29cbef6b53f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -400,17 +400,32 @@ static long nfs4_update_delay(long *timeout)  	return ret;  } -static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) +static int nfs4_delay_killable(long *timeout)  { -	int res = 0; -  	might_sleep();  	freezable_schedule_timeout_killable_unsafe(  		nfs4_update_delay(timeout)); -	if (fatal_signal_pending(current)) -		res = -ERESTARTSYS; -	return res; +	if (!__fatal_signal_pending(current)) +		return 0; +	return -EINTR; +} + +static int nfs4_delay_interruptible(long *timeout) +{ +	might_sleep(); + +	freezable_schedule_timeout_interruptible(nfs4_update_delay(timeout)); +	if (!signal_pending(current)) +		return 0; +	return __fatal_signal_pending(current) ? -EINTR :-ERESTARTSYS; +} + +static int nfs4_delay(long *timeout, bool interruptible) +{ +	if (interruptible) +		return nfs4_delay_interruptible(timeout); +	return nfs4_delay_killable(timeout);  }  /* This is the error handling routine for processes that are allowed @@ -546,7 +561,8 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_  	ret = nfs4_do_handle_exception(server, errorcode, exception);  	if (exception->delay) { -		ret = nfs4_delay(server->client, &exception->timeout); +		ret = nfs4_delay(&exception->timeout, +				exception->interruptible);  		goto out_retry;  	}  	if (exception->recovering) { @@ -978,10 +994,8 @@ int nfs4_setup_sequence(struct nfs_client *client,  	if (res->sr_slot != NULL)  		goto out_start; -	if (session) { +	if (session)  		tbl = &session->fc_slot_table; -		task->tk_timeout = 0; -	}  	spin_lock(&tbl->slot_tbl_lock);  	/* The state manager will wait until the slot table is empty */ @@ -990,9 +1004,8 @@ int nfs4_setup_sequence(struct nfs_client *client,  	slot = nfs4_alloc_slot(tbl);  	if (IS_ERR(slot)) { -		/* Try again in 1/4 second */  		if (slot == ERR_PTR(-ENOMEM)) -			task->tk_timeout = HZ >> 2; +			goto out_sleep_timeout;  		goto out_sleep;  	}  	spin_unlock(&tbl->slot_tbl_lock); @@ -1004,11 +1017,20 @@ out_start:  	nfs41_sequence_res_init(res);  	rpc_call_start(task);  	return 0; - +out_sleep_timeout: +	/* Try again in 1/4 second */ +	if (args->sa_privileged) +		rpc_sleep_on_priority_timeout(&tbl->slot_tbl_waitq, task, +				jiffies + (HZ >> 2), RPC_PRIORITY_PRIVILEGED); +	else +		rpc_sleep_on_timeout(&tbl->slot_tbl_waitq, task, +				NULL, jiffies + (HZ >> 2)); +	spin_unlock(&tbl->slot_tbl_lock); +	return -EAGAIN;  out_sleep:  	if (args->sa_privileged)  		rpc_sleep_on_priority(&tbl->slot_tbl_waitq, task, -				NULL, RPC_PRIORITY_PRIVILEGED); +				RPC_PRIORITY_PRIVILEGED);  	else  		rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);  	spin_unlock(&tbl->slot_tbl_lock); @@ -3060,7 +3082,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,  					int *opened)  {  	struct nfs_server *server = NFS_SERVER(dir); -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	struct nfs4_state *res;  	struct nfs4_open_createattrs c = {  		.label = label, @@ -3673,7 +3697,9 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f  int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = nfs4_handle_exception(server, @@ -3715,7 +3741,9 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,  static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,  		struct nfs_fsinfo *info)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_lookup_root(server, fhandle, info); @@ -3942,7 +3970,9 @@ static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,  				struct nfs_fattr *fattr, struct nfs4_label *label,  				struct inode *inode)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_proc_getattr(server, fhandle, fattr, label, inode); @@ -4065,7 +4095,9 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,  				   const struct qstr *name, struct nfs_fh *fhandle,  				   struct nfs_fattr *fattr, struct nfs4_label *label)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	struct rpc_clnt *client = *clnt;  	int err;  	do { @@ -4169,7 +4201,9 @@ static int _nfs4_proc_lookupp(struct inode *inode,  static int nfs4_proc_lookupp(struct inode *inode, struct nfs_fh *fhandle,  			     struct nfs_fattr *fattr, struct nfs4_label *label)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_proc_lookupp(inode, fhandle, fattr, label); @@ -4216,7 +4250,9 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry  static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_proc_access(inode, entry); @@ -4271,7 +4307,9 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page,  static int nfs4_proc_readlink(struct inode *inode, struct page *page,  		unsigned int pgbase, unsigned int pglen)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_proc_readlink(inode, page, pgbase, pglen); @@ -4347,7 +4385,9 @@ _nfs4_proc_remove(struct inode *dir, const struct qstr *name, u32 ftype)  static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	struct inode *inode = d_inode(dentry);  	int err; @@ -4368,7 +4408,9 @@ static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)  static int nfs4_proc_rmdir(struct inode *dir, const struct qstr *name)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do { @@ -4527,7 +4569,9 @@ out:  static int nfs4_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = nfs4_handle_exception(NFS_SERVER(inode), @@ -4634,7 +4678,9 @@ out:  static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,  		struct page *page, unsigned int len, struct iattr *sattr)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	struct nfs4_label l, *label = NULL;  	int err; @@ -4673,7 +4719,9 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,  		struct iattr *sattr)  {  	struct nfs_server *server = NFS_SERVER(dir); -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	struct nfs4_label l, *label = NULL;  	int err; @@ -4733,7 +4781,9 @@ static int _nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred,  static int nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred,  		u64 cookie, struct page **pages, unsigned int count, bool plus)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_proc_readdir(dentry, cred, cookie, @@ -4784,7 +4834,9 @@ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,  		struct iattr *sattr, dev_t rdev)  {  	struct nfs_server *server = NFS_SERVER(dir); -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	struct nfs4_label l, *label = NULL;  	int err; @@ -4826,7 +4878,9 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,  static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = nfs4_handle_exception(server, @@ -4857,7 +4911,9 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,  static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	unsigned long now = jiffies;  	int err; @@ -4919,7 +4975,9 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle  static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,  		struct nfs_pathconf *pathconf)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do { @@ -5488,7 +5546,9 @@ out_free:  static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	ssize_t ret;  	do {  		ret = __nfs4_get_acl_uncached(inode, buf, buflen); @@ -5622,7 +5682,9 @@ static int _nfs4_get_security_label(struct inode *inode, void *buf,  static int nfs4_get_security_label(struct inode *inode, void *buf,  					size_t buflen)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) @@ -6263,7 +6325,9 @@ out:  static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do { @@ -6827,6 +6891,7 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *  	struct nfs4_exception exception = {  		.state = state,  		.inode = state->inode, +		.interruptible = true,  	};  	int err; @@ -7240,7 +7305,9 @@ int nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,  			   struct nfs4_fs_locations *fs_locations,  			   struct page *page)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs4_proc_fs_locations(client, dir, name, @@ -7383,7 +7450,9 @@ int nfs4_proc_get_locations(struct inode *inode,  	struct nfs_client *clp = server->nfs_client;  	const struct nfs4_mig_recovery_ops *ops =  					clp->cl_mvops->mig_recovery_ops; -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int status;  	dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__, @@ -7507,7 +7576,9 @@ int nfs4_proc_fsid_present(struct inode *inode, const struct cred *cred)  	struct nfs_client *clp = server->nfs_client;  	const struct nfs4_mig_recovery_ops *ops =  					clp->cl_mvops->mig_recovery_ops; -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int status;  	dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__, @@ -7573,7 +7644,9 @@ static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct  int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,  		      struct nfs4_secinfo_flavors *flavors)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = -NFS4ERR_WRONGSEC; @@ -9263,7 +9336,9 @@ static int  nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,  			   struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		/* first try using integrity protection */ @@ -9430,7 +9505,9 @@ static int nfs41_test_stateid(struct nfs_server *server,  		nfs4_stateid *stateid,  		const struct cred *cred)  { -	struct nfs4_exception exception = { }; +	struct nfs4_exception exception = { +		.interruptible = true, +	};  	int err;  	do {  		err = _nfs41_test_stateid(server, stateid, cred); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 3de36479ed7a..e2e3c4f04d3e 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -159,6 +159,10 @@ int nfs40_discover_server_trunking(struct nfs_client *clp,  		/* Sustain the lease, even if it's empty.  If the clientid4  		 * goes stale it's of no use for trunking discovery. */  		nfs4_schedule_state_renewal(*result); + +		/* If the client state need to recover, do it. */ +		if (clp->cl_state) +			nfs4_schedule_state_manager(clp);  	}  out:  	return status; @@ -2346,8 +2350,7 @@ static void nfs41_handle_recallable_state_revoked(struct nfs_client *clp)  {  	/* FIXME: For now, we destroy all layouts. */  	pnfs_destroy_all_layouts(clp); -	/* FIXME: For now, we test all delegations+open state+locks. */ -	nfs41_handle_some_state_revoked(clp); +	nfs_test_expired_all_delegations(clp);  	dprintk("%s: Recallable state revoked on server %s!\n", __func__,  			clp->cl_hostname);  } diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 6fb7cb6b3f4b..689977e148cb 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c @@ -50,7 +50,7 @@ struct file_system_type nfs4_referral_fs_type = {  static const struct super_operations nfs4_sops = {  	.alloc_inode	= nfs_alloc_inode, -	.destroy_inode	= nfs_destroy_inode, +	.free_inode	= nfs_free_inode,  	.write_inode	= nfs4_write_inode,  	.drop_inode	= nfs_drop_inode,  	.statfs		= nfs_statfs, diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index e9f39fa5964b..6ec30014a439 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -16,8 +16,8 @@  #include <linux/nfs.h>  #include <linux/nfs3.h>  #include <linux/nfs4.h> -#include <linux/nfs_page.h>  #include <linux/nfs_fs.h> +#include <linux/nfs_page.h>  #include <linux/nfs_mount.h>  #include <linux/export.h> @@ -47,7 +47,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,  	hdr->req = nfs_list_entry(mirror->pg_list.next);  	hdr->inode = desc->pg_inode; -	hdr->cred = hdr->req->wb_context->cred; +	hdr->cred = nfs_req_openctx(hdr->req)->cred;  	hdr->io_start = req_offset(hdr->req);  	hdr->good_bytes = mirror->pg_count;  	hdr->io_completion = desc->pg_io_completion; @@ -295,25 +295,13 @@ out:  		nfs_release_request(head);  } -/** - * nfs_create_request - Create an NFS read/write request. - * @ctx: open context to use - * @page: page to write - * @last: last nfs request created for this page group or NULL if head - * @offset: starting offset within the page for the write - * @count: number of bytes to read/write - * - * The page must be locked by the caller. This makes sure we never - * create two different requests for the same page. - * User should ensure it is safe to sleep in this function. - */ -struct nfs_page * -nfs_create_request(struct nfs_open_context *ctx, struct page *page, -		   struct nfs_page *last, unsigned int offset, +static struct nfs_page * +__nfs_create_request(struct nfs_lock_context *l_ctx, struct page *page, +		   unsigned int pgbase, unsigned int offset,  		   unsigned int count)  {  	struct nfs_page		*req; -	struct nfs_lock_context *l_ctx; +	struct nfs_open_context *ctx = l_ctx->open_context;  	if (test_bit(NFS_CONTEXT_BAD, &ctx->flags))  		return ERR_PTR(-EBADF); @@ -322,13 +310,8 @@ nfs_create_request(struct nfs_open_context *ctx, struct page *page,  	if (req == NULL)  		return ERR_PTR(-ENOMEM); -	/* get lock context early so we can deal with alloc failures */ -	l_ctx = nfs_get_lock_context(ctx); -	if (IS_ERR(l_ctx)) { -		nfs_page_free(req); -		return ERR_CAST(l_ctx); -	}  	req->wb_lock_context = l_ctx; +	refcount_inc(&l_ctx->count);  	atomic_inc(&l_ctx->io_count);  	/* Initialize the request struct. Initially, we assume a @@ -340,15 +323,59 @@ nfs_create_request(struct nfs_open_context *ctx, struct page *page,  		get_page(page);  	}  	req->wb_offset  = offset; -	req->wb_pgbase	= offset; +	req->wb_pgbase	= pgbase;  	req->wb_bytes   = count; -	req->wb_context = get_nfs_open_context(ctx);  	kref_init(&req->wb_kref); -	nfs_page_group_init(req, last); +	req->wb_nio = 0;  	return req;  }  /** + * nfs_create_request - Create an NFS read/write request. + * @ctx: open context to use + * @page: page to write + * @offset: starting offset within the page for the write + * @count: number of bytes to read/write + * + * The page must be locked by the caller. This makes sure we never + * create two different requests for the same page. + * User should ensure it is safe to sleep in this function. + */ +struct nfs_page * +nfs_create_request(struct nfs_open_context *ctx, struct page *page, +		   unsigned int offset, unsigned int count) +{ +	struct nfs_lock_context *l_ctx = nfs_get_lock_context(ctx); +	struct nfs_page *ret; + +	if (IS_ERR(l_ctx)) +		return ERR_CAST(l_ctx); +	ret = __nfs_create_request(l_ctx, page, offset, offset, count); +	if (!IS_ERR(ret)) +		nfs_page_group_init(ret, NULL); +	nfs_put_lock_context(l_ctx); +	return ret; +} + +static struct nfs_page * +nfs_create_subreq(struct nfs_page *req, struct nfs_page *last, +		  unsigned int pgbase, unsigned int offset, +		  unsigned int count) +{ +	struct nfs_page *ret; + +	ret = __nfs_create_request(req->wb_lock_context, req->wb_page, +			pgbase, offset, count); +	if (!IS_ERR(ret)) { +		nfs_lock_request(ret); +		ret->wb_index = req->wb_index; +		nfs_page_group_init(ret, last); +		ret->wb_nio = req->wb_nio; +	} +	return ret; +} + +/**   * nfs_unlock_request - Unlock request and wake up sleepers.   * @req: pointer to request   */ @@ -386,8 +413,8 @@ void nfs_unlock_and_release_request(struct nfs_page *req)  static void nfs_clear_request(struct nfs_page *req)  {  	struct page *page = req->wb_page; -	struct nfs_open_context *ctx = req->wb_context;  	struct nfs_lock_context *l_ctx = req->wb_lock_context; +	struct nfs_open_context *ctx;  	if (page != NULL) {  		put_page(page); @@ -396,16 +423,13 @@ static void nfs_clear_request(struct nfs_page *req)  	if (l_ctx != NULL) {  		if (atomic_dec_and_test(&l_ctx->io_count)) {  			wake_up_var(&l_ctx->io_count); +			ctx = l_ctx->open_context;  			if (test_bit(NFS_CONTEXT_UNLOCK, &ctx->flags))  				rpc_wake_up(&NFS_SERVER(d_inode(ctx->dentry))->uoc_rpcwaitq);  		}  		nfs_put_lock_context(l_ctx);  		req->wb_lock_context = NULL;  	} -	if (ctx != NULL) { -		put_nfs_open_context(ctx); -		req->wb_context = NULL; -	}  }  /** @@ -550,7 +574,7 @@ static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr,  	hdr->args.pgbase = req->wb_pgbase;  	hdr->args.pages  = hdr->page_array.pagevec;  	hdr->args.count  = count; -	hdr->args.context = get_nfs_open_context(req->wb_context); +	hdr->args.context = get_nfs_open_context(nfs_req_openctx(req));  	hdr->args.lock_context = req->wb_lock_context;  	hdr->args.stable  = NFS_UNSTABLE;  	switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) { @@ -698,6 +722,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,  	desc->pg_mirrors_dynamic = NULL;  	desc->pg_mirrors = desc->pg_mirrors_static;  	nfs_pageio_mirror_init(&desc->pg_mirrors[0], bsize); +	desc->pg_maxretrans = 0;  }  /** @@ -906,9 +931,9 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev,  	struct file_lock_context *flctx;  	if (prev) { -		if (!nfs_match_open_context(req->wb_context, prev->wb_context)) +		if (!nfs_match_open_context(nfs_req_openctx(req), nfs_req_openctx(prev)))  			return false; -		flctx = d_inode(req->wb_context->dentry)->i_flctx; +		flctx = d_inode(nfs_req_openctx(req)->dentry)->i_flctx;  		if (flctx != NULL &&  		    !(list_empty_careful(&flctx->flc_posix) &&  		      list_empty_careful(&flctx->flc_flock)) && @@ -957,6 +982,15 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,  			return 0;  		mirror->pg_base = req->wb_pgbase;  	} + +	if (desc->pg_maxretrans && req->wb_nio > desc->pg_maxretrans) { +		if (NFS_SERVER(desc->pg_inode)->flags & NFS_MOUNT_SOFTERR) +			desc->pg_error = -ETIMEDOUT; +		else +			desc->pg_error = -EIO; +		return 0; +	} +  	if (!nfs_can_coalesce_requests(prev, req, desc))  		return 0;  	nfs_list_move_request(req, &mirror->pg_list); @@ -1049,14 +1083,10 @@ static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,  		pgbase += subreq->wb_bytes;  		if (bytes_left) { -			subreq = nfs_create_request(req->wb_context, -					req->wb_page, -					subreq, pgbase, bytes_left); +			subreq = nfs_create_subreq(req, subreq, pgbase, +					offset, bytes_left);  			if (IS_ERR(subreq))  				goto err_ptr; -			nfs_lock_request(subreq); -			subreq->wb_offset  = offset; -			subreq->wb_index = req->wb_index;  		}  	} while (bytes_left > 0); @@ -1158,19 +1188,14 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,  			     lastreq = lastreq->wb_this_page)  				; -			dupreq = nfs_create_request(req->wb_context, -					req->wb_page, lastreq, pgbase, bytes); +			dupreq = nfs_create_subreq(req, lastreq, +					pgbase, offset, bytes); +			nfs_page_group_unlock(req);  			if (IS_ERR(dupreq)) { -				nfs_page_group_unlock(req);  				desc->pg_error = PTR_ERR(dupreq);  				goto out_failed;  			} - -			nfs_lock_request(dupreq); -			nfs_page_group_unlock(req); -			dupreq->wb_offset = offset; -			dupreq->wb_index = req->wb_index;  		} else  			dupreq = req; diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 7066cd7c7aff..83722e936b4a 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2436,7 +2436,7 @@ pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *r  			rd_size = nfs_dreq_bytes_left(pgio->pg_dreq);  		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, -						   req->wb_context, +						   nfs_req_openctx(req),  						   req_offset(req),  						   rd_size,  						   IOMODE_READ, @@ -2463,7 +2463,7 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio,  	pnfs_generic_pg_check_range(pgio, req);  	if (pgio->pg_lseg == NULL) {  		pgio->pg_lseg = pnfs_update_layout(pgio->pg_inode, -						   req->wb_context, +						   nfs_req_openctx(req),  						   req_offset(req),  						   wb_size,  						   IOMODE_RW, diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index c0420b979d88..f15609c003d8 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -459,7 +459,7 @@ static inline bool  pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,  			 struct nfs_commit_info *cinfo, u32 ds_commit_idx)  { -	struct inode *inode = d_inode(req->wb_context->dentry); +	struct inode *inode = d_inode(nfs_req_openctx(req)->dentry);  	struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;  	if (lseg == NULL || ld->mark_request_commit == NULL) @@ -471,7 +471,7 @@ pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,  static inline bool  pnfs_clear_request_commit(struct nfs_page *req, struct nfs_commit_info *cinfo)  { -	struct inode *inode = d_inode(req->wb_context->dentry); +	struct inode *inode = d_inode(nfs_req_openctx(req)->dentry);  	struct pnfs_layoutdriver_type *ld = NFS_SERVER(inode)->pnfs_curr_ld;  	if (ld == NULL || ld->clear_request_commit == NULL) diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 1d95a60b2586..c799e540ed1e 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -92,7 +92,7 @@ EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);  static void nfs_readpage_release(struct nfs_page *req)  { -	struct inode *inode = d_inode(req->wb_context->dentry); +	struct inode *inode = d_inode(nfs_req_openctx(req)->dentry);  	dprintk("NFS: read done (%s/%llu %d@%lld)\n", inode->i_sb->s_id,  		(unsigned long long)NFS_FILEID(inode), req->wb_bytes, @@ -118,7 +118,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,  	len = nfs_page_length(page);  	if (len == 0)  		return nfs_return_empty_page(page); -	new = nfs_create_request(ctx, page, NULL, 0, len); +	new = nfs_create_request(ctx, page, 0, len);  	if (IS_ERR(new)) {  		unlock_page(page);  		return PTR_ERR(new); @@ -363,7 +363,7 @@ readpage_async_filler(void *data, struct page *page)  	if (len == 0)  		return nfs_return_empty_page(page); -	new = nfs_create_request(desc->ctx, page, NULL, 0, len); +	new = nfs_create_request(desc->ctx, page, 0, len);  	if (IS_ERR(new))  		goto out_error; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index c27ac96a95bd..d6c687419a81 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -78,7 +78,7 @@  enum {  	/* Mount options that take no arguments */ -	Opt_soft, Opt_hard, +	Opt_soft, Opt_softerr, Opt_hard,  	Opt_posix, Opt_noposix,  	Opt_cto, Opt_nocto,  	Opt_ac, Opt_noac, @@ -125,6 +125,7 @@ static const match_table_t nfs_mount_option_tokens = {  	{ Opt_sloppy, "sloppy" },  	{ Opt_soft, "soft" }, +	{ Opt_softerr, "softerr" },  	{ Opt_hard, "hard" },  	{ Opt_deprecated, "intr" },  	{ Opt_deprecated, "nointr" }, @@ -309,7 +310,7 @@ struct file_system_type nfs_xdev_fs_type = {  const struct super_operations nfs_sops = {  	.alloc_inode	= nfs_alloc_inode, -	.destroy_inode	= nfs_destroy_inode, +	.free_inode	= nfs_free_inode,  	.write_inode	= nfs_write_inode,  	.drop_inode	= nfs_drop_inode,  	.statfs		= nfs_statfs, @@ -628,7 +629,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,  		const char *str;  		const char *nostr;  	} nfs_info[] = { -		{ NFS_MOUNT_SOFT, ",soft", ",hard" }, +		{ NFS_MOUNT_SOFT, ",soft", "" }, +		{ NFS_MOUNT_SOFTERR, ",softerr", "" },  		{ NFS_MOUNT_POSIX, ",posix", "" },  		{ NFS_MOUNT_NOCTO, ",nocto", "" },  		{ NFS_MOUNT_NOAC, ",noac", "" }, @@ -658,6 +660,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,  		seq_printf(m, ",acdirmin=%u", nfss->acdirmin/HZ);  	if (nfss->acdirmax != NFS_DEF_ACDIRMAX*HZ || showdefaults)  		seq_printf(m, ",acdirmax=%u", nfss->acdirmax/HZ); +	if (!(nfss->flags & (NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR))) +			seq_puts(m, ",hard");  	for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {  		if (nfss->flags & nfs_infop->flag)  			seq_puts(m, nfs_infop->str); @@ -1239,10 +1243,15 @@ static int nfs_parse_mount_options(char *raw,  		 */  		case Opt_soft:  			mnt->flags |= NFS_MOUNT_SOFT; +			mnt->flags &= ~NFS_MOUNT_SOFTERR;  			break; -		case Opt_hard: +		case Opt_softerr: +			mnt->flags |= NFS_MOUNT_SOFTERR;  			mnt->flags &= ~NFS_MOUNT_SOFT;  			break; +		case Opt_hard: +			mnt->flags &= ~(NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR); +			break;  		case Opt_posix:  			mnt->flags |= NFS_MOUNT_POSIX;  			break; @@ -2476,6 +2485,21 @@ static int nfs_compare_super_address(struct nfs_server *server1,  	return 1;  } +static int nfs_compare_userns(const struct nfs_server *old, +		const struct nfs_server *new) +{ +	const struct user_namespace *oldns = &init_user_ns; +	const struct user_namespace *newns = &init_user_ns; + +	if (old->client && old->client->cl_cred) +		oldns = old->client->cl_cred->user_ns; +	if (new->client && new->client->cl_cred) +		newns = new->client->cl_cred->user_ns; +	if (oldns != newns) +		return 0; +	return 1; +} +  static int nfs_compare_super(struct super_block *sb, void *data)  {  	struct nfs_sb_mountdata *sb_mntdata = data; @@ -2489,6 +2513,8 @@ static int nfs_compare_super(struct super_block *sb, void *data)  		return 0;  	if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0)  		return 0; +	if (!nfs_compare_userns(old, server)) +		return 0;  	return nfs_compare_mount_options(sb, server, mntflags);  } diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 06eb44b47885..25ba299fdac2 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -26,8 +26,9 @@   * and straight-forward than readdir caching.   */ -static int nfs_symlink_filler(struct inode *inode, struct page *page) +static int nfs_symlink_filler(void *data, struct page *page)  { +	struct inode *inode = data;  	int error;  	error = NFS_PROTO(inode)->readlink(inode, page, 0, PAGE_SIZE); @@ -65,8 +66,8 @@ static const char *nfs_get_link(struct dentry *dentry,  		err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));  		if (err)  			return err; -		page = read_cache_page(&inode->i_data, 0, -					(filler_t *)nfs_symlink_filler, inode); +		page = read_cache_page(&inode->i_data, 0, nfs_symlink_filler, +				inode);  		if (IS_ERR(page))  			return ERR_CAST(page);  	} diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f3ebabaa291d..bc5bb9323412 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -244,6 +244,12 @@ static void nfs_set_pageerror(struct address_space *mapping)  	nfs_zap_mapping(mapping->host, mapping);  } +static void nfs_mapping_set_error(struct page *page, int error) +{ +	SetPageError(page); +	mapping_set_error(page_file_mapping(page), error); +} +  /*   * nfs_page_group_search_locked   * @head - head request of page group @@ -582,11 +588,10 @@ release_request:  	return ERR_PTR(ret);  } -static void nfs_write_error_remove_page(struct nfs_page *req) +static void nfs_write_error(struct nfs_page *req, int error)  { +	nfs_mapping_set_error(req->wb_page, error);  	nfs_end_page_writeback(req); -	generic_error_remove_page(page_file_mapping(req->wb_page), -				  req->wb_page);  	nfs_release_request(req);  } @@ -609,6 +614,7 @@ nfs_error_is_fatal_on_server(int err)  static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,  				struct page *page)  { +	struct address_space *mapping;  	struct nfs_page *req;  	int ret = 0; @@ -622,19 +628,19 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,  	nfs_set_page_writeback(page);  	WARN_ON_ONCE(test_bit(PG_CLEAN, &req->wb_flags)); -	ret = req->wb_context->error;  	/* If there is a fatal error that covers this write, just exit */ -	if (nfs_error_is_fatal_on_server(ret)) +	ret = 0; +	mapping = page_file_mapping(page); +	if (test_bit(AS_ENOSPC, &mapping->flags) || +	    test_bit(AS_EIO, &mapping->flags))  		goto out_launder; -	ret = 0;  	if (!nfs_pageio_add_request(pgio, req)) {  		ret = pgio->pg_error;  		/*  		 * Remove the problematic req upon fatal errors on the server  		 */  		if (nfs_error_is_fatal(ret)) { -			nfs_context_set_write_error(req->wb_context, ret);  			if (nfs_error_is_fatal_on_server(ret))  				goto out_launder;  		} else @@ -646,8 +652,8 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,  out:  	return ret;  out_launder: -	nfs_write_error_remove_page(req); -	return ret; +	nfs_write_error(req, ret); +	return 0;  }  static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, @@ -958,7 +964,8 @@ static void  nfs_clear_request_commit(struct nfs_page *req)  {  	if (test_bit(PG_CLEAN, &req->wb_flags)) { -		struct inode *inode = d_inode(req->wb_context->dentry); +		struct nfs_open_context *ctx = nfs_req_openctx(req); +		struct inode *inode = d_inode(ctx->dentry);  		struct nfs_commit_info cinfo;  		nfs_init_cinfo_from_inode(&cinfo, inode); @@ -999,10 +1006,12 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)  		if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) &&  		    (hdr->good_bytes < bytes)) {  			nfs_set_pageerror(page_file_mapping(req->wb_page)); -			nfs_context_set_write_error(req->wb_context, hdr->error); +			nfs_mapping_set_error(req->wb_page, hdr->error);  			goto remove_req;  		}  		if (nfs_write_need_commit(hdr)) { +			/* Reset wb_nio, since the write was successful. */ +			req->wb_nio = 0;  			memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));  			nfs_mark_request_commit(req, hdr->lseg, &cinfo,  				hdr->pgio_mirror_idx); @@ -1136,6 +1145,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,  		req->wb_bytes = end - req->wb_offset;  	else  		req->wb_bytes = rqend - req->wb_offset; +	req->wb_nio = 0;  	return req;  out_flushme:  	/* @@ -1165,7 +1175,7 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,  	req = nfs_try_to_update_request(inode, page, offset, bytes);  	if (req != NULL)  		goto out; -	req = nfs_create_request(ctx, page, NULL, offset, bytes); +	req = nfs_create_request(ctx, page, offset, bytes);  	if (IS_ERR(req))  		goto out;  	nfs_inode_add_request(inode, req); @@ -1210,7 +1220,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)  			return 0;  		l_ctx = req->wb_lock_context;  		do_flush = req->wb_page != page || -			!nfs_match_open_context(req->wb_context, ctx); +			!nfs_match_open_context(nfs_req_openctx(req), ctx);  		if (l_ctx && flctx &&  		    !(list_empty_careful(&flctx->flc_posix) &&  		      list_empty_careful(&flctx->flc_flock))) { @@ -1410,8 +1420,10 @@ static void nfs_initiate_write(struct nfs_pgio_header *hdr,   */  static void nfs_redirty_request(struct nfs_page *req)  { +	/* Bump the transmission count */ +	req->wb_nio++;  	nfs_mark_request_dirty(req); -	set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags); +	set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags);  	nfs_end_page_writeback(req);  	nfs_release_request(req);  } @@ -1423,14 +1435,10 @@ static void nfs_async_write_error(struct list_head *head, int error)  	while (!list_empty(head)) {  		req = nfs_list_entry(head->next);  		nfs_list_remove_request(req); -		if (nfs_error_is_fatal(error)) { -			nfs_context_set_write_error(req->wb_context, error); -			if (nfs_error_is_fatal_on_server(error)) { -				nfs_write_error_remove_page(req); -				continue; -			} -		} -		nfs_redirty_request(req); +		if (nfs_error_is_fatal(error)) +			nfs_write_error(req, error); +		else +			nfs_redirty_request(req);  	}  } @@ -1735,7 +1743,8 @@ void nfs_init_commit(struct nfs_commit_data *data,  		     struct nfs_commit_info *cinfo)  {  	struct nfs_page *first = nfs_list_entry(head->next); -	struct inode *inode = d_inode(first->wb_context->dentry); +	struct nfs_open_context *ctx = nfs_req_openctx(first); +	struct inode *inode = d_inode(ctx->dentry);  	/* Set up the RPC argument and reply structs  	 * NB: take care not to mess about with data->commit et al. */ @@ -1743,7 +1752,7 @@ void nfs_init_commit(struct nfs_commit_data *data,  	list_splice_init(head, &data->pages);  	data->inode	  = inode; -	data->cred	  = first->wb_context->cred; +	data->cred	  = ctx->cred;  	data->lseg	  = lseg; /* reference transferred */  	/* only set lwb for pnfs commit */  	if (lseg) @@ -1756,7 +1765,7 @@ void nfs_init_commit(struct nfs_commit_data *data,  	/* Note: we always request a commit of the entire inode */  	data->args.offset = 0;  	data->args.count  = 0; -	data->context     = get_nfs_open_context(first->wb_context); +	data->context     = get_nfs_open_context(ctx);  	data->res.fattr   = &data->fattr;  	data->res.verf    = &data->verf;  	nfs_fattr_init(&data->fattr); @@ -1839,14 +1848,15 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)  			nfs_clear_page_commit(req->wb_page);  		dprintk("NFS:       commit (%s/%llu %d@%lld)", -			req->wb_context->dentry->d_sb->s_id, -			(unsigned long long)NFS_FILEID(d_inode(req->wb_context->dentry)), +			nfs_req_openctx(req)->dentry->d_sb->s_id, +			(unsigned long long)NFS_FILEID(d_inode(nfs_req_openctx(req)->dentry)),  			req->wb_bytes,  			(long long)req_offset(req));  		if (status < 0) { -			nfs_context_set_write_error(req->wb_context, status); -			if (req->wb_page) +			if (req->wb_page) { +				nfs_mapping_set_error(req->wb_page, status);  				nfs_inode_remove_request(req); +			}  			dprintk_cont(", error = %d\n", status);  			goto next;  		} @@ -1863,7 +1873,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)  		/* We have a mismatch. Write the page again */  		dprintk_cont(" mismatch\n");  		nfs_mark_request_dirty(req); -		set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags); +		set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags);  	next:  		nfs_unlock_and_release_request(req);  		/* Latency breaker */ |