mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	nfsd: clients don't need to break their own delegations
We currently revoke read delegations on any write open or any operation that modifies file data or metadata (including rename, link, and unlink). But if the delegation in question is the only read delegation and is held by the client performing the operation, that's not really necessary. It's not always possible to prevent this in the NFSv4.0 case, because there's not always a way to determine which client an NFSv4.0 delegation came from. (In theory we could try to guess this from the transport layer, e.g., by assuming all traffic on a given TCP connection comes from the same client. But that's not really correct.) In the NFSv4.1 case the session layer always tells us the client. This patch should remove such self-conflicts in all cases where we can reliably determine the client from the compound. To do that we need to track "who" is performing a given (possibly lease-breaking) file operation. We're doing that by storing the information in the svc_rqst and using kthread_data() to map the current task back to a svc_rqst. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
		
							parent
							
								
									52782c92ac
								
							
						
					
					
						commit
						28df3d1539
					
				
					 8 changed files with 31 additions and 0 deletions
				
			
		| 
						 | 
					@ -425,6 +425,7 @@ prototypes::
 | 
				
			||||||
	int (*lm_grant)(struct file_lock *, struct file_lock *, int);
 | 
						int (*lm_grant)(struct file_lock *, struct file_lock *, int);
 | 
				
			||||||
	void (*lm_break)(struct file_lock *); /* break_lease callback */
 | 
						void (*lm_break)(struct file_lock *); /* break_lease callback */
 | 
				
			||||||
	int (*lm_change)(struct file_lock **, int);
 | 
						int (*lm_change)(struct file_lock **, int);
 | 
				
			||||||
 | 
						bool (*lm_breaker_owns_lease)(struct file_lock *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
locking rules:
 | 
					locking rules:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -435,6 +436,7 @@ lm_notify:		yes		yes			no
 | 
				
			||||||
lm_grant:		no		no			no
 | 
					lm_grant:		no		no			no
 | 
				
			||||||
lm_break:		yes		no			no
 | 
					lm_break:		yes		no			no
 | 
				
			||||||
lm_change		yes		no			no
 | 
					lm_change		yes		no			no
 | 
				
			||||||
 | 
					lm_breaker_owns_lease:	no		no			no
 | 
				
			||||||
==========		=============	=================	=========
 | 
					==========		=============	=================	=========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
buffer_head
 | 
					buffer_head
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1557,6 +1557,9 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool rc;
 | 
						bool rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (lease->fl_lmops->lm_breaker_owns_lease
 | 
				
			||||||
 | 
								&& lease->fl_lmops->lm_breaker_owns_lease(lease))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
	if ((breaker->fl_flags & FL_LAYOUT) != (lease->fl_flags & FL_LAYOUT)) {
 | 
						if ((breaker->fl_flags & FL_LAYOUT) != (lease->fl_flags & FL_LAYOUT)) {
 | 
				
			||||||
		rc = false;
 | 
							rc = false;
 | 
				
			||||||
		goto trace;
 | 
							goto trace;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2302,6 +2302,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	check_if_stalefh_allowed(args);
 | 
						check_if_stalefh_allowed(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rqstp->rq_lease_breaker = (void **)&cstate->clp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_nfsd_compound(rqstp, args->opcnt);
 | 
						trace_nfsd_compound(rqstp, args->opcnt);
 | 
				
			||||||
	while (!status && resp->opcnt < args->opcnt) {
 | 
						while (!status && resp->opcnt < args->opcnt) {
 | 
				
			||||||
		op = &args->ops[resp->opcnt++];
 | 
							op = &args->ops[resp->opcnt++];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4579,6 +4579,19 @@ nfsd_break_deleg_cb(struct file_lock *fl)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool nfsd_breaker_owns_lease(struct file_lock *fl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nfs4_delegation *dl = fl->fl_owner;
 | 
				
			||||||
 | 
						struct svc_rqst *rqst;
 | 
				
			||||||
 | 
						struct nfs4_client *clp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!i_am_nfsd())
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						rqst = kthread_data(current);
 | 
				
			||||||
 | 
						clp = *(rqst->rq_lease_breaker);
 | 
				
			||||||
 | 
						return dl->dl_stid.sc_client == clp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
 | 
					nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
 | 
				
			||||||
		     struct list_head *dispose)
 | 
							     struct list_head *dispose)
 | 
				
			||||||
| 
						 | 
					@ -4590,6 +4603,7 @@ nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct lock_manager_operations nfsd_lease_mng_ops = {
 | 
					static const struct lock_manager_operations nfsd_lease_mng_ops = {
 | 
				
			||||||
 | 
						.lm_breaker_owns_lease = nfsd_breaker_owns_lease,
 | 
				
			||||||
	.lm_break = nfsd_break_deleg_cb,
 | 
						.lm_break = nfsd_break_deleg_cb,
 | 
				
			||||||
	.lm_change = nfsd_change_deleg_cb,
 | 
						.lm_change = nfsd_change_deleg_cb,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,6 +88,8 @@ int		nfsd_pool_stats_release(struct inode *, struct file *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void		nfsd_destroy(struct net *net);
 | 
					void		nfsd_destroy(struct net *net);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool		i_am_nfsd(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct nfsdfs_client {
 | 
					struct nfsdfs_client {
 | 
				
			||||||
	struct kref cl_ref;
 | 
						struct kref cl_ref;
 | 
				
			||||||
	void (*cl_release)(struct kref *kref);
 | 
						void (*cl_release)(struct kref *kref);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -601,6 +601,11 @@ static const struct svc_serv_ops nfsd_thread_sv_ops = {
 | 
				
			||||||
	.svo_module		= THIS_MODULE,
 | 
						.svo_module		= THIS_MODULE,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool i_am_nfsd()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return kthread_func(current) == nfsd;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int nfsd_create_serv(struct net *net)
 | 
					int nfsd_create_serv(struct net *net)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int error;
 | 
						int error;
 | 
				
			||||||
| 
						 | 
					@ -1011,6 +1016,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 | 
				
			||||||
		*statp = rpc_garbage_args;
 | 
							*statp = rpc_garbage_args;
 | 
				
			||||||
		return 1;
 | 
							return 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						rqstp->rq_lease_breaker = NULL;
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Give the xdr decoder a chance to change this if it wants
 | 
						 * Give the xdr decoder a chance to change this if it wants
 | 
				
			||||||
	 * (necessary in the NFSv4.0 compound case)
 | 
						 * (necessary in the NFSv4.0 compound case)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1045,6 +1045,7 @@ struct lock_manager_operations {
 | 
				
			||||||
	bool (*lm_break)(struct file_lock *);
 | 
						bool (*lm_break)(struct file_lock *);
 | 
				
			||||||
	int (*lm_change)(struct file_lock *, int, struct list_head *);
 | 
						int (*lm_change)(struct file_lock *, int, struct list_head *);
 | 
				
			||||||
	void (*lm_setup)(struct file_lock *, void **);
 | 
						void (*lm_setup)(struct file_lock *, void **);
 | 
				
			||||||
 | 
						bool (*lm_breaker_owns_lease)(struct file_lock *);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct lock_manager {
 | 
					struct lock_manager {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -299,6 +299,7 @@ struct svc_rqst {
 | 
				
			||||||
	struct net		*rq_bc_net;	/* pointer to backchannel's
 | 
						struct net		*rq_bc_net;	/* pointer to backchannel's
 | 
				
			||||||
						 * net namespace
 | 
											 * net namespace
 | 
				
			||||||
						 */
 | 
											 */
 | 
				
			||||||
 | 
						void **			rq_lease_breaker; /* The v4 client breaking a lease */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SVC_NET(rqst) (rqst->rq_xprt ? rqst->rq_xprt->xpt_net : rqst->rq_bc_net)
 | 
					#define SVC_NET(rqst) (rqst->rq_xprt ? rqst->rq_xprt->xpt_net : rqst->rq_bc_net)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue