]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
nfsd: clients don't need to break their own delegations
authorJ. Bruce Fields <bfields@redhat.com>
Fri, 28 Jul 2017 20:35:15 +0000 (16:35 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Sat, 9 May 2020 01:23:10 +0000 (21:23 -0400)
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>
Documentation/filesystems/locking.rst
fs/locks.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfsd.h
fs/nfsd/nfssvc.c
include/linux/fs.h
include/linux/sunrpc/svc.h

index 5057e4d9dcd1d6c8799dafac8ef0ebab022e695d..9fdcec416614244c9016dca1244ca642d45a0460 100644 (file)
@@ -425,6 +425,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)(struct file_lock *);
 
 locking rules:
 
@@ -435,6 +436,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
 ==========             =============   =================       =========
 
 buffer_head
index b8a31c1c4fff3c2a4e35c57a953d9bf025569a4b..a3f186846e93e1030670cc9da91f9eaaeeba8569 100644 (file)
@@ -1557,6 +1557,9 @@ static bool leases_conflict(struct file_lock *lease, struct file_lock *breaker)
 {
        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)) {
                rc = false;
                goto trace;
index 0e75f7fb5fec0a363e1c1230f6fb731de9b26737..a6d73aa51ce4e98ccead947e857bf25919337c69 100644 (file)
@@ -2302,6 +2302,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
        }
        check_if_stalefh_allowed(args);
 
+       rqstp->rq_lease_breaker = (void **)&cstate->clp;
+
        trace_nfsd_compound(rqstp, args->opcnt);
        while (!status && resp->opcnt < args->opcnt) {
                op = &args->ops[resp->opcnt++];
index fe88f5f23cf46057d92fe7f1cc5803a7421f2ecc..31388046c6fe9b346bdf0d8e90ede19969203d6c 100644 (file)
@@ -4579,6 +4579,19 @@ nfsd_break_deleg_cb(struct file_lock *fl)
        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
 nfsd_change_deleg_cb(struct file_lock *onlist, int arg,
                     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 = {
+       .lm_breaker_owns_lease = nfsd_breaker_owns_lease,
        .lm_break = nfsd_break_deleg_cb,
        .lm_change = nfsd_change_deleg_cb,
 };
index 2ab5569126b8a40bdc81d91a0b6961f62569ca50..36cdd81b6688a0c3563911606a3bd3fa45f70929 100644 (file)
@@ -88,6 +88,8 @@ int           nfsd_pool_stats_release(struct inode *, struct file *);
 
 void           nfsd_destroy(struct net *net);
 
+bool           i_am_nfsd(void);
+
 struct nfsdfs_client {
        struct kref cl_ref;
        void (*cl_release)(struct kref *kref);
index ca9fd348548b8ba2b904a54198139076a60c0982..4f588c0eaaf44ef70e4b5876b861d3d4c2f4e291 100644 (file)
@@ -601,6 +601,11 @@ static const struct svc_serv_ops nfsd_thread_sv_ops = {
        .svo_module             = THIS_MODULE,
 };
 
+bool i_am_nfsd()
+{
+       return kthread_func(current) == nfsd;
+}
+
 int nfsd_create_serv(struct net *net)
 {
        int error;
@@ -1011,6 +1016,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)
index 4f6f59b4f22a807e55e479468df1f1cb7068cfdc..4b784560ffaf305ea47366adc74d44662853e23b 100644 (file)
@@ -1045,6 +1045,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)(struct file_lock *);
 };
 
 struct lock_manager {
index fd390894a5849aa5cff3e4ffb8dea8bb80cd515c..abf4a57ce4a7db947b515399156c8d2b82f1cd3a 100644 (file)
@@ -299,6 +299,7 @@ struct svc_rqst {
        struct net              *rq_bc_net;     /* pointer to backchannel's
                                                 * 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)