]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nfs_localio: always hold nfsd net ref with nfsd_file ref
authorNeilBrown <neil@brown.name>
Fri, 9 May 2025 00:46:39 +0000 (10:46 +1000)
committerAnna Schumaker <anna.schumaker@oracle.com>
Wed, 28 May 2025 21:17:14 +0000 (17:17 -0400)
Having separate nfsd_file_put and nfsd_file_put_local in struct
nfsd_localio_operations doesn't make much sense.  The difference is that
nfsd_file_put doesn't drop a reference to the nfs_net which is what
keeps nfsd from shutting down.

Currently, if nfsd tries to shutdown it will invalidate the files stored
in the list from the nfs_uuid and this will drop all references to the
nfsd net that the client holds.  But the client could still hold some
references to nfsd_files for active IO.  So nfsd might think is has
completely shut down local IO, but hasn't and has no way to wait for
those active IO requests to complete.

So this patch changes nfsd_file_get to nfsd_file_get_local and has it
increase the ref count on the nfsd net and it replaces all calls to
->nfsd_put_file to ->nfsd_put_file_local.

It also changes ->nfsd_open_local_fh to return with the refcount on the
net elevated precisely when a valid nfsd_file is returned.

This means that whenever the client holds a valid nfsd_file, there will
be an associated count on the nfsd net, and so the count can only reach
zero when all nfsd_files have been returned.

nfs_local_file_put() is changed to call nfs_to_nfsd_file_put_local()
instead of replacing calls to one with calls to the other because this
will help a later patch which changes nfs_to_nfsd_file_put_local() to
take an __rcu pointer while nfs_local_file_put() doesn't.

Fixes: 86e00412254a ("nfs: cache all open LOCALIO nfsd_file(s) in client")
Signed-off-by: NeilBrown <neil@brown.name>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
fs/nfs/localio.c
fs/nfs_common/nfslocalio.c
fs/nfsd/filecache.c
fs/nfsd/filecache.h
fs/nfsd/localio.c
include/linux/nfslocalio.h

index 7a33da477da3e84d0f40dfe8654e9d0eab8128eb..86df8d2cd22e83ecd4b9d418c4297c652e16e45c 100644 (file)
@@ -211,12 +211,12 @@ EXPORT_SYMBOL_GPL(nfs_local_probe_async);
 
 static inline struct nfsd_file *nfs_local_file_get(struct nfsd_file *nf)
 {
-       return nfs_to->nfsd_file_get(nf);
+       return nfs_to->nfsd_file_get_local(nf);
 }
 
 static inline void nfs_local_file_put(struct nfsd_file *nf)
 {
-       nfs_to->nfsd_file_put(nf);
+       nfs_to_nfsd_file_put_local(nf);
 }
 
 /*
index bdf251332b6b86e283a3b0edda0b27896461d2ef..f6821b2c87a2f1e170bc3127279ffb83baf69a15 100644 (file)
@@ -262,9 +262,8 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
        /* We have an implied reference to net thanks to nfsd_net_try_get */
        localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
                                             cred, nfs_fh, fmode);
-       if (IS_ERR(localio))
-               nfs_to_nfsd_net_put(net);
-       else
+       nfs_to_nfsd_net_put(net);
+       if (!IS_ERR(localio))
                nfs_uuid_add_file(uuid, nfl);
 
        return localio;
index ab85e6a2454f4c783fcb1175ebbeb63a31519c18..eedf2af8ee6ec1b8e82600d8f2b7b8563d88166d 100644 (file)
@@ -386,6 +386,27 @@ nfsd_file_put_local(struct nfsd_file *nf)
        return net;
 }
 
+/**
+ * nfsd_file_get_local - get nfsd_file reference and reference to net
+ * @nf: nfsd_file of which to put the reference
+ *
+ * Get reference to both the nfsd_file and nf->nf_net.
+ */
+struct nfsd_file *
+nfsd_file_get_local(struct nfsd_file *nf)
+{
+       struct net *net = nf->nf_net;
+
+       if (nfsd_net_try_get(net)) {
+               nf = nfsd_file_get(nf);
+               if (!nf)
+                       nfsd_net_put(net);
+       } else {
+               nf = NULL;
+       }
+       return nf;
+}
+
 /**
  * nfsd_file_file - get the backing file of an nfsd_file
  * @nf: nfsd_file of which to access the backing file.
index 5865f9c7271214de7269ab33480689cd61c1c552..cd02f91aaef13dfd83bf13ec21b6170b45dd7650 100644 (file)
@@ -63,6 +63,7 @@ int nfsd_file_cache_start_net(struct net *net);
 void nfsd_file_cache_shutdown_net(struct net *net);
 void nfsd_file_put(struct nfsd_file *nf);
 struct net *nfsd_file_put_local(struct nfsd_file *nf);
+struct nfsd_file *nfsd_file_get_local(struct nfsd_file *nf);
 struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
 struct file *nfsd_file_file(struct nfsd_file *nf);
 void nfsd_file_close_inode_sync(struct inode *inode);
index 238647fa379e325103f1a7b773f4a91734397bb7..2c0afd1067ea61c200272ab7f74298a472a87f55 100644 (file)
@@ -29,8 +29,7 @@ static const struct nfsd_localio_operations nfsd_localio_ops = {
        .nfsd_net_put  = nfsd_net_put,
        .nfsd_open_local_fh = nfsd_open_local_fh,
        .nfsd_file_put_local = nfsd_file_put_local,
-       .nfsd_file_get = nfsd_file_get,
-       .nfsd_file_put = nfsd_file_put,
+       .nfsd_file_get_local = nfsd_file_get_local,
        .nfsd_file_file = nfsd_file_file,
 };
 
@@ -71,6 +70,9 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
        if (nfs_fh->size > NFS4_FHSIZE)
                return ERR_PTR(-EINVAL);
 
+       if (!nfsd_net_try_get(net))
+               return ERR_PTR(-ENXIO);
+
        /* nfs_fh -> svc_fh */
        fh_init(&fh, NFS4_FHSIZE);
        fh.fh_handle.fh_size = nfs_fh->size;
@@ -92,6 +94,9 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
        if (rq_cred.cr_group_info)
                put_group_info(rq_cred.cr_group_info);
 
+       if (IS_ERR(localio))
+               nfsd_net_put(net);
+
        return localio;
 }
 EXPORT_SYMBOL_GPL(nfsd_open_local_fh);
index 9aa8a43843d7179c9f71857599d7fb27a36727aa..c3f34bae60e13be3f8839f4f46ac1e2ee1e14e6d 100644 (file)
@@ -66,8 +66,7 @@ struct nfsd_localio_operations {
                                                const struct nfs_fh *,
                                                const fmode_t);
        struct net *(*nfsd_file_put_local)(struct nfsd_file *);
-       struct nfsd_file *(*nfsd_file_get)(struct nfsd_file *);
-       void (*nfsd_file_put)(struct nfsd_file *);
+       struct nfsd_file *(*nfsd_file_get_local)(struct nfsd_file *);
        struct file *(*nfsd_file_file)(struct nfsd_file *);
 } ____cacheline_aligned;