@@ -366,6 +366,7 @@ prototypes:
int (*lm_grant)(struct file_lock *, struct file_lock *, int);
void (*lm_break)(struct file_lock *); /* break_lease callback */
int (*lm_change)(struct file_lock **, int);
+ bool (*lm_breaker_owns_lease)(void *, struct file_lock *);
locking rules:
@@ -376,6 +377,7 @@ lm_notify: yes yes no
lm_grant: no no no
lm_break: yes no no
lm_change yes no no
+lm_breaker_owns_lease: no no no
[1]: ->lm_compare_owner and ->lm_owner_key are generally called with
*an* inode->i_lock held. It may not be the i_lock of the inode
@@ -1414,6 +1414,9 @@ static void time_out_leases(struct inode *inode, struct list_head *dispose)
static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
{
+ if (lease->fl_lmops->lm_breaker_owns_lease && breaker->fl_owner &&
+ lease->fl_lmops->lm_breaker_owns_lease(breaker->fl_owner, lease))
+ return false;
if ((breaker->fl_flags & FL_LAYOUT) != (lease->fl_flags & FL_LAYOUT))
return false;
if ((breaker->fl_flags & FL_DELEG) && (lease->fl_flags & FL_LEASE))
@@ -1462,6 +1465,7 @@ int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
if (IS_ERR(new_fl))
return PTR_ERR(new_fl);
new_fl->fl_flags = type;
+ new_fl->fl_owner = current_cred()->lease_breaker;
/* typically we will check that ctx is non-NULL before calling */
ctx = smp_load_acquire(&inode->i_flctx);
@@ -81,6 +81,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
else
new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
new->cap_permitted);
+ if (rqstp->rq_lease_breaker)
+ new->lease_breaker = *rqstp->rq_lease_breaker;
validate_process_creds();
put_cred(override_creds(new));
put_cred(new);
@@ -1713,6 +1713,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
op->status = status;
goto encode_op;
}
+ rqstp->rq_lease_breaker = (void **)&cstate->clp;
while (!status && resp->opcnt < args->opcnt) {
op = &args->ops[resp->opcnt++];
@@ -3927,6 +3927,13 @@ nfsd_break_deleg_cb(struct file_lock *fl)
return ret;
}
+static bool nfsd_breaker_owns_lease(void *breaker, struct file_lock *fl)
+{
+ struct nfs4_delegation *dl = fl->fl_owner;
+
+ return dl->dl_stid.sc_client == breaker;
+}
+
static int
nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
struct list_head *dispose)
@@ -3938,6 +3945,7 @@ nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
}
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_change = nfsd_change_deleg_cb,
};
@@ -803,6 +803,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
*statp = rpc_garbage_args;
return 1;
}
+ rqstp->rq_lease_breaker = NULL;
/*
* Give the xdr decoder a chance to change this if it wants
* (necessary in the NFSv4.0 compound case)
@@ -139,6 +139,9 @@ struct cred {
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
#endif
+#ifdef CONFIG_FILE_LOCKING
+ void *lease_breaker; /* identify NFS client breaking a delegation */
+#endif
#ifdef CONFIG_SECURITY
void *security; /* subjective LSM security */
#endif
@@ -959,6 +959,7 @@ struct lock_manager_operations {
bool (*lm_break)(struct file_lock *);
int (*lm_change)(struct file_lock *, int, struct list_head *);
void (*lm_setup)(struct file_lock *, void **);
+ bool (*lm_breaker_owns_lease)(void *, struct file_lock *);
};
struct lock_manager {
@@ -293,6 +293,7 @@ struct svc_rqst {
struct svc_cacherep * rq_cacherep; /* cache info */
struct task_struct *rq_task; /* service thread */
spinlock_t rq_lock; /* per-request lock */
+ void ** rq_lease_breaker; /* The v4 client breaking a lease */
};
#define SVC_NET(svc_rqst) (svc_rqst->rq_xprt->xpt_net)