* [for-6.13 PATCH v2 01/15] nfs_common: must not hold RCU while calling nfsd_file_put_local
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 02/15] nfs/localio: add direct IO enablement with sync and async IO support Mike Snitzer
` (14 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Move holding the RCU from nfs_to_nfsd_file_put_local to
nfs_to_nfsd_net_put. It is the call to nfs_to->nfsd_serv_put that
requires the RCU anyway (the puts for nfsd_file and netns were
combined to avoid an extra indirect reference but that
micro-optimization isn't possible now).
This fixes xfstests generic/013 and it triggering:
"Voluntary context switch within RCU read-side critical section!"
[ 143.545738] Call Trace:
[ 143.546206] <TASK>
[ 143.546625] ? show_regs+0x6d/0x80
[ 143.547267] ? __warn+0x91/0x140
[ 143.547951] ? rcu_note_context_switch+0x496/0x5d0
[ 143.548856] ? report_bug+0x193/0x1a0
[ 143.549557] ? handle_bug+0x63/0xa0
[ 143.550214] ? exc_invalid_op+0x1d/0x80
[ 143.550938] ? asm_exc_invalid_op+0x1f/0x30
[ 143.551736] ? rcu_note_context_switch+0x496/0x5d0
[ 143.552634] ? wakeup_preempt+0x62/0x70
[ 143.553358] __schedule+0xaa/0x1380
[ 143.554025] ? _raw_spin_unlock_irqrestore+0x12/0x40
[ 143.554958] ? try_to_wake_up+0x1fe/0x6b0
[ 143.555715] ? wake_up_process+0x19/0x20
[ 143.556452] schedule+0x2e/0x120
[ 143.557066] schedule_preempt_disabled+0x19/0x30
[ 143.557933] rwsem_down_read_slowpath+0x24d/0x4a0
[ 143.558818] ? xfs_efi_item_format+0x50/0xc0 [xfs]
[ 143.559894] down_read+0x4e/0xb0
[ 143.560519] xlog_cil_commit+0x1b2/0xbc0 [xfs]
[ 143.561460] ? _raw_spin_unlock+0x12/0x30
[ 143.562212] ? xfs_inode_item_precommit+0xc7/0x220 [xfs]
[ 143.563309] ? xfs_trans_run_precommits+0x69/0xd0 [xfs]
[ 143.564394] __xfs_trans_commit+0xb5/0x330 [xfs]
[ 143.565367] xfs_trans_roll+0x48/0xc0 [xfs]
[ 143.566262] xfs_defer_trans_roll+0x57/0x100 [xfs]
[ 143.567278] xfs_defer_finish_noroll+0x27a/0x490 [xfs]
[ 143.568342] xfs_defer_finish+0x1a/0x80 [xfs]
[ 143.569267] xfs_bunmapi_range+0x4d/0xb0 [xfs]
[ 143.570208] xfs_itruncate_extents_flags+0x13d/0x230 [xfs]
[ 143.571353] xfs_free_eofblocks+0x12e/0x190 [xfs]
[ 143.572359] xfs_file_release+0x12d/0x140 [xfs]
[ 143.573324] __fput+0xe8/0x2d0
[ 143.573922] __fput_sync+0x1d/0x30
[ 143.574574] nfsd_filp_close+0x33/0x60 [nfsd]
[ 143.575430] nfsd_file_free+0x96/0x150 [nfsd]
[ 143.576274] nfsd_file_put+0xf7/0x1a0 [nfsd]
[ 143.577104] nfsd_file_put_local+0x18/0x30 [nfsd]
[ 143.578070] nfs_close_local_fh+0x101/0x110 [nfs_localio]
[ 143.579079] __put_nfs_open_context+0xc9/0x180 [nfs]
[ 143.580031] nfs_file_clear_open_context+0x4a/0x60 [nfs]
[ 143.581038] nfs_file_release+0x3e/0x60 [nfs]
[ 143.581879] __fput+0xe8/0x2d0
[ 143.582464] __fput_sync+0x1d/0x30
[ 143.583108] __x64_sys_close+0x41/0x80
[ 143.583823] x64_sys_call+0x189a/0x20d0
[ 143.584552] do_syscall_64+0x64/0x170
[ 143.585240] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 143.586185] RIP: 0033:0x7f3c5153efd7
Fixes: 65f2a5c36635 ("nfs_common: fix race in NFS calls to nfsd_file_put_local() and nfsd_serv_put()")
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: NeilBrown <neilb@suse.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfs_common/nfslocalio.c | 8 +++-----
fs/nfsd/filecache.c | 14 +++++++-------
fs/nfsd/filecache.h | 2 +-
include/linux/nfslocalio.h | 18 +++++++++++++++---
4 files changed, 26 insertions(+), 16 deletions(-)
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 09404d142d1ae..a74ec08f6c96d 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -155,11 +155,9 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
/* We have an implied reference to net thanks to nfsd_serv_try_get */
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
cred, nfs_fh, fmode);
- if (IS_ERR(localio)) {
- rcu_read_lock();
- nfs_to->nfsd_serv_put(net);
- rcu_read_unlock();
- }
+ if (IS_ERR(localio))
+ nfs_to_nfsd_net_put(net);
+
return localio;
}
EXPORT_SYMBOL_GPL(nfs_open_local_fh);
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index c16671135d176..9a62b4da89bb7 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -391,19 +391,19 @@ nfsd_file_put(struct nfsd_file *nf)
}
/**
- * nfsd_file_put_local - put the reference to nfsd_file and local nfsd_serv
- * @nf: nfsd_file of which to put the references
+ * nfsd_file_put_local - put nfsd_file reference and arm nfsd_serv_put in caller
+ * @nf: nfsd_file of which to put the reference
*
- * First put the reference of the nfsd_file and then put the
- * reference to the associated nn->nfsd_serv.
+ * First save the associated net to return to caller, then put
+ * the reference of the nfsd_file.
*/
-void
-nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu)
+struct net *
+nfsd_file_put_local(struct nfsd_file *nf)
{
struct net *net = nf->nf_net;
nfsd_file_put(nf);
- nfsd_serv_put(net);
+ return net;
}
/**
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index cadf3c2689c44..d5db6b34ba302 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -55,7 +55,7 @@ void nfsd_file_cache_shutdown(void);
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);
-void nfsd_file_put_local(struct nfsd_file *nf);
+struct net *nfsd_file_put_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);
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index 3982fea799195..9202f4b24343d 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -55,7 +55,7 @@ struct nfsd_localio_operations {
const struct cred *,
const struct nfs_fh *,
const fmode_t);
- void (*nfsd_file_put_local)(struct nfsd_file *);
+ struct net *(*nfsd_file_put_local)(struct nfsd_file *);
struct file *(*nfsd_file_file)(struct nfsd_file *);
} ____cacheline_aligned;
@@ -66,7 +66,7 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
struct rpc_clnt *, const struct cred *,
const struct nfs_fh *, const fmode_t);
-static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
+static inline void nfs_to_nfsd_net_put(struct net *net)
{
/*
* Once reference to nfsd_serv is dropped, NFSD could be
@@ -74,10 +74,22 @@ static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
* by always taking RCU.
*/
rcu_read_lock();
- nfs_to->nfsd_file_put_local(localio);
+ nfs_to->nfsd_serv_put(net);
rcu_read_unlock();
}
+static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
+{
+ /*
+ * Must not hold RCU otherwise nfsd_file_put() can easily trigger:
+ * "Voluntary context switch within RCU read-side critical section!"
+ * by scheduling deep in underlying filesystem (e.g. XFS).
+ */
+ struct net *net = nfs_to->nfsd_file_put_local(localio);
+
+ nfs_to_nfsd_net_put(net);
+}
+
#else /* CONFIG_NFS_LOCALIO */
static inline void nfsd_localio_ops_init(void)
{
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 02/15] nfs/localio: add direct IO enablement with sync and async IO support
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 01/15] nfs_common: must not hold RCU while calling nfsd_file_put_local Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 03/15] nfsd: add nfsd_file_{get,put} to 'nfs_to' nfsd_localio_operations Mike Snitzer
` (13 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
This commit simply adds the required O_DIRECT plumbing. It doesn't
address the fact that NFS doesn't ensure all writes are page aligned
(nor device logical block size aligned as required by O_DIRECT).
Because NFS will read-modify-write for IO that isn't aligned, LOCALIO
will not use O_DIRECT semantics by default if/when an application
requests the use of O_DIRECT. Allow the use of O_DIRECT semantics by:
1: Adding a flag to the nfs_pgio_header struct to allow the NFS
O_DIRECT layer to signal that O_DIRECT was used by the application
2: Adding a 'localio_O_DIRECT_semantics' NFS module parameter that
when enabled will cause LOCALIO to use O_DIRECT semantics (this may
cause IO to fail if applications do not properly align their IO).
This commit is derived from code developed by Weston Andros Adamson.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
Documentation/filesystems/nfs/localio.rst | 11 +++
fs/nfs/direct.c | 1 +
fs/nfs/localio.c | 93 ++++++++++++++++++++---
include/linux/nfs_xdr.h | 1 +
4 files changed, 96 insertions(+), 10 deletions(-)
diff --git a/Documentation/filesystems/nfs/localio.rst b/Documentation/filesystems/nfs/localio.rst
index bd1967e2eab32..eb3dc764cfce8 100644
--- a/Documentation/filesystems/nfs/localio.rst
+++ b/Documentation/filesystems/nfs/localio.rst
@@ -306,6 +306,17 @@ is issuing IO to the underlying local filesystem that it is sharing with
the NFS server. See: fs/nfs/localio.c:nfs_local_doio() and
fs/nfs/localio.c:nfs_local_commit().
+With normal NFS that makes use of RPC to issue IO to the server, if an
+application uses O_DIRECT the NFS client will bypass the pagecache but
+the NFS server will not. Because the NFS server's use of buffered IO
+affords applications to be less precise with their alignment when
+issuing IO to the NFS client. LOCALIO can be configured to use O_DIRECT
+semantics by setting the 'localio_O_DIRECT_semantics' nfs module
+parameter to Y, e.g.:
+ echo Y > /sys/module/nfs/parameters/localio_O_DIRECT_semantics
+Once enabled, it will cause LOCALIO to use O_DIRECT semantics (this may
+cause IO to fail if applications do not properly align their IO).
+
Security
========
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 90079ca134dd3..4b92493d6ff0e 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -303,6 +303,7 @@ static void nfs_read_sync_pgio_error(struct list_head *head, int error)
static void nfs_direct_pgio_init(struct nfs_pgio_header *hdr)
{
get_dreq(hdr->dreq);
+ set_bit(NFS_IOHDR_ODIRECT, &hdr->flags);
}
static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = {
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 4b8618cf114ca..7637786c5cb70 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -35,6 +35,7 @@ struct nfs_local_kiocb {
struct bio_vec *bvec;
struct nfs_pgio_header *hdr;
struct work_struct work;
+ void (*aio_complete_work)(struct work_struct *);
struct nfsd_file *localio;
};
@@ -48,6 +49,11 @@ struct nfs_local_fsync_ctx {
static bool localio_enabled __read_mostly = true;
module_param(localio_enabled, bool, 0644);
+static bool localio_O_DIRECT_semantics __read_mostly = false;
+module_param(localio_O_DIRECT_semantics, bool, 0644);
+MODULE_PARM_DESC(localio_O_DIRECT_semantics,
+ "LOCALIO will use O_DIRECT semantics to filesystem.");
+
static inline bool nfs_client_is_local(const struct nfs_client *clp)
{
return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
@@ -285,10 +291,19 @@ nfs_local_iocb_alloc(struct nfs_pgio_header *hdr,
kfree(iocb);
return NULL;
}
- init_sync_kiocb(&iocb->kiocb, file);
+
+ if (localio_O_DIRECT_semantics &&
+ test_bit(NFS_IOHDR_ODIRECT, &hdr->flags)) {
+ iocb->kiocb.ki_filp = file;
+ iocb->kiocb.ki_flags = IOCB_DIRECT;
+ } else
+ init_sync_kiocb(&iocb->kiocb, file);
+
iocb->kiocb.ki_pos = hdr->args.offset;
iocb->hdr = hdr;
iocb->kiocb.ki_flags &= ~IOCB_APPEND;
+ iocb->aio_complete_work = NULL;
+
return iocb;
}
@@ -343,6 +358,18 @@ nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
nfs_local_hdr_release(hdr, hdr->task.tk_ops);
}
+/*
+ * Complete the I/O from iocb->kiocb.ki_complete()
+ *
+ * Note that this function can be called from a bottom half context,
+ * hence we need to queue the rpc_call_done() etc to a workqueue
+ */
+static inline void nfs_local_pgio_aio_complete(struct nfs_local_kiocb *iocb)
+{
+ INIT_WORK(&iocb->work, iocb->aio_complete_work);
+ queue_work(nfsiod_workqueue, &iocb->work);
+}
+
static void
nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
{
@@ -365,6 +392,23 @@ nfs_local_read_done(struct nfs_local_kiocb *iocb, long status)
status > 0 ? status : 0, hdr->res.eof);
}
+static void nfs_local_read_aio_complete_work(struct work_struct *work)
+{
+ struct nfs_local_kiocb *iocb =
+ container_of(work, struct nfs_local_kiocb, work);
+
+ nfs_local_pgio_release(iocb);
+}
+
+static void nfs_local_read_aio_complete(struct kiocb *kiocb, long ret)
+{
+ struct nfs_local_kiocb *iocb =
+ container_of(kiocb, struct nfs_local_kiocb, kiocb);
+
+ nfs_local_read_done(iocb, ret);
+ nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_read_aio_complete_work */
+}
+
static void nfs_local_call_read(struct work_struct *work)
{
struct nfs_local_kiocb *iocb =
@@ -379,10 +423,10 @@ static void nfs_local_call_read(struct work_struct *work)
nfs_local_iter_init(&iter, iocb, READ);
status = filp->f_op->read_iter(&iocb->kiocb, &iter);
- WARN_ON_ONCE(status == -EIOCBQUEUED);
-
- nfs_local_read_done(iocb, status);
- nfs_local_pgio_release(iocb);
+ if (status != -EIOCBQUEUED) {
+ nfs_local_read_done(iocb, status);
+ nfs_local_pgio_release(iocb);
+ }
revert_creds(save_cred);
}
@@ -410,6 +454,11 @@ nfs_do_local_read(struct nfs_pgio_header *hdr,
nfs_local_pgio_init(hdr, call_ops);
hdr->res.eof = false;
+ if (iocb->kiocb.ki_flags & IOCB_DIRECT) {
+ iocb->kiocb.ki_complete = nfs_local_read_aio_complete;
+ iocb->aio_complete_work = nfs_local_read_aio_complete_work;
+ }
+
INIT_WORK(&iocb->work, nfs_local_call_read);
queue_work(nfslocaliod_workqueue, &iocb->work);
@@ -534,6 +583,24 @@ nfs_local_write_done(struct nfs_local_kiocb *iocb, long status)
nfs_local_pgio_done(hdr, status);
}
+static void nfs_local_write_aio_complete_work(struct work_struct *work)
+{
+ struct nfs_local_kiocb *iocb =
+ container_of(work, struct nfs_local_kiocb, work);
+
+ nfs_local_vfs_getattr(iocb);
+ nfs_local_pgio_release(iocb);
+}
+
+static void nfs_local_write_aio_complete(struct kiocb *kiocb, long ret)
+{
+ struct nfs_local_kiocb *iocb =
+ container_of(kiocb, struct nfs_local_kiocb, kiocb);
+
+ nfs_local_write_done(iocb, ret);
+ nfs_local_pgio_aio_complete(iocb); /* Calls nfs_local_write_aio_complete_work */
+}
+
static void nfs_local_call_write(struct work_struct *work)
{
struct nfs_local_kiocb *iocb =
@@ -552,11 +619,11 @@ static void nfs_local_call_write(struct work_struct *work)
file_start_write(filp);
status = filp->f_op->write_iter(&iocb->kiocb, &iter);
file_end_write(filp);
- WARN_ON_ONCE(status == -EIOCBQUEUED);
-
- nfs_local_write_done(iocb, status);
- nfs_local_vfs_getattr(iocb);
- nfs_local_pgio_release(iocb);
+ if (status != -EIOCBQUEUED) {
+ nfs_local_write_done(iocb, status);
+ nfs_local_vfs_getattr(iocb);
+ nfs_local_pgio_release(iocb);
+ }
revert_creds(save_cred);
current->flags = old_flags;
@@ -592,10 +659,16 @@ nfs_do_local_write(struct nfs_pgio_header *hdr,
case NFS_FILE_SYNC:
iocb->kiocb.ki_flags |= IOCB_DSYNC|IOCB_SYNC;
}
+
nfs_local_pgio_init(hdr, call_ops);
nfs_set_local_verifier(hdr->inode, hdr->res.verf, hdr->args.stable);
+ if (iocb->kiocb.ki_flags & IOCB_DIRECT) {
+ iocb->kiocb.ki_complete = nfs_local_write_aio_complete;
+ iocb->aio_complete_work = nfs_local_write_aio_complete_work;
+ }
+
INIT_WORK(&iocb->work, nfs_local_call_write);
queue_work(nfslocaliod_workqueue, &iocb->work);
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index e74a87bb18a44..162b7c0c35557 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1632,6 +1632,7 @@ enum {
NFS_IOHDR_RESEND_PNFS,
NFS_IOHDR_RESEND_MDS,
NFS_IOHDR_UNSTABLE_WRITES,
+ NFS_IOHDR_ODIRECT,
};
struct nfs_io_completion;
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 03/15] nfsd: add nfsd_file_{get,put} to 'nfs_to' nfsd_localio_operations
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 01/15] nfs_common: must not hold RCU while calling nfsd_file_put_local Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 02/15] nfs/localio: add direct IO enablement with sync and async IO support Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 04/15] nfs_common: rename functions that invalidate LOCALIO nfs_clients Mike Snitzer
` (12 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
In later a commit LOCALIO must call both nfsd_file_get and
nfsd_file_put to manage extra nfsd_file references.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfsd/localio.c | 2 ++
include/linux/nfslocalio.h | 2 ++
2 files changed, 4 insertions(+)
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
index f441cb9f74d56..8beda4c851119 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -29,6 +29,8 @@ static const struct nfsd_localio_operations nfsd_localio_ops = {
.nfsd_serv_put = nfsd_serv_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_file = nfsd_file_file,
};
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index 9202f4b24343d..ab6a2a53f505f 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -56,6 +56,8 @@ 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 file *(*nfsd_file_file)(struct nfsd_file *);
} ____cacheline_aligned;
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 04/15] nfs_common: rename functions that invalidate LOCALIO nfs_clients
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (2 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 03/15] nfsd: add nfsd_file_{get,put} to 'nfs_to' nfsd_localio_operations Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 05/15] nfs_common: move localio_lock to new lock member of nfs_uuid_t Mike Snitzer
` (11 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Rename nfs_uuid_invalidate_one_client to nfs_localio_disable_client.
Rename nfs_uuid_invalidate_clients to nfs_localio_invalidate_clients.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Reviewed-by: NeilBrown <neilb@suse.de>
---
fs/nfs/localio.c | 2 +-
fs/nfs_common/nfslocalio.c | 8 ++++----
fs/nfsd/nfsctl.c | 4 ++--
include/linux/nfslocalio.h | 5 +++--
4 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 7637786c5cb70..94af5d89bb996 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -140,7 +140,7 @@ void nfs_local_disable(struct nfs_client *clp)
spin_lock(&clp->cl_localio_lock);
if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
trace_nfs_local_disable(clp);
- nfs_uuid_invalidate_one_client(&clp->cl_uuid);
+ nfs_localio_disable_client(&clp->cl_uuid);
}
spin_unlock(&clp->cl_localio_lock);
}
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index a74ec08f6c96d..904439e4bb859 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -107,7 +107,7 @@ static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
list_del_init(&nfs_uuid->list);
}
-void nfs_uuid_invalidate_clients(struct list_head *list)
+void nfs_localio_invalidate_clients(struct list_head *list)
{
nfs_uuid_t *nfs_uuid, *tmp;
@@ -116,9 +116,9 @@ void nfs_uuid_invalidate_clients(struct list_head *list)
nfs_uuid_put_locked(nfs_uuid);
spin_unlock(&nfs_uuid_lock);
}
-EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_clients);
+EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
-void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid)
+void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid)
{
if (nfs_uuid->net) {
spin_lock(&nfs_uuid_lock);
@@ -126,7 +126,7 @@ void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid)
spin_unlock(&nfs_uuid_lock);
}
}
-EXPORT_SYMBOL_GPL(nfs_uuid_invalidate_one_client);
+EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
struct rpc_clnt *rpc_clnt, const struct cred *cred,
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 3adbc05ebaac4..727904d8a4d01 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2276,14 +2276,14 @@ static __net_init int nfsd_net_init(struct net *net)
* nfsd_net_pre_exit - Disconnect localio clients from net namespace
* @net: a network namespace that is about to be destroyed
*
- * This invalidated ->net pointers held by localio clients
+ * This invalidates ->net pointers held by localio clients
* while they can still safely access nn->counter.
*/
static __net_exit void nfsd_net_pre_exit(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- nfs_uuid_invalidate_clients(&nn->local_clients);
+ nfs_localio_invalidate_clients(&nn->local_clients);
}
#endif
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index ab6a2a53f505f..a05d1043f2b07 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -37,8 +37,9 @@ bool nfs_uuid_begin(nfs_uuid_t *);
void nfs_uuid_end(nfs_uuid_t *);
void nfs_uuid_is_local(const uuid_t *, struct list_head *,
struct net *, struct auth_domain *, struct module *);
-void nfs_uuid_invalidate_clients(struct list_head *list);
-void nfs_uuid_invalidate_one_client(nfs_uuid_t *nfs_uuid);
+
+void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid);
+void nfs_localio_invalidate_clients(struct list_head *list);
/* localio needs to map filehandle -> struct nfsd_file */
extern struct nfsd_file *
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 05/15] nfs_common: move localio_lock to new lock member of nfs_uuid_t
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (3 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 04/15] nfs_common: rename functions that invalidate LOCALIO nfs_clients Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client Mike Snitzer
` (10 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Remove cl_localio_lock from 'struct nfs_client' in favor of adding a
lock to the nfs_uuid_t struct (which is embedded in each nfs_client).
Push nfs_local_{enable,disable} implementation down to nfs_common.
Those methods now call nfs_localio_{enable,disable}_client.
This allows implementing nfs_localio_invalidate_clients in terms of
nfs_localio_disable_client.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs/client.c | 1 -
fs/nfs/localio.c | 14 +++-------
fs/nfs_common/nfslocalio.c | 57 ++++++++++++++++++++++++++------------
include/linux/nfs_fs_sb.h | 1 -
include/linux/nfslocalio.h | 8 +++++-
5 files changed, 51 insertions(+), 30 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 550ca934c9cfc..e83e1ce046130 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -186,7 +186,6 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
seqlock_init(&clp->cl_boot_lock);
ktime_get_real_ts64(&clp->cl_nfssvc_boot);
nfs_uuid_init(&clp->cl_uuid);
- spin_lock_init(&clp->cl_localio_lock);
#endif /* CONFIG_NFS_LOCALIO */
clp->cl_principal = "*";
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 94af5d89bb996..7191135b47a42 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -126,10 +126,8 @@ const struct rpc_program nfslocalio_program = {
*/
static void nfs_local_enable(struct nfs_client *clp)
{
- spin_lock(&clp->cl_localio_lock);
- set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
trace_nfs_local_enable(clp);
- spin_unlock(&clp->cl_localio_lock);
+ nfs_localio_enable_client(clp);
}
/*
@@ -137,12 +135,8 @@ static void nfs_local_enable(struct nfs_client *clp)
*/
void nfs_local_disable(struct nfs_client *clp)
{
- spin_lock(&clp->cl_localio_lock);
- if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
- trace_nfs_local_disable(clp);
- nfs_localio_disable_client(&clp->cl_uuid);
- }
- spin_unlock(&clp->cl_localio_lock);
+ trace_nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
}
/*
@@ -184,7 +178,7 @@ static bool nfs_server_uuid_is_local(struct nfs_client *clp)
rpc_shutdown_client(rpcclient_localio);
/* Server is only local if it initialized required struct members */
- if (status || !clp->cl_uuid.net || !clp->cl_uuid.dom)
+ if (status || !rcu_access_pointer(clp->cl_uuid.net) || !clp->cl_uuid.dom)
return false;
return true;
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 904439e4bb859..abc132166742e 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -7,6 +7,9 @@
#include <linux/module.h>
#include <linux/list.h>
#include <linux/nfslocalio.h>
+#include <linux/nfs3.h>
+#include <linux/nfs4.h>
+#include <linux/nfs_fs_sb.h>
#include <net/netns/generic.h>
MODULE_LICENSE("GPL");
@@ -25,6 +28,7 @@ void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
nfs_uuid->net = NULL;
nfs_uuid->dom = NULL;
INIT_LIST_HEAD(&nfs_uuid->list);
+ spin_lock_init(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_uuid_init);
@@ -94,12 +98,23 @@ void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
}
EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
+void nfs_localio_enable_client(struct nfs_client *clp)
+{
+ nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
+
+ spin_lock(&nfs_uuid->lock);
+ set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ spin_unlock(&nfs_uuid->lock);
+}
+EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
+
static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
{
- if (nfs_uuid->net) {
- module_put(nfsd_mod);
- nfs_uuid->net = NULL;
- }
+ if (!nfs_uuid->net)
+ return;
+ module_put(nfsd_mod);
+ RCU_INIT_POINTER(nfs_uuid->net, NULL);
+
if (nfs_uuid->dom) {
auth_domain_put(nfs_uuid->dom);
nfs_uuid->dom = NULL;
@@ -107,27 +122,35 @@ static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
list_del_init(&nfs_uuid->list);
}
-void nfs_localio_invalidate_clients(struct list_head *list)
+void nfs_localio_disable_client(struct nfs_client *clp)
+{
+ nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
+
+ spin_lock(&nfs_uuid->lock);
+ if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
+ spin_lock(&nfs_uuid_lock);
+ nfs_uuid_put_locked(nfs_uuid);
+ spin_unlock(&nfs_uuid_lock);
+ }
+ spin_unlock(&nfs_uuid->lock);
+}
+EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
+
+void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
{
nfs_uuid_t *nfs_uuid, *tmp;
spin_lock(&nfs_uuid_lock);
- list_for_each_entry_safe(nfs_uuid, tmp, list, list)
- nfs_uuid_put_locked(nfs_uuid);
+ list_for_each_entry_safe(nfs_uuid, tmp, cl_uuid_list, list) {
+ struct nfs_client *clp =
+ container_of(nfs_uuid, struct nfs_client, cl_uuid);
+
+ nfs_localio_disable_client(clp);
+ }
spin_unlock(&nfs_uuid_lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
-void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid)
-{
- if (nfs_uuid->net) {
- spin_lock(&nfs_uuid_lock);
- nfs_uuid_put_locked(nfs_uuid);
- spin_unlock(&nfs_uuid_lock);
- }
-}
-EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
-
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
struct rpc_clnt *rpc_clnt, const struct cred *cred,
const struct nfs_fh *nfs_fh, const fmode_t fmode)
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index b804346a97419..239d86ef166c0 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -132,7 +132,6 @@ struct nfs_client {
struct timespec64 cl_nfssvc_boot;
seqlock_t cl_boot_lock;
nfs_uuid_t cl_uuid;
- spinlock_t cl_localio_lock;
#endif /* CONFIG_NFS_LOCALIO */
};
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index a05d1043f2b07..4d5583873f418 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -6,6 +6,7 @@
#ifndef __LINUX_NFSLOCALIO_H
#define __LINUX_NFSLOCALIO_H
+
/* nfsd_file structure is purposely kept opaque to NFS client */
struct nfsd_file;
@@ -19,6 +20,8 @@ struct nfsd_file;
#include <linux/nfs.h>
#include <net/net_namespace.h>
+struct nfs_client;
+
/*
* Useful to allow a client to negotiate if localio
* possible with its server.
@@ -27,6 +30,8 @@ struct nfsd_file;
*/
typedef struct {
uuid_t uuid;
+ /* sadly this struct is just over a cacheline, avoid bouncing */
+ spinlock_t ____cacheline_aligned lock;
struct list_head list;
struct net __rcu *net; /* nfsd's network namespace */
struct auth_domain *dom; /* auth_domain for localio */
@@ -38,7 +43,8 @@ void nfs_uuid_end(nfs_uuid_t *);
void nfs_uuid_is_local(const uuid_t *, struct list_head *,
struct net *, struct auth_domain *, struct module *);
-void nfs_localio_disable_client(nfs_uuid_t *nfs_uuid);
+void nfs_localio_enable_client(struct nfs_client *clp);
+void nfs_localio_disable_client(struct nfs_client *clp);
void nfs_localio_invalidate_clients(struct list_head *list);
/* localio needs to map filehandle -> struct nfsd_file */
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (4 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 05/15] nfs_common: move localio_lock to new lock member of nfs_uuid_t Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-15 3:12 ` NeilBrown
2024-11-14 3:59 ` [for-6.13 PATCH v2 07/15] nfsd: update percpu_ref to manage references on nfsd_net Mike Snitzer
` (9 subsequent siblings)
15 siblings, 1 reply; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
This commit switches from leaning heavily on NFSD's filecache (in
terms of GC'd nfsd_files) back to caching nfsd_files in the
client. A later commit will add the callback mechanism needed to
allow NFSD to force the NFS client to cleanup all caches files.
Add nfs_fh_localio_init() and 'struct nfs_fh_localio' to cache opened
nfsd_file(s) (both a RO and RW nfsd_file is able to be opened and
cached for a given nfs_fh).
Update nfs_local_open_fh() to cache the nfsd_file once it is opened
using __nfs_local_open_fh().
Introduce nfs_close_local_fh() to clear the cached open nfsd_files and
call nfs_to_nfsd_file_put_local().
Refcounting is such that:
- nfs_local_open_fh() is paired with nfs_close_local_fh().
- __nfs_local_open_fh() is paired with nfs_to_nfsd_file_put_local().
- nfs_local_file_get() is paired with nfs_local_file_put().
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs/flexfilelayout/flexfilelayout.c | 29 +++++----
fs/nfs/flexfilelayout/flexfilelayout.h | 1 +
fs/nfs/inode.c | 3 +
fs/nfs/internal.h | 4 +-
fs/nfs/localio.c | 89 +++++++++++++++++++++-----
fs/nfs/pagelist.c | 5 +-
fs/nfs/write.c | 3 +-
fs/nfs_common/nfslocalio.c | 52 ++++++++++++++-
include/linux/nfs_fs.h | 22 ++++++-
include/linux/nfslocalio.h | 18 +++---
10 files changed, 181 insertions(+), 45 deletions(-)
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
index f78115c6c2c12..451f168d882be 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.c
+++ b/fs/nfs/flexfilelayout/flexfilelayout.c
@@ -164,18 +164,17 @@ decode_name(struct xdr_stream *xdr, u32 *id)
}
static struct nfsd_file *
-ff_local_open_fh(struct nfs_client *clp, const struct cred *cred,
+ff_local_open_fh(struct pnfs_layout_segment *lseg, u32 ds_idx,
+ struct nfs_client *clp, const struct cred *cred,
struct nfs_fh *fh, fmode_t mode)
{
- if (mode & FMODE_WRITE) {
- /*
- * Always request read and write access since this corresponds
- * to a rw layout.
- */
- mode |= FMODE_READ;
- }
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
- return nfs_local_open_fh(clp, cred, fh, mode);
+ return nfs_local_open_fh(clp, cred, fh, &mirror->nfl, mode);
+#else
+ return NULL;
+#endif
}
static bool ff_mirror_match_fh(const struct nfs4_ff_layout_mirror *m1,
@@ -247,6 +246,9 @@ static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags)
spin_lock_init(&mirror->lock);
refcount_set(&mirror->ref, 1);
INIT_LIST_HEAD(&mirror->mirrors);
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ nfs_localio_file_init(&mirror->nfl);
+#endif
}
return mirror;
}
@@ -257,6 +259,9 @@ static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror)
ff_layout_remove_mirror(mirror);
kfree(mirror->fh_versions);
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ nfs_close_local_fh(&mirror->nfl);
+#endif
cred = rcu_access_pointer(mirror->ro_cred);
put_cred(cred);
cred = rcu_access_pointer(mirror->rw_cred);
@@ -1820,7 +1825,7 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
hdr->mds_offset = offset;
/* Start IO accounting for local read */
- localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh, FMODE_READ);
+ localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh, FMODE_READ);
if (localio) {
hdr->task.tk_start = ktime_get();
ff_layout_read_record_layoutstats_start(&hdr->task, hdr);
@@ -1896,7 +1901,7 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
hdr->args.offset = offset;
/* Start IO accounting for local write */
- localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
+ localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh,
FMODE_READ|FMODE_WRITE);
if (localio) {
hdr->task.tk_start = ktime_get();
@@ -1981,7 +1986,7 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
data->args.fh = fh;
/* Start IO accounting for local commit */
- localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
+ localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh,
FMODE_READ|FMODE_WRITE);
if (localio) {
data->task.tk_start = ktime_get();
diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h
index f84b3fb0dddd8..095df09017a57 100644
--- a/fs/nfs/flexfilelayout/flexfilelayout.h
+++ b/fs/nfs/flexfilelayout/flexfilelayout.h
@@ -83,6 +83,7 @@ struct nfs4_ff_layout_mirror {
nfs4_stateid stateid;
const struct cred __rcu *ro_cred;
const struct cred __rcu *rw_cred;
+ struct nfs_file_localio nfl;
refcount_t ref;
spinlock_t lock;
unsigned long flags;
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 596f351701372..1aa67fca69b2f 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1137,6 +1137,8 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
ctx->lock_context.open_context = ctx;
INIT_LIST_HEAD(&ctx->list);
ctx->mdsthreshold = NULL;
+ nfs_localio_file_init(&ctx->nfl);
+
return ctx;
}
EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
@@ -1168,6 +1170,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
nfs_sb_deactive(sb);
put_rpccred(rcu_dereference_protected(ctx->ll_cred, 1));
kfree(ctx->mdsthreshold);
+ nfs_close_local_fh(&ctx->nfl);
kfree_rcu(ctx, rcu_head);
}
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 430733e3eff26..57af3ab3adbe5 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -459,6 +459,7 @@ extern void nfs_local_probe(struct nfs_client *);
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
const struct cred *,
struct nfs_fh *,
+ struct nfs_file_localio *,
const fmode_t);
extern int nfs_local_doio(struct nfs_client *,
struct nfsd_file *,
@@ -474,7 +475,8 @@ static inline void nfs_local_disable(struct nfs_client *clp) {}
static inline void nfs_local_probe(struct nfs_client *clp) {}
static inline struct nfsd_file *
nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
- struct nfs_fh *fh, const fmode_t mode)
+ struct nfs_fh *fh, struct nfs_file_localio *nfl,
+ const fmode_t mode)
{
return NULL;
}
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 7191135b47a42..7e432057c3a1f 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -211,27 +211,33 @@ void nfs_local_probe(struct nfs_client *clp)
}
EXPORT_SYMBOL_GPL(nfs_local_probe);
+static inline struct nfsd_file *nfs_local_file_get(struct nfsd_file *nf)
+{
+ return nfs_to->nfsd_file_get(nf);
+}
+
+static inline void nfs_local_file_put(struct nfsd_file *nf)
+{
+ nfs_to->nfsd_file_put(nf);
+}
+
/*
- * nfs_local_open_fh - open a local filehandle in terms of nfsd_file
+ * __nfs_local_open_fh - open a local filehandle in terms of nfsd_file.
*
- * Returns a pointer to a struct nfsd_file or NULL
+ * Returns a pointer to a struct nfsd_file or ERR_PTR.
+ * Caller must release returned nfsd_file with nfs_to_nfsd_file_put_local().
*/
-struct nfsd_file *
-nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
- struct nfs_fh *fh, const fmode_t mode)
+static struct nfsd_file *
+__nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
+ struct nfs_fh *fh, struct nfs_file_localio *nfl,
+ const fmode_t mode)
{
struct nfsd_file *localio;
- int status;
-
- if (!nfs_server_is_local(clp))
- return NULL;
- if (mode & ~(FMODE_READ | FMODE_WRITE))
- return NULL;
localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient,
- cred, fh, mode);
+ cred, fh, nfl, mode);
if (IS_ERR(localio)) {
- status = PTR_ERR(localio);
+ int status = PTR_ERR(localio);
trace_nfs_local_open_fh(fh, mode, status);
switch (status) {
case -ENOMEM:
@@ -240,10 +246,59 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
/* Revalidate localio, will disable if unsupported */
nfs_local_probe(clp);
}
- return NULL;
}
return localio;
}
+
+/*
+ * nfs_local_open_fh - open a local filehandle in terms of nfsd_file.
+ * First checking if the open nfsd_file is already cached, otherwise
+ * must __nfs_local_open_fh and insert the nfsd_file in nfs_file_localio.
+ *
+ * Returns a pointer to a struct nfsd_file or NULL.
+ */
+struct nfsd_file *
+nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
+ struct nfs_fh *fh, struct nfs_file_localio *nfl,
+ const fmode_t mode)
+{
+ struct nfsd_file *nf, *new, __rcu **pnf;
+
+ if (!nfs_server_is_local(clp))
+ return NULL;
+ if (mode & ~(FMODE_READ | FMODE_WRITE))
+ return NULL;
+
+ if (mode & FMODE_WRITE)
+ pnf = &nfl->rw_file;
+ else
+ pnf = &nfl->ro_file;
+
+ new = NULL;
+ rcu_read_lock();
+ nf = rcu_dereference(*pnf);
+ if (!nf) {
+ rcu_read_unlock();
+ new = __nfs_local_open_fh(clp, cred, fh, nfl, mode);
+ if (IS_ERR(new))
+ return NULL;
+ /* try to swap in the pointer */
+ spin_lock(&clp->cl_uuid.lock);
+ nf = rcu_dereference_protected(*pnf, 1);
+ if (!nf) {
+ nf = new;
+ new = NULL;
+ rcu_assign_pointer(*pnf, nf);
+ }
+ spin_unlock(&clp->cl_uuid.lock);
+ rcu_read_lock();
+ }
+ nf = nfs_local_file_get(nf);
+ rcu_read_unlock();
+ if (new)
+ nfs_to_nfsd_file_put_local(new);
+ return nf;
+}
EXPORT_SYMBOL_GPL(nfs_local_open_fh);
static struct bio_vec *
@@ -347,7 +402,7 @@ nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
{
struct nfs_pgio_header *hdr = iocb->hdr;
- nfs_to_nfsd_file_put_local(iocb->localio);
+ nfs_local_file_put(iocb->localio);
nfs_local_iocb_free(iocb);
nfs_local_hdr_release(hdr, hdr->task.tk_ops);
}
@@ -694,7 +749,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
if (status != 0) {
if (status == -EAGAIN)
nfs_local_disable(clp);
- nfs_to_nfsd_file_put_local(localio);
+ nfs_local_file_put(localio);
hdr->task.tk_status = status;
nfs_local_hdr_release(hdr, call_ops);
}
@@ -745,7 +800,7 @@ nfs_local_release_commit_data(struct nfsd_file *localio,
struct nfs_commit_data *data,
const struct rpc_call_ops *call_ops)
{
- nfs_to_nfsd_file_put_local(localio);
+ nfs_local_file_put(localio);
call_ops->rpc_call_done(&data->task, data);
call_ops->rpc_release(data);
}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index e27c07bd89290..11968dcb72431 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -961,8 +961,9 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
struct nfs_client *clp = NFS_SERVER(hdr->inode)->nfs_client;
struct nfsd_file *localio =
- nfs_local_open_fh(clp, hdr->cred,
- hdr->args.fh, hdr->args.context->mode);
+ nfs_local_open_fh(clp, hdr->cred, hdr->args.fh,
+ &hdr->args.context->nfl,
+ hdr->args.context->mode);
if (NFS_SERVER(hdr->inode)->nfs_client->cl_minorversion)
task_flags = RPC_TASK_MOVEABLE;
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 2da00987d9ed4..75779e3cac16d 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1817,7 +1817,8 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
task_flags = RPC_TASK_MOVEABLE;
localio = nfs_local_open_fh(NFS_SERVER(inode)->nfs_client, data->cred,
- data->args.fh, data->context->mode);
+ data->args.fh, &data->context->nfl,
+ data->context->mode);
return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
data->mds_ops, how,
RPC_TASK_CRED_NOREF | task_flags, localio);
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index abc132166742e..35a2e48731df6 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -9,7 +9,7 @@
#include <linux/nfslocalio.h>
#include <linux/nfs3.h>
#include <linux/nfs4.h>
-#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_fs.h>
#include <net/netns/generic.h>
MODULE_LICENSE("GPL");
@@ -151,9 +151,18 @@ void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
}
EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
+static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
+{
+ spin_lock(&nfs_uuid_lock);
+ if (!nfl->nfs_uuid)
+ rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
+ spin_unlock(&nfs_uuid_lock);
+}
+
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
struct rpc_clnt *rpc_clnt, const struct cred *cred,
- const struct nfs_fh *nfs_fh, const fmode_t fmode)
+ const struct nfs_fh *nfs_fh, struct nfs_file_localio *nfl,
+ const fmode_t fmode)
{
struct net *net;
struct nfsd_file *localio;
@@ -180,11 +189,50 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
cred, nfs_fh, fmode);
if (IS_ERR(localio))
nfs_to_nfsd_net_put(net);
+ else
+ nfs_uuid_add_file(uuid, nfl);
return localio;
}
EXPORT_SYMBOL_GPL(nfs_open_local_fh);
+void nfs_close_local_fh(struct nfs_file_localio *nfl)
+{
+ struct nfsd_file *ro_nf = NULL;
+ struct nfsd_file *rw_nf = NULL;
+ nfs_uuid_t *nfs_uuid;
+
+ rcu_read_lock();
+ nfs_uuid = rcu_dereference(nfl->nfs_uuid);
+ if (!nfs_uuid) {
+ /* regular (non-LOCALIO) NFS will hammer this */
+ rcu_read_unlock();
+ return;
+ }
+
+ ro_nf = rcu_access_pointer(nfl->ro_file);
+ rw_nf = rcu_access_pointer(nfl->rw_file);
+ if (ro_nf || rw_nf) {
+ spin_lock(&nfs_uuid_lock);
+ if (ro_nf)
+ ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
+ if (rw_nf)
+ rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
+
+ rcu_assign_pointer(nfl->nfs_uuid, NULL);
+ spin_unlock(&nfs_uuid_lock);
+ rcu_read_unlock();
+
+ if (ro_nf)
+ nfs_to_nfsd_file_put_local(ro_nf);
+ if (rw_nf)
+ nfs_to_nfsd_file_put_local(rw_nf);
+ return;
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(nfs_close_local_fh);
+
/*
* The NFS LOCALIO code needs to call into NFSD using various symbols,
* but cannot be statically linked, because that will make the NFS
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 039898d70954f..67ae2c3f41d20 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -77,6 +77,23 @@ struct nfs_lock_context {
struct rcu_head rcu_head;
};
+struct nfs_file_localio {
+ struct nfsd_file __rcu *ro_file;
+ struct nfsd_file __rcu *rw_file;
+ struct list_head list;
+ void __rcu *nfs_uuid; /* opaque pointer to 'nfs_uuid_t' */
+};
+
+static inline void nfs_localio_file_init(struct nfs_file_localio *nfl)
+{
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ nfl->ro_file = NULL;
+ nfl->rw_file = NULL;
+ INIT_LIST_HEAD(&nfl->list);
+ nfl->nfs_uuid = NULL;
+#endif
+}
+
struct nfs4_state;
struct nfs_open_context {
struct nfs_lock_context lock_context;
@@ -87,15 +104,16 @@ struct nfs_open_context {
struct nfs4_state *state;
fmode_t mode;
+ int error;
unsigned long flags;
#define NFS_CONTEXT_BAD (2)
#define NFS_CONTEXT_UNLOCK (3)
#define NFS_CONTEXT_FILE_OPEN (4)
- int error;
- struct list_head list;
struct nfs4_threshold *mdsthreshold;
+ struct list_head list;
struct rcu_head rcu_head;
+ struct nfs_file_localio nfl;
};
struct nfs_open_dir_context {
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index 4d5583873f418..7cfc6720ed26d 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -6,10 +6,6 @@
#ifndef __LINUX_NFSLOCALIO_H
#define __LINUX_NFSLOCALIO_H
-
-/* nfsd_file structure is purposely kept opaque to NFS client */
-struct nfsd_file;
-
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
#include <linux/module.h>
@@ -21,6 +17,7 @@ struct nfsd_file;
#include <net/net_namespace.h>
struct nfs_client;
+struct nfs_file_localio;
/*
* Useful to allow a client to negotiate if localio
@@ -52,6 +49,7 @@ extern struct nfsd_file *
nfsd_open_local_fh(struct net *, struct auth_domain *, struct rpc_clnt *,
const struct cred *, const struct nfs_fh *,
const fmode_t) __must_hold(rcu);
+void nfs_close_local_fh(struct nfs_file_localio *);
struct nfsd_localio_operations {
bool (*nfsd_serv_try_get)(struct net *);
@@ -73,7 +71,8 @@ extern const struct nfsd_localio_operations *nfs_to;
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
struct rpc_clnt *, const struct cred *,
- const struct nfs_fh *, const fmode_t);
+ const struct nfs_fh *, struct nfs_file_localio *,
+ const fmode_t);
static inline void nfs_to_nfsd_net_put(struct net *net)
{
@@ -100,12 +99,15 @@ static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
}
#else /* CONFIG_NFS_LOCALIO */
+
+struct nfs_file_localio;
+static inline void nfs_close_local_fh(struct nfs_file_localio *nfl)
+{
+}
static inline void nfsd_localio_ops_init(void)
{
}
-static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
-{
-}
+
#endif /* CONFIG_NFS_LOCALIO */
#endif /* __LINUX_NFSLOCALIO_H */
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client
2024-11-14 3:59 ` [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client Mike Snitzer
@ 2024-11-15 3:12 ` NeilBrown
2024-11-15 16:49 ` Mike Snitzer
0 siblings, 1 reply; 23+ messages in thread
From: NeilBrown @ 2024-11-15 3:12 UTC (permalink / raw)
To: Mike Snitzer
Cc: linux-nfs, Anna Schumaker, Trond Myklebust, Chuck Lever,
Jeff Layton
On Thu, 14 Nov 2024, Mike Snitzer wrote:
> This commit switches from leaning heavily on NFSD's filecache (in
> terms of GC'd nfsd_files) back to caching nfsd_files in the
> client. A later commit will add the callback mechanism needed to
> allow NFSD to force the NFS client to cleanup all caches files.
>
> Add nfs_fh_localio_init() and 'struct nfs_fh_localio' to cache opened
> nfsd_file(s) (both a RO and RW nfsd_file is able to be opened and
> cached for a given nfs_fh).
>
> Update nfs_local_open_fh() to cache the nfsd_file once it is opened
> using __nfs_local_open_fh().
>
> Introduce nfs_close_local_fh() to clear the cached open nfsd_files and
> call nfs_to_nfsd_file_put_local().
>
> Refcounting is such that:
> - nfs_local_open_fh() is paired with nfs_close_local_fh().
> - __nfs_local_open_fh() is paired with nfs_to_nfsd_file_put_local().
> - nfs_local_file_get() is paired with nfs_local_file_put().
>
> Signed-off-by: Mike Snitzer <snitzer@kernel.org>
> ---
> fs/nfs/flexfilelayout/flexfilelayout.c | 29 +++++----
> fs/nfs/flexfilelayout/flexfilelayout.h | 1 +
> fs/nfs/inode.c | 3 +
> fs/nfs/internal.h | 4 +-
> fs/nfs/localio.c | 89 +++++++++++++++++++++-----
> fs/nfs/pagelist.c | 5 +-
> fs/nfs/write.c | 3 +-
> fs/nfs_common/nfslocalio.c | 52 ++++++++++++++-
> include/linux/nfs_fs.h | 22 ++++++-
> include/linux/nfslocalio.h | 18 +++---
> 10 files changed, 181 insertions(+), 45 deletions(-)
>
> diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
> index f78115c6c2c12..451f168d882be 100644
> --- a/fs/nfs/flexfilelayout/flexfilelayout.c
> +++ b/fs/nfs/flexfilelayout/flexfilelayout.c
> @@ -164,18 +164,17 @@ decode_name(struct xdr_stream *xdr, u32 *id)
> }
>
> static struct nfsd_file *
> -ff_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> +ff_local_open_fh(struct pnfs_layout_segment *lseg, u32 ds_idx,
> + struct nfs_client *clp, const struct cred *cred,
> struct nfs_fh *fh, fmode_t mode)
> {
> - if (mode & FMODE_WRITE) {
> - /*
> - * Always request read and write access since this corresponds
> - * to a rw layout.
> - */
> - mode |= FMODE_READ;
> - }
> +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> + struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
>
> - return nfs_local_open_fh(clp, cred, fh, mode);
> + return nfs_local_open_fh(clp, cred, fh, &mirror->nfl, mode);
> +#else
> + return NULL;
> +#endif
> }
>
> static bool ff_mirror_match_fh(const struct nfs4_ff_layout_mirror *m1,
> @@ -247,6 +246,9 @@ static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags)
> spin_lock_init(&mirror->lock);
> refcount_set(&mirror->ref, 1);
> INIT_LIST_HEAD(&mirror->mirrors);
> +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> + nfs_localio_file_init(&mirror->nfl);
> +#endif
Can we make nfs_localio_file_init() a #define in the no-NFS_LOCALIO
case, we don't need the #if.
(every time you write #if in a .c file think to your self "Neil will
hate this". See also coding-style.rst. 21) Conditional Compilation.
> }
> return mirror;
> }
> @@ -257,6 +259,9 @@ static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror)
>
> ff_layout_remove_mirror(mirror);
> kfree(mirror->fh_versions);
> +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> + nfs_close_local_fh(&mirror->nfl);
> +#endif
> cred = rcu_access_pointer(mirror->ro_cred);
> put_cred(cred);
> cred = rcu_access_pointer(mirror->rw_cred);
> @@ -1820,7 +1825,7 @@ ff_layout_read_pagelist(struct nfs_pgio_header *hdr)
> hdr->mds_offset = offset;
>
> /* Start IO accounting for local read */
> - localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh, FMODE_READ);
> + localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh, FMODE_READ);
> if (localio) {
> hdr->task.tk_start = ktime_get();
> ff_layout_read_record_layoutstats_start(&hdr->task, hdr);
> @@ -1896,7 +1901,7 @@ ff_layout_write_pagelist(struct nfs_pgio_header *hdr, int sync)
> hdr->args.offset = offset;
>
> /* Start IO accounting for local write */
> - localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
> + localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh,
> FMODE_READ|FMODE_WRITE);
> if (localio) {
> hdr->task.tk_start = ktime_get();
> @@ -1981,7 +1986,7 @@ static int ff_layout_initiate_commit(struct nfs_commit_data *data, int how)
> data->args.fh = fh;
>
> /* Start IO accounting for local commit */
> - localio = ff_local_open_fh(ds->ds_clp, ds_cred, fh,
> + localio = ff_local_open_fh(lseg, idx, ds->ds_clp, ds_cred, fh,
> FMODE_READ|FMODE_WRITE);
> if (localio) {
> data->task.tk_start = ktime_get();
> diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h
> index f84b3fb0dddd8..095df09017a57 100644
> --- a/fs/nfs/flexfilelayout/flexfilelayout.h
> +++ b/fs/nfs/flexfilelayout/flexfilelayout.h
> @@ -83,6 +83,7 @@ struct nfs4_ff_layout_mirror {
> nfs4_stateid stateid;
> const struct cred __rcu *ro_cred;
> const struct cred __rcu *rw_cred;
> + struct nfs_file_localio nfl;
This probably wants a #if around it though - it is in a .h after all.
> refcount_t ref;
> spinlock_t lock;
> unsigned long flags;
> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> index 596f351701372..1aa67fca69b2f 100644
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -1137,6 +1137,8 @@ struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry,
> ctx->lock_context.open_context = ctx;
> INIT_LIST_HEAD(&ctx->list);
> ctx->mdsthreshold = NULL;
> + nfs_localio_file_init(&ctx->nfl);
> +
> return ctx;
> }
> EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
> @@ -1168,6 +1170,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
> nfs_sb_deactive(sb);
> put_rpccred(rcu_dereference_protected(ctx->ll_cred, 1));
> kfree(ctx->mdsthreshold);
> + nfs_close_local_fh(&ctx->nfl);
> kfree_rcu(ctx, rcu_head);
> }
>
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index 430733e3eff26..57af3ab3adbe5 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -459,6 +459,7 @@ extern void nfs_local_probe(struct nfs_client *);
> extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
> const struct cred *,
> struct nfs_fh *,
> + struct nfs_file_localio *,
> const fmode_t);
> extern int nfs_local_doio(struct nfs_client *,
> struct nfsd_file *,
> @@ -474,7 +475,8 @@ static inline void nfs_local_disable(struct nfs_client *clp) {}
> static inline void nfs_local_probe(struct nfs_client *clp) {}
> static inline struct nfsd_file *
> nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> - struct nfs_fh *fh, const fmode_t mode)
> + struct nfs_fh *fh, struct nfs_file_localio *nfl,
> + const fmode_t mode)
> {
> return NULL;
> }
> diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
> index 7191135b47a42..7e432057c3a1f 100644
> --- a/fs/nfs/localio.c
> +++ b/fs/nfs/localio.c
> @@ -211,27 +211,33 @@ void nfs_local_probe(struct nfs_client *clp)
> }
> EXPORT_SYMBOL_GPL(nfs_local_probe);
>
> +static inline struct nfsd_file *nfs_local_file_get(struct nfsd_file *nf)
> +{
> + return nfs_to->nfsd_file_get(nf);
> +}
> +
> +static inline void nfs_local_file_put(struct nfsd_file *nf)
> +{
> + nfs_to->nfsd_file_put(nf);
> +}
> +
> /*
> - * nfs_local_open_fh - open a local filehandle in terms of nfsd_file
> + * __nfs_local_open_fh - open a local filehandle in terms of nfsd_file.
> *
> - * Returns a pointer to a struct nfsd_file or NULL
> + * Returns a pointer to a struct nfsd_file or ERR_PTR.
> + * Caller must release returned nfsd_file with nfs_to_nfsd_file_put_local().
> */
> -struct nfsd_file *
> -nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> - struct nfs_fh *fh, const fmode_t mode)
> +static struct nfsd_file *
> +__nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> + struct nfs_fh *fh, struct nfs_file_localio *nfl,
> + const fmode_t mode)
> {
> struct nfsd_file *localio;
> - int status;
> -
> - if (!nfs_server_is_local(clp))
> - return NULL;
> - if (mode & ~(FMODE_READ | FMODE_WRITE))
> - return NULL;
>
> localio = nfs_open_local_fh(&clp->cl_uuid, clp->cl_rpcclient,
> - cred, fh, mode);
> + cred, fh, nfl, mode);
> if (IS_ERR(localio)) {
> - status = PTR_ERR(localio);
> + int status = PTR_ERR(localio);
> trace_nfs_local_open_fh(fh, mode, status);
> switch (status) {
> case -ENOMEM:
> @@ -240,10 +246,59 @@ nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> /* Revalidate localio, will disable if unsupported */
> nfs_local_probe(clp);
> }
> - return NULL;
> }
> return localio;
> }
> +
> +/*
> + * nfs_local_open_fh - open a local filehandle in terms of nfsd_file.
> + * First checking if the open nfsd_file is already cached, otherwise
> + * must __nfs_local_open_fh and insert the nfsd_file in nfs_file_localio.
> + *
> + * Returns a pointer to a struct nfsd_file or NULL.
> + */
> +struct nfsd_file *
> +nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> + struct nfs_fh *fh, struct nfs_file_localio *nfl,
> + const fmode_t mode)
> +{
> + struct nfsd_file *nf, *new, __rcu **pnf;
> +
> + if (!nfs_server_is_local(clp))
> + return NULL;
> + if (mode & ~(FMODE_READ | FMODE_WRITE))
> + return NULL;
> +
> + if (mode & FMODE_WRITE)
> + pnf = &nfl->rw_file;
> + else
> + pnf = &nfl->ro_file;
> +
> + new = NULL;
> + rcu_read_lock();
> + nf = rcu_dereference(*pnf);
> + if (!nf) {
> + rcu_read_unlock();
> + new = __nfs_local_open_fh(clp, cred, fh, nfl, mode);
> + if (IS_ERR(new))
> + return NULL;
> + /* try to swap in the pointer */
> + spin_lock(&clp->cl_uuid.lock);
> + nf = rcu_dereference_protected(*pnf, 1);
> + if (!nf) {
> + nf = new;
> + new = NULL;
> + rcu_assign_pointer(*pnf, nf);
> + }
> + spin_unlock(&clp->cl_uuid.lock);
> + rcu_read_lock();
> + }
> + nf = nfs_local_file_get(nf);
> + rcu_read_unlock();
> + if (new)
> + nfs_to_nfsd_file_put_local(new);
> + return nf;
> +}
> EXPORT_SYMBOL_GPL(nfs_local_open_fh);
>
> static struct bio_vec *
> @@ -347,7 +402,7 @@ nfs_local_pgio_release(struct nfs_local_kiocb *iocb)
> {
> struct nfs_pgio_header *hdr = iocb->hdr;
>
> - nfs_to_nfsd_file_put_local(iocb->localio);
> + nfs_local_file_put(iocb->localio);
> nfs_local_iocb_free(iocb);
> nfs_local_hdr_release(hdr, hdr->task.tk_ops);
> }
> @@ -694,7 +749,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
> if (status != 0) {
> if (status == -EAGAIN)
> nfs_local_disable(clp);
> - nfs_to_nfsd_file_put_local(localio);
> + nfs_local_file_put(localio);
> hdr->task.tk_status = status;
> nfs_local_hdr_release(hdr, call_ops);
> }
> @@ -745,7 +800,7 @@ nfs_local_release_commit_data(struct nfsd_file *localio,
> struct nfs_commit_data *data,
> const struct rpc_call_ops *call_ops)
> {
> - nfs_to_nfsd_file_put_local(localio);
> + nfs_local_file_put(localio);
> call_ops->rpc_call_done(&data->task, data);
> call_ops->rpc_release(data);
> }
> diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
> index e27c07bd89290..11968dcb72431 100644
> --- a/fs/nfs/pagelist.c
> +++ b/fs/nfs/pagelist.c
> @@ -961,8 +961,9 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
> struct nfs_client *clp = NFS_SERVER(hdr->inode)->nfs_client;
>
> struct nfsd_file *localio =
> - nfs_local_open_fh(clp, hdr->cred,
> - hdr->args.fh, hdr->args.context->mode);
> + nfs_local_open_fh(clp, hdr->cred, hdr->args.fh,
> + &hdr->args.context->nfl,
> + hdr->args.context->mode);
>
> if (NFS_SERVER(hdr->inode)->nfs_client->cl_minorversion)
> task_flags = RPC_TASK_MOVEABLE;
> diff --git a/fs/nfs/write.c b/fs/nfs/write.c
> index 2da00987d9ed4..75779e3cac16d 100644
> --- a/fs/nfs/write.c
> +++ b/fs/nfs/write.c
> @@ -1817,7 +1817,8 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how,
> task_flags = RPC_TASK_MOVEABLE;
>
> localio = nfs_local_open_fh(NFS_SERVER(inode)->nfs_client, data->cred,
> - data->args.fh, data->context->mode);
> + data->args.fh, &data->context->nfl,
> + data->context->mode);
> return nfs_initiate_commit(NFS_CLIENT(inode), data, NFS_PROTO(inode),
> data->mds_ops, how,
> RPC_TASK_CRED_NOREF | task_flags, localio);
> diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
> index abc132166742e..35a2e48731df6 100644
> --- a/fs/nfs_common/nfslocalio.c
> +++ b/fs/nfs_common/nfslocalio.c
> @@ -9,7 +9,7 @@
> #include <linux/nfslocalio.h>
> #include <linux/nfs3.h>
> #include <linux/nfs4.h>
> -#include <linux/nfs_fs_sb.h>
> +#include <linux/nfs_fs.h>
> #include <net/netns/generic.h>
>
> MODULE_LICENSE("GPL");
> @@ -151,9 +151,18 @@ void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
> }
> EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
>
> +static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
> +{
> + spin_lock(&nfs_uuid_lock);
> + if (!nfl->nfs_uuid)
> + rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
> + spin_unlock(&nfs_uuid_lock);
> +}
> +
> struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
> struct rpc_clnt *rpc_clnt, const struct cred *cred,
> - const struct nfs_fh *nfs_fh, const fmode_t fmode)
> + const struct nfs_fh *nfs_fh, struct nfs_file_localio *nfl,
> + const fmode_t fmode)
> {
> struct net *net;
> struct nfsd_file *localio;
> @@ -180,11 +189,50 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
> cred, nfs_fh, fmode);
> if (IS_ERR(localio))
> nfs_to_nfsd_net_put(net);
> + else
> + nfs_uuid_add_file(uuid, nfl);
>
> return localio;
> }
> EXPORT_SYMBOL_GPL(nfs_open_local_fh);
>
> +void nfs_close_local_fh(struct nfs_file_localio *nfl)
> +{
> + struct nfsd_file *ro_nf = NULL;
> + struct nfsd_file *rw_nf = NULL;
> + nfs_uuid_t *nfs_uuid;
> +
> + rcu_read_lock();
> + nfs_uuid = rcu_dereference(nfl->nfs_uuid);
nfl->nfs_uuid is a void*. Why do you assign it to an 'nfs_uuid_t *' ??
And why do we need rcu here? We never dereference that pointer.
I would just have
if (!nfl->nfs_uuid || (!nfl->ro_file && !nfl->rw_file))
return;
then take the spinlock and do it the real work.
> + if (!nfs_uuid) {
> + /* regular (non-LOCALIO) NFS will hammer this */
> + rcu_read_unlock();
> + return;
> + }
> +
> + ro_nf = rcu_access_pointer(nfl->ro_file);
> + rw_nf = rcu_access_pointer(nfl->rw_file);
> + if (ro_nf || rw_nf) {
> + spin_lock(&nfs_uuid_lock);
> + if (ro_nf)
> + ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
> + if (rw_nf)
> + rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
> +
> + rcu_assign_pointer(nfl->nfs_uuid, NULL);
> + spin_unlock(&nfs_uuid_lock);
> + rcu_read_unlock();
> +
> + if (ro_nf)
> + nfs_to_nfsd_file_put_local(ro_nf);
> + if (rw_nf)
> + nfs_to_nfsd_file_put_local(rw_nf);
> + return;
> + }
> + rcu_read_unlock();
> +}
> +EXPORT_SYMBOL_GPL(nfs_close_local_fh);
> +
> /*
> * The NFS LOCALIO code needs to call into NFSD using various symbols,
> * but cannot be statically linked, because that will make the NFS
> diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
> index 039898d70954f..67ae2c3f41d20 100644
> --- a/include/linux/nfs_fs.h
> +++ b/include/linux/nfs_fs.h
> @@ -77,6 +77,23 @@ struct nfs_lock_context {
> struct rcu_head rcu_head;
> };
>
> +struct nfs_file_localio {
> + struct nfsd_file __rcu *ro_file;
> + struct nfsd_file __rcu *rw_file;
> + struct list_head list;
> + void __rcu *nfs_uuid; /* opaque pointer to 'nfs_uuid_t' */
I've said it above but just to be clear: No "__rcu" here.
Thanks,
NeilBrown
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client
2024-11-15 3:12 ` NeilBrown
@ 2024-11-15 16:49 ` Mike Snitzer
0 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-15 16:49 UTC (permalink / raw)
To: NeilBrown
Cc: linux-nfs, Anna Schumaker, Trond Myklebust, Chuck Lever,
Jeff Layton
On Fri, Nov 15, 2024 at 02:12:46PM +1100, NeilBrown wrote:
> On Thu, 14 Nov 2024, Mike Snitzer wrote:
> > This commit switches from leaning heavily on NFSD's filecache (in
> > terms of GC'd nfsd_files) back to caching nfsd_files in the
> > client. A later commit will add the callback mechanism needed to
> > allow NFSD to force the NFS client to cleanup all caches files.
> >
> > Add nfs_fh_localio_init() and 'struct nfs_fh_localio' to cache opened
> > nfsd_file(s) (both a RO and RW nfsd_file is able to be opened and
> > cached for a given nfs_fh).
> >
> > Update nfs_local_open_fh() to cache the nfsd_file once it is opened
> > using __nfs_local_open_fh().
> >
> > Introduce nfs_close_local_fh() to clear the cached open nfsd_files and
> > call nfs_to_nfsd_file_put_local().
> >
> > Refcounting is such that:
> > - nfs_local_open_fh() is paired with nfs_close_local_fh().
> > - __nfs_local_open_fh() is paired with nfs_to_nfsd_file_put_local().
> > - nfs_local_file_get() is paired with nfs_local_file_put().
> >
> > Signed-off-by: Mike Snitzer <snitzer@kernel.org>
> > ---
> > fs/nfs/flexfilelayout/flexfilelayout.c | 29 +++++----
> > fs/nfs/flexfilelayout/flexfilelayout.h | 1 +
> > fs/nfs/inode.c | 3 +
> > fs/nfs/internal.h | 4 +-
> > fs/nfs/localio.c | 89 +++++++++++++++++++++-----
> > fs/nfs/pagelist.c | 5 +-
> > fs/nfs/write.c | 3 +-
> > fs/nfs_common/nfslocalio.c | 52 ++++++++++++++-
> > include/linux/nfs_fs.h | 22 ++++++-
> > include/linux/nfslocalio.h | 18 +++---
> > 10 files changed, 181 insertions(+), 45 deletions(-)
> >
> > diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c
> > index f78115c6c2c12..451f168d882be 100644
> > --- a/fs/nfs/flexfilelayout/flexfilelayout.c
> > +++ b/fs/nfs/flexfilelayout/flexfilelayout.c
> > @@ -164,18 +164,17 @@ decode_name(struct xdr_stream *xdr, u32 *id)
> > }
> >
> > static struct nfsd_file *
> > -ff_local_open_fh(struct nfs_client *clp, const struct cred *cred,
> > +ff_local_open_fh(struct pnfs_layout_segment *lseg, u32 ds_idx,
> > + struct nfs_client *clp, const struct cred *cred,
> > struct nfs_fh *fh, fmode_t mode)
> > {
> > - if (mode & FMODE_WRITE) {
> > - /*
> > - * Always request read and write access since this corresponds
> > - * to a rw layout.
> > - */
> > - mode |= FMODE_READ;
> > - }
> > +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> > + struct nfs4_ff_layout_mirror *mirror = FF_LAYOUT_COMP(lseg, ds_idx);
> >
> > - return nfs_local_open_fh(clp, cred, fh, mode);
> > + return nfs_local_open_fh(clp, cred, fh, &mirror->nfl, mode);
> > +#else
> > + return NULL;
> > +#endif
> > }
> >
> > static bool ff_mirror_match_fh(const struct nfs4_ff_layout_mirror *m1,
> > @@ -247,6 +246,9 @@ static struct nfs4_ff_layout_mirror *ff_layout_alloc_mirror(gfp_t gfp_flags)
> > spin_lock_init(&mirror->lock);
> > refcount_set(&mirror->ref, 1);
> > INIT_LIST_HEAD(&mirror->mirrors);
> > +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> > + nfs_localio_file_init(&mirror->nfl);
> > +#endif
>
> Can we make nfs_localio_file_init() a #define in the no-NFS_LOCALIO
> case, we don't need the #if.
> (every time you write #if in a .c file think to your self "Neil will
> hate this". See also coding-style.rst. 21) Conditional Compilation.
It already was defined in header, and doesn't need wrapping in caller,
I just mistakenly wrapped it again in ff_layout_alloc_mirror().
Fixed.
> > }
> > return mirror;
> > }
> > @@ -257,6 +259,9 @@ static void ff_layout_free_mirror(struct nfs4_ff_layout_mirror *mirror)
> >
> > ff_layout_remove_mirror(mirror);
> > kfree(mirror->fh_versions);
> > +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> > + nfs_close_local_fh(&mirror->nfl);
> > +#endif
> > cred = rcu_access_pointer(mirror->ro_cred);
> > put_cred(cred);
> > cred = rcu_access_pointer(mirror->rw_cred);
I fixed this call to nfs_close_local_fh() to not use #if also.
> > diff --git a/fs/nfs/flexfilelayout/flexfilelayout.h b/fs/nfs/flexfilelayout/flexfilelayout.h
> > index f84b3fb0dddd8..095df09017a57 100644
> > --- a/fs/nfs/flexfilelayout/flexfilelayout.h
> > +++ b/fs/nfs/flexfilelayout/flexfilelayout.h
> > @@ -83,6 +83,7 @@ struct nfs4_ff_layout_mirror {
> > nfs4_stateid stateid;
> > const struct cred __rcu *ro_cred;
> > const struct cred __rcu *rw_cred;
> > + struct nfs_file_localio nfl;
>
> This probably wants a #if around it though - it is in a .h after all.
I unconditionall defined 'struct nfs_file_localio' otherwise the
calls that access this nfl member (nfs_localio_file_init) _will_ need
special treatment.
>
> > refcount_t ref;
> > spinlock_t lock;
> > unsigned long flags;
<snip>
> > diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
> > index abc132166742e..35a2e48731df6 100644
> > --- a/fs/nfs_common/nfslocalio.c
> > +++ b/fs/nfs_common/nfslocalio.c
> > @@ -151,9 +151,18 @@ void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
> >
> > +static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
> > +{
> > + spin_lock(&nfs_uuid_lock);
> > + if (!nfl->nfs_uuid)
> > + rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
> > + spin_unlock(&nfs_uuid_lock);
> > +}
> > +
> > struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
> > struct rpc_clnt *rpc_clnt, const struct cred *cred,
> > - const struct nfs_fh *nfs_fh, const fmode_t fmode)
> > + const struct nfs_fh *nfs_fh, struct nfs_file_localio *nfl,
> > + const fmode_t fmode)
> > {
> > struct net *net;
> > struct nfsd_file *localio;
> > @@ -180,11 +189,50 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
> > cred, nfs_fh, fmode);
> > if (IS_ERR(localio))
> > nfs_to_nfsd_net_put(net);
> > + else
> > + nfs_uuid_add_file(uuid, nfl);
> >
> > return localio;
> > }
> > EXPORT_SYMBOL_GPL(nfs_open_local_fh);
> >
> > +void nfs_close_local_fh(struct nfs_file_localio *nfl)
> > +{
> > + struct nfsd_file *ro_nf = NULL;
> > + struct nfsd_file *rw_nf = NULL;
> > + nfs_uuid_t *nfs_uuid;
> > +
> > + rcu_read_lock();
> > + nfs_uuid = rcu_dereference(nfl->nfs_uuid);
>
> nfl->nfs_uuid is a void*. Why do you assign it to an 'nfs_uuid_t *' ??
Yeah, I made it void* to not have to play games with nfs_uuid_t in
include/linux/nfs_fs.h
It is assigned to 'nfs_uuid_t *' because I dereference it to get its
spinlock in later patch (that splits the nfs_uuid_lock).
> And why do we need rcu here? We never dereference that pointer.
As I just said, I do in the patch that splits the nfs_uuid_lock.
And I made nfl->nfs_uuid management RCU protected given it being NULL
or not is significant and I'd rather not require other synchronization
via other locking.
SO while I appreciate your review here, the code in final form (once
nfs_uuid_lock lock split patch applied) does need RCU as I've written
it.
> I would just have
>
> if (!nfl->nfs_uuid || (!nfl->ro_file && !nfl->rw_file))
> return;
>
> then take the spinlock and do it the real work.
>
> > + if (!nfs_uuid) {
> > + /* regular (non-LOCALIO) NFS will hammer this */
> > + rcu_read_unlock();
> > + return;
> > + }
> > +
> > + ro_nf = rcu_access_pointer(nfl->ro_file);
> > + rw_nf = rcu_access_pointer(nfl->rw_file);
> > + if (ro_nf || rw_nf) {
> > + spin_lock(&nfs_uuid_lock);
> > + if (ro_nf)
> > + ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
> > + if (rw_nf)
> > + rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
> > +
> > + rcu_assign_pointer(nfl->nfs_uuid, NULL);
> > + spin_unlock(&nfs_uuid_lock);
> > + rcu_read_unlock();
> > +
> > + if (ro_nf)
> > + nfs_to_nfsd_file_put_local(ro_nf);
> > + if (rw_nf)
> > + nfs_to_nfsd_file_put_local(rw_nf);
> > + return;
> > + }
> > + rcu_read_unlock();
> > +}
> > +EXPORT_SYMBOL_GPL(nfs_close_local_fh);
> > +
> > /*
> > * The NFS LOCALIO code needs to call into NFSD using various symbols,
> > * but cannot be statically linked, because that will make the NFS
> > diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
> > index 039898d70954f..67ae2c3f41d20 100644
> > --- a/include/linux/nfs_fs.h
> > +++ b/include/linux/nfs_fs.h
> > @@ -77,6 +77,23 @@ struct nfs_lock_context {
> > struct rcu_head rcu_head;
> > };
> >
> > +struct nfs_file_localio {
> > + struct nfsd_file __rcu *ro_file;
> > + struct nfsd_file __rcu *rw_file;
> > + struct list_head list;
> > + void __rcu *nfs_uuid; /* opaque pointer to 'nfs_uuid_t' */
>
> I've said it above but just to be clear: No "__rcu" here.
Please look at final form of nfs_close_local_fh() with all patches
applied. I wanted to avoid churn in nfs_close_local_fh(), but yeah it
does have some needless RCU-ification in this intermediate patch.
That said, could be there is still room for cleanup even with the
final nfs_close_local_fh()...
Thanks,
Mike
^ permalink raw reply [flat|nested] 23+ messages in thread
* [for-6.13 PATCH v2 07/15] nfsd: update percpu_ref to manage references on nfsd_net
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (5 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 06/15] nfs: cache all open LOCALIO nfsd_file(s) in client Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 08/15] nfsd: rename nfsd_serv_ prefixed methods and variables with nfsd_net_ Mike Snitzer
` (8 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Holding a reference on nfsd_net is what is required, it was never
actually about ensuring nn->nfsd_serv available.
Move waiting for outstanding percpu references from
nfsd_destroy_serv() to nfsd_shutdown_net().
By moving it later it will be possible to invalidate localio clients
during nfsd_file_cache_shutdown_net() via __nfsd_file_cache_purge().
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfsd/nfssvc.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 49e2f32102ab5..6ca5540424266 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -436,6 +436,10 @@ static void nfsd_shutdown_net(struct net *net)
if (!nn->nfsd_net_up)
return;
+
+ percpu_ref_kill_and_confirm(&nn->nfsd_serv_ref, nfsd_serv_done);
+ wait_for_completion(&nn->nfsd_serv_confirm_done);
+
nfsd_export_flush(net);
nfs4_state_shutdown_net(net);
nfsd_reply_cache_shutdown(nn);
@@ -444,7 +448,10 @@ static void nfsd_shutdown_net(struct net *net)
lockd_down(net);
nn->lockd_up = false;
}
+
+ wait_for_completion(&nn->nfsd_serv_free_done);
percpu_ref_exit(&nn->nfsd_serv_ref);
+
nn->nfsd_net_up = false;
nfsd_shutdown_generic();
}
@@ -526,11 +533,6 @@ void nfsd_destroy_serv(struct net *net)
lockdep_assert_held(&nfsd_mutex);
- percpu_ref_kill_and_confirm(&nn->nfsd_serv_ref, nfsd_serv_done);
- wait_for_completion(&nn->nfsd_serv_confirm_done);
- wait_for_completion(&nn->nfsd_serv_free_done);
- /* percpu_ref_exit is called in nfsd_shutdown_net */
-
spin_lock(&nfsd_notifier_lock);
nn->nfsd_serv = NULL;
spin_unlock(&nfsd_notifier_lock);
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 08/15] nfsd: rename nfsd_serv_ prefixed methods and variables with nfsd_net_
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (6 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 07/15] nfsd: update percpu_ref to manage references on nfsd_net Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 09/15] nfsd: nfsd_file_acquire_local no longer returns GC'd nfsd_file Mike Snitzer
` (7 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Also update Documentation/filesystems/nfs/localio.rst accordingly
and reduce the technical documentation debt that was previously
captured in that document.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
Documentation/filesystems/nfs/localio.rst | 81 +++++++----------------
fs/nfs_common/nfslocalio.c | 10 ++-
fs/nfsd/filecache.c | 2 +-
fs/nfsd/localio.c | 4 +-
fs/nfsd/netns.h | 11 +--
fs/nfsd/nfssvc.c | 34 +++++-----
include/linux/nfslocalio.h | 12 ++--
7 files changed, 64 insertions(+), 90 deletions(-)
diff --git a/Documentation/filesystems/nfs/localio.rst b/Documentation/filesystems/nfs/localio.rst
index eb3dc764cfce8..9d991a958c81c 100644
--- a/Documentation/filesystems/nfs/localio.rst
+++ b/Documentation/filesystems/nfs/localio.rst
@@ -218,64 +218,30 @@ NFS Client and Server Interlock
===============================
LOCALIO provides the nfs_uuid_t object and associated interfaces to
-allow proper network namespace (net-ns) and NFSD object refcounting:
+allow proper network namespace (net-ns) and NFSD object refcounting.
- We don't want to keep a long-term counted reference on each NFSD's
- net-ns in the client because that prevents a server container from
- completely shutting down.
-
- So we avoid taking a reference at all and rely on the per-cpu
- reference to the server (detailed below) being sufficient to keep
- the net-ns active. This involves allowing the NFSD's net-ns exit
- code to iterate all active clients and clear their ->net pointers
- (which are needed to find the per-cpu-refcount for the nfsd_serv).
-
- Details:
-
- - Embed nfs_uuid_t in nfs_client. nfs_uuid_t provides a list_head
- that can be used to find the client. It does add the 16-byte
- uuid_t to nfs_client so it is bigger than needed (given that
- uuid_t is only used during the initial NFS client and server
- LOCALIO handshake to determine if they are local to each other).
- If that is really a problem we can find a fix.
-
- - When the nfs server confirms that the uuid_t is local, it moves
- the nfs_uuid_t onto a per-net-ns list in NFSD's nfsd_net.
-
- - When each server's net-ns is shutting down - in a "pre_exit"
- handler, all these nfs_uuid_t have their ->net cleared. There is
- an rcu_synchronize() call between pre_exit() handlers and exit()
- handlers so any caller that sees nfs_uuid_t ->net as not NULL can
- safely manage the per-cpu-refcount for nfsd_serv.
-
- - The client's nfs_uuid_t is passed to nfsd_open_local_fh() so it
- can safely dereference ->net in a private rcu_read_lock() section
- to allow safe access to the associated nfsd_net and nfsd_serv.
-
-So LOCALIO required the introduction and use of NFSD's percpu_ref to
-interlock nfsd_destroy_serv() and nfsd_open_local_fh(), to ensure each
-nn->nfsd_serv is not destroyed while in use by nfsd_open_local_fh(), and
+LOCALIO required the introduction and use of NFSD's percpu nfsd_net_ref
+to interlock nfsd_shutdown_net() and nfsd_open_local_fh(), to ensure
+each net-ns is not destroyed while in use by nfsd_open_local_fh(), and
warrants a more detailed explanation:
- nfsd_open_local_fh() uses nfsd_serv_try_get() before opening its
+ nfsd_open_local_fh() uses nfsd_net_try_get() before opening its
nfsd_file handle and then the caller (NFS client) must drop the
- reference for the nfsd_file and associated nn->nfsd_serv using
- nfs_file_put_local() once it has completed its IO.
+ reference for the nfsd_file and associated net-ns using
+ nfsd_file_put_local() once it has completed its IO.
This interlock working relies heavily on nfsd_open_local_fh() being
afforded the ability to safely deal with the possibility that the
NFSD's net-ns (and nfsd_net by association) may have been destroyed
- by nfsd_destroy_serv() via nfsd_shutdown_net() -- which is only
- possible given the nfs_uuid_t ->net pointer managemenet detailed
- above.
+ by nfsd_destroy_serv() via nfsd_shutdown_net().
-All told, this elaborate interlock of the NFS client and server has been
-verified to fix an easy to hit crash that would occur if an NFSD
-instance running in a container, with a LOCALIO client mounted, is
-shutdown. Upon restart of the container and associated NFSD the client
-would go on to crash due to NULL pointer dereference that occurred due
-to the LOCALIO client's attempting to nfsd_open_local_fh(), using
-nn->nfsd_serv, without having a proper reference on nn->nfsd_serv.
+This interlock of the NFS client and server has been verified to fix an
+easy to hit crash that would occur if an NFSD instance running in a
+container, with a LOCALIO client mounted, is shutdown. Upon restart of
+the container and associated NFSD, the client would go on to crash due
+to NULL pointer dereference that occurred due to the LOCALIO client's
+attempting to nfsd_open_local_fh() without having a proper reference on
+NFSD's net-ns.
NFS Client issues IO instead of Server
======================================
@@ -308,14 +274,17 @@ fs/nfs/localio.c:nfs_local_commit().
With normal NFS that makes use of RPC to issue IO to the server, if an
application uses O_DIRECT the NFS client will bypass the pagecache but
-the NFS server will not. Because the NFS server's use of buffered IO
-affords applications to be less precise with their alignment when
-issuing IO to the NFS client. LOCALIO can be configured to use O_DIRECT
-semantics by setting the 'localio_O_DIRECT_semantics' nfs module
+the NFS server will not. The NFS server's use of buffered IO affords
+applications to be less precise with their alignment when issuing IO to
+the NFS client. But if all applications properly align their IO, LOCALIO
+can be configured to use end-to-end O_DIRECT semantics from the NFS
+client to the underlying local filesystem, that it is sharing with
+the NFS server, by setting the 'localio_O_DIRECT_semantics' nfs module
parameter to Y, e.g.:
- echo Y > /sys/module/nfs/parameters/localio_O_DIRECT_semantics
-Once enabled, it will cause LOCALIO to use O_DIRECT semantics (this may
-cause IO to fail if applications do not properly align their IO).
+ echo Y > /sys/module/nfs/parameters/localio_O_DIRECT_semantics
+Once enabled, it will cause LOCALIO to use end-to-end O_DIRECT semantics
+(but again, this may cause IO to fail if applications do not properly
+align their IO).
Security
========
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 35a2e48731df6..e75cd21f4c8bc 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -159,6 +159,10 @@ static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl
spin_unlock(&nfs_uuid_lock);
}
+/*
+ * Caller is responsible for calling nfsd_net_put and
+ * nfsd_file_put (via nfs_to_nfsd_file_put_local).
+ */
struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
struct rpc_clnt *rpc_clnt, const struct cred *cred,
const struct nfs_fh *nfs_fh, struct nfs_file_localio *nfl,
@@ -171,7 +175,7 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
* Not running in nfsd context, so must safely get reference on nfsd_serv.
* But the server may already be shutting down, if so disallow new localio.
* uuid->net is NOT a counted reference, but rcu_read_lock() ensures that
- * if uuid->net is not NULL, then calling nfsd_serv_try_get() is safe
+ * if uuid->net is not NULL, then calling nfsd_net_try_get() is safe
* and if it succeeds we will have an implied reference to the net.
*
* Otherwise NFS may not have ref on NFSD and therefore cannot safely
@@ -179,12 +183,12 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
*/
rcu_read_lock();
net = rcu_dereference(uuid->net);
- if (!net || !nfs_to->nfsd_serv_try_get(net)) {
+ if (!net || !nfs_to->nfsd_net_try_get(net)) {
rcu_read_unlock();
return ERR_PTR(-ENXIO);
}
rcu_read_unlock();
- /* We have an implied reference to net thanks to nfsd_serv_try_get */
+ /* 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))
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 9a62b4da89bb7..fac98b2cb463a 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -391,7 +391,7 @@ nfsd_file_put(struct nfsd_file *nf)
}
/**
- * nfsd_file_put_local - put nfsd_file reference and arm nfsd_serv_put in caller
+ * nfsd_file_put_local - put nfsd_file reference and arm nfsd_net_put in caller
* @nf: nfsd_file of which to put the reference
*
* First save the associated net to return to caller, then put
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
index 8beda4c851119..f9a91cd3b5ec7 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -25,8 +25,8 @@
#include "cache.h"
static const struct nfsd_localio_operations nfsd_localio_ops = {
- .nfsd_serv_try_get = nfsd_serv_try_get,
- .nfsd_serv_put = nfsd_serv_put,
+ .nfsd_net_try_get = nfsd_net_try_get,
+ .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,
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 26f7b34d1a030..8faef59d71229 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -140,9 +140,10 @@ struct nfsd_net {
struct svc_info nfsd_info;
#define nfsd_serv nfsd_info.serv
- struct percpu_ref nfsd_serv_ref;
- struct completion nfsd_serv_confirm_done;
- struct completion nfsd_serv_free_done;
+
+ struct percpu_ref nfsd_net_ref;
+ struct completion nfsd_net_confirm_done;
+ struct completion nfsd_net_free_done;
/*
* clientid and stateid data for construction of net unique COPY
@@ -229,8 +230,8 @@ struct nfsd_net {
extern bool nfsd_support_version(int vers);
extern unsigned int nfsd_net_id;
-bool nfsd_serv_try_get(struct net *net);
-void nfsd_serv_put(struct net *net);
+bool nfsd_net_try_get(struct net *net);
+void nfsd_net_put(struct net *net);
void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn);
void nfsd_reset_write_verifier(struct nfsd_net *nn);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 6ca5540424266..e937e2d0ce62c 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -214,32 +214,32 @@ int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change
return 0;
}
-bool nfsd_serv_try_get(struct net *net) __must_hold(rcu)
+bool nfsd_net_try_get(struct net *net) __must_hold(rcu)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref));
+ return (nn && percpu_ref_tryget_live(&nn->nfsd_net_ref));
}
-void nfsd_serv_put(struct net *net) __must_hold(rcu)
+void nfsd_net_put(struct net *net) __must_hold(rcu)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- percpu_ref_put(&nn->nfsd_serv_ref);
+ percpu_ref_put(&nn->nfsd_net_ref);
}
-static void nfsd_serv_done(struct percpu_ref *ref)
+static void nfsd_net_done(struct percpu_ref *ref)
{
- struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_serv_ref);
+ struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_net_ref);
- complete(&nn->nfsd_serv_confirm_done);
+ complete(&nn->nfsd_net_confirm_done);
}
-static void nfsd_serv_free(struct percpu_ref *ref)
+static void nfsd_net_free(struct percpu_ref *ref)
{
- struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_serv_ref);
+ struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_net_ref);
- complete(&nn->nfsd_serv_free_done);
+ complete(&nn->nfsd_net_free_done);
}
/*
@@ -437,8 +437,8 @@ static void nfsd_shutdown_net(struct net *net)
if (!nn->nfsd_net_up)
return;
- percpu_ref_kill_and_confirm(&nn->nfsd_serv_ref, nfsd_serv_done);
- wait_for_completion(&nn->nfsd_serv_confirm_done);
+ percpu_ref_kill_and_confirm(&nn->nfsd_net_ref, nfsd_net_done);
+ wait_for_completion(&nn->nfsd_net_confirm_done);
nfsd_export_flush(net);
nfs4_state_shutdown_net(net);
@@ -449,8 +449,8 @@ static void nfsd_shutdown_net(struct net *net)
nn->lockd_up = false;
}
- wait_for_completion(&nn->nfsd_serv_free_done);
- percpu_ref_exit(&nn->nfsd_serv_ref);
+ wait_for_completion(&nn->nfsd_net_free_done);
+ percpu_ref_exit(&nn->nfsd_net_ref);
nn->nfsd_net_up = false;
nfsd_shutdown_generic();
@@ -654,12 +654,12 @@ int nfsd_create_serv(struct net *net)
if (nn->nfsd_serv)
return 0;
- error = percpu_ref_init(&nn->nfsd_serv_ref, nfsd_serv_free,
+ error = percpu_ref_init(&nn->nfsd_net_ref, nfsd_net_free,
0, GFP_KERNEL);
if (error)
return error;
- init_completion(&nn->nfsd_serv_free_done);
- init_completion(&nn->nfsd_serv_confirm_done);
+ init_completion(&nn->nfsd_net_free_done);
+ init_completion(&nn->nfsd_net_confirm_done);
if (nfsd_max_blksize == 0)
nfsd_max_blksize = nfsd_get_default_max_blksize();
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index 7cfc6720ed26d..aa2b5c6561ab0 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -52,8 +52,8 @@ nfsd_open_local_fh(struct net *, struct auth_domain *, struct rpc_clnt *,
void nfs_close_local_fh(struct nfs_file_localio *);
struct nfsd_localio_operations {
- bool (*nfsd_serv_try_get)(struct net *);
- void (*nfsd_serv_put)(struct net *);
+ bool (*nfsd_net_try_get)(struct net *);
+ void (*nfsd_net_put)(struct net *);
struct nfsd_file *(*nfsd_open_local_fh)(struct net *,
struct auth_domain *,
struct rpc_clnt *,
@@ -77,12 +77,12 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *,
static inline void nfs_to_nfsd_net_put(struct net *net)
{
/*
- * Once reference to nfsd_serv is dropped, NFSD could be
- * unloaded, so ensure safe return from nfsd_file_put_local()
- * by always taking RCU.
+ * Once reference to net (and associated nfsd_serv) is dropped, NFSD
+ * could be unloaded, so ensure safe return from nfsd_net_put() by
+ * always taking RCU.
*/
rcu_read_lock();
- nfs_to->nfsd_serv_put(net);
+ nfs_to->nfsd_net_put(net);
rcu_read_unlock();
}
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 09/15] nfsd: nfsd_file_acquire_local no longer returns GC'd nfsd_file
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (7 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 08/15] nfsd: rename nfsd_serv_ prefixed methods and variables with nfsd_net_ Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 10/15] nfs_common: rename nfslocalio nfs_uuid_lock to nfs_uuids_lock Mike Snitzer
` (6 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Now that LOCALIO no longer leans on NFSD's filecache for caching open
files (and instead uses NFS client-side open nfsd_file caching) there
is no need to use NFSD filecache's GC feature. Avoiding GC will speed
up nfsd_file initial opens.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfsd/filecache.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index fac98b2cb463a..ab9942e420543 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -1225,10 +1225,9 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
* a file. The security implications of this should be carefully
* considered before use.
*
- * The nfsd_file object returned by this API is reference-counted
- * and garbage-collected. The object is retained for a few
- * seconds after the final nfsd_file_put() in case the caller
- * wants to re-use it.
+ * The nfsd_file_object returned by this API is reference-counted
+ * but not garbage-collected. The object is unhashed after the
+ * final nfsd_file_put().
*
* Return values:
* %nfs_ok - @pnf points to an nfsd_file with its reference
@@ -1250,7 +1249,7 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
__be32 beres;
beres = nfsd_file_do_acquire(NULL, net, cred, client,
- fhp, may_flags, NULL, pnf, true);
+ fhp, may_flags, NULL, pnf, false);
revert_creds(save_cred);
return beres;
}
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 10/15] nfs_common: rename nfslocalio nfs_uuid_lock to nfs_uuids_lock
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (8 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 09/15] nfsd: nfsd_file_acquire_local no longer returns GC'd nfsd_file Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client Mike Snitzer
` (5 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
This global spinlock protects all nfs_uuid_t relative to the global
nfs_uuids list. A later commit will split this global spinlock so
prepare by renaming this lock to reflect its intended narrow scope.
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
---
fs/nfs_common/nfslocalio.c | 34 +++++++++++++++++-----------------
fs/nfsd/localio.c | 2 +-
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index e75cd21f4c8bc..5fa3f47b442e9 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -15,11 +15,11 @@
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("NFS localio protocol bypass support");
-static DEFINE_SPINLOCK(nfs_uuid_lock);
+static DEFINE_SPINLOCK(nfs_uuids_lock);
/*
* Global list of nfs_uuid_t instances
- * that is protected by nfs_uuid_lock.
+ * that is protected by nfs_uuids_lock.
*/
static LIST_HEAD(nfs_uuids);
@@ -34,15 +34,15 @@ EXPORT_SYMBOL_GPL(nfs_uuid_init);
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
/* Is this nfs_uuid already in use? */
if (!list_empty(&nfs_uuid->list)) {
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
return false;
}
uuid_gen(&nfs_uuid->uuid);
list_add_tail(&nfs_uuid->list, &nfs_uuids);
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
return true;
}
@@ -51,10 +51,10 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{
if (nfs_uuid->net == NULL) {
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
if (nfs_uuid->net == NULL)
list_del_init(&nfs_uuid->list);
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
}
}
EXPORT_SYMBOL_GPL(nfs_uuid_end);
@@ -78,7 +78,7 @@ void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
{
nfs_uuid_t *nfs_uuid;
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
nfs_uuid = nfs_uuid_lookup_locked(uuid);
if (nfs_uuid) {
kref_get(&dom->ref);
@@ -94,7 +94,7 @@ void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
__module_get(mod);
nfsd_mod = mod;
}
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
}
EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
@@ -128,9 +128,9 @@ void nfs_localio_disable_client(struct nfs_client *clp)
spin_lock(&nfs_uuid->lock);
if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
nfs_uuid_put_locked(nfs_uuid);
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
}
spin_unlock(&nfs_uuid->lock);
}
@@ -140,23 +140,23 @@ void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
{
nfs_uuid_t *nfs_uuid, *tmp;
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
list_for_each_entry_safe(nfs_uuid, tmp, cl_uuid_list, list) {
struct nfs_client *clp =
container_of(nfs_uuid, struct nfs_client, cl_uuid);
nfs_localio_disable_client(clp);
}
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
{
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
if (!nfl->nfs_uuid)
rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
}
/*
@@ -217,14 +217,14 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
ro_nf = rcu_access_pointer(nfl->ro_file);
rw_nf = rcu_access_pointer(nfl->rw_file);
if (ro_nf || rw_nf) {
- spin_lock(&nfs_uuid_lock);
+ spin_lock(&nfs_uuids_lock);
if (ro_nf)
ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
if (rw_nf)
rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
rcu_assign_pointer(nfl->nfs_uuid, NULL);
- spin_unlock(&nfs_uuid_lock);
+ spin_unlock(&nfs_uuids_lock);
rcu_read_unlock();
if (ro_nf)
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
index f9a91cd3b5ec7..2ae07161b9195 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -54,7 +54,7 @@ void nfsd_localio_ops_init(void)
* avoid all the NFS overhead with reads, writes and commits.
*
* On successful return, returned nfsd_file will have its nf_net member
- * set. Caller (NFS client) is responsible for calling nfsd_serv_put and
+ * set. Caller (NFS client) is responsible for calling nfsd_net_put and
* nfsd_file_put (via nfs_to_nfsd_file_put_local).
*/
struct nfsd_file *
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (9 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 10/15] nfs_common: rename nfslocalio nfs_uuid_lock to nfs_uuids_lock Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 18:53 ` Jeff Layton
2024-11-14 3:59 ` [for-6.13 PATCH v2 12/15] nfs_common: add nfs_localio trace events Mike Snitzer
` (4 subsequent siblings)
15 siblings, 1 reply; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
This tracking enables __nfsd_file_cache_purge() to call
nfs_localio_invalidate_clients(), upon shutdown or export change, to
nfs_close_local_fh() all open nfsd_files that are still cached by the
LOCALIO nfs clients associated with nfsd_net that is being shutdown.
Now that the client must track all open nfsd_files there was more work
than necessary being done with the global nfs_uuids_lock contended.
This manifested in various RCU issues, e.g.:
hrtimer: interrupt took 47969440 ns
rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
Use nfs_uuid->lock to protect all nfs_uuid_t members, instead of
nfs_uuids_lock, once nfs_uuid_is_local() adds the client to
nn->local_clients.
Also add 'local_clients_lock' to 'struct nfsd_net' to protect
nn->local_clients. And store a pointer to spinlock in the 'list_lock'
member of nfs_uuid_t so nfs_localio_disable_client() can use it to
avoid taking the global nfs_uuids_lock.
In combination, these split out locks eliminate the use of the single
nfslocalio.c global nfs_uuids_lock in the IO paths (open and close).
Also refactored associated fs/nfs_common/nfslocalio.c methods' locking
to reduce work performed with spinlocks held in general.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs_common/nfslocalio.c | 166 +++++++++++++++++++++++++++----------
fs/nfsd/filecache.c | 9 ++
fs/nfsd/localio.c | 1 +
fs/nfsd/netns.h | 1 +
fs/nfsd/nfsctl.c | 4 +-
include/linux/nfslocalio.h | 8 +-
6 files changed, 143 insertions(+), 46 deletions(-)
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 5fa3f47b442e9..e5c0912b9a025 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -23,27 +23,49 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
*/
static LIST_HEAD(nfs_uuids);
+/*
+ * Lock ordering:
+ * 1: nfs_uuid->lock
+ * 2: nfs_uuids_lock
+ * 3: nfs_uuid->list_lock (aka nn->local_clients_lock)
+ *
+ * May skip locks in select cases, but never hold multiple
+ * locks out of order.
+ */
+
void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
{
nfs_uuid->net = NULL;
nfs_uuid->dom = NULL;
+ nfs_uuid->list_lock = NULL;
INIT_LIST_HEAD(&nfs_uuid->list);
+ INIT_LIST_HEAD(&nfs_uuid->files);
spin_lock_init(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_uuid_init);
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
+ spin_lock(&nfs_uuid->lock);
+ if (nfs_uuid->net) {
+ /* This nfs_uuid is already in use */
+ spin_unlock(&nfs_uuid->lock);
+ return false;
+ }
+
spin_lock(&nfs_uuids_lock);
- /* Is this nfs_uuid already in use? */
if (!list_empty(&nfs_uuid->list)) {
+ /* This nfs_uuid is already in use */
spin_unlock(&nfs_uuids_lock);
+ spin_unlock(&nfs_uuid->lock);
return false;
}
- uuid_gen(&nfs_uuid->uuid);
list_add_tail(&nfs_uuid->list, &nfs_uuids);
spin_unlock(&nfs_uuids_lock);
+ uuid_gen(&nfs_uuid->uuid);
+ spin_unlock(&nfs_uuid->lock);
+
return true;
}
EXPORT_SYMBOL_GPL(nfs_uuid_begin);
@@ -51,11 +73,15 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{
if (nfs_uuid->net == NULL) {
- spin_lock(&nfs_uuids_lock);
- if (nfs_uuid->net == NULL)
+ spin_lock(&nfs_uuid->lock);
+ if (nfs_uuid->net == NULL) {
+ /* Not local, remove from nfs_uuids */
+ spin_lock(&nfs_uuids_lock);
list_del_init(&nfs_uuid->list);
- spin_unlock(&nfs_uuids_lock);
- }
+ spin_unlock(&nfs_uuids_lock);
+ }
+ spin_unlock(&nfs_uuid->lock);
+ }
}
EXPORT_SYMBOL_GPL(nfs_uuid_end);
@@ -73,28 +99,39 @@ static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
static struct module *nfsd_mod;
void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
- struct net *net, struct auth_domain *dom,
- struct module *mod)
+ spinlock_t *list_lock, struct net *net,
+ struct auth_domain *dom, struct module *mod)
{
nfs_uuid_t *nfs_uuid;
spin_lock(&nfs_uuids_lock);
nfs_uuid = nfs_uuid_lookup_locked(uuid);
- if (nfs_uuid) {
- kref_get(&dom->ref);
- nfs_uuid->dom = dom;
- /*
- * We don't hold a ref on the net, but instead put
- * ourselves on a list so the net pointer can be
- * invalidated.
- */
- list_move(&nfs_uuid->list, list);
- rcu_assign_pointer(nfs_uuid->net, net);
-
- __module_get(mod);
- nfsd_mod = mod;
+ if (!nfs_uuid) {
+ spin_unlock(&nfs_uuids_lock);
+ return;
}
+
+ /*
+ * We don't hold a ref on the net, but instead put
+ * ourselves on @list (nn->local_clients) so the net
+ * pointer can be invalidated.
+ */
+ spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
+ list_move(&nfs_uuid->list, list);
+ spin_unlock(list_lock);
+
spin_unlock(&nfs_uuids_lock);
+ /* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
+ spin_lock(&nfs_uuid->lock);
+
+ __module_get(mod);
+ nfsd_mod = mod;
+
+ nfs_uuid->list_lock = list_lock;
+ kref_get(&dom->ref);
+ nfs_uuid->dom = dom;
+ rcu_assign_pointer(nfs_uuid->net, net);
+ spin_unlock(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
@@ -108,55 +145,96 @@ void nfs_localio_enable_client(struct nfs_client *clp)
}
EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
-static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
+/*
+ * Cleanup the nfs_uuid_t embedded in an nfs_client.
+ * This is the long-form of nfs_uuid_init().
+ */
+static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
{
- if (!nfs_uuid->net)
+ LIST_HEAD(local_files);
+ struct nfs_file_localio *nfl, *tmp;
+
+ spin_lock(&nfs_uuid->lock);
+ if (unlikely(!nfs_uuid->net)) {
+ spin_unlock(&nfs_uuid->lock);
return;
- module_put(nfsd_mod);
+ }
RCU_INIT_POINTER(nfs_uuid->net, NULL);
if (nfs_uuid->dom) {
auth_domain_put(nfs_uuid->dom);
nfs_uuid->dom = NULL;
}
- list_del_init(&nfs_uuid->list);
+
+ list_splice_init(&nfs_uuid->files, &local_files);
+ spin_unlock(&nfs_uuid->lock);
+
+ /* Walk list of files and ensure their last references dropped */
+ list_for_each_entry_safe(nfl, tmp, &local_files, list) {
+ nfs_close_local_fh(nfl);
+ cond_resched();
+ }
+
+ spin_lock(&nfs_uuid->lock);
+ BUG_ON(!list_empty(&nfs_uuid->files));
+
+ /* Remove client from nn->local_clients */
+ if (nfs_uuid->list_lock) {
+ spin_lock(nfs_uuid->list_lock);
+ BUG_ON(list_empty(&nfs_uuid->list));
+ list_del_init(&nfs_uuid->list);
+ spin_unlock(nfs_uuid->list_lock);
+ nfs_uuid->list_lock = NULL;
+ }
+
+ module_put(nfsd_mod);
+ spin_unlock(&nfs_uuid->lock);
}
void nfs_localio_disable_client(struct nfs_client *clp)
{
- nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
+ nfs_uuid_t *nfs_uuid = NULL;
- spin_lock(&nfs_uuid->lock);
+ spin_lock(&clp->cl_uuid.lock); /* aka &nfs_uuid->lock */
if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
- spin_lock(&nfs_uuids_lock);
- nfs_uuid_put_locked(nfs_uuid);
- spin_unlock(&nfs_uuids_lock);
+ /* &clp->cl_uuid is always not NULL, using as bool here */
+ nfs_uuid = &clp->cl_uuid;
}
- spin_unlock(&nfs_uuid->lock);
+ spin_unlock(&clp->cl_uuid.lock);
+
+ if (nfs_uuid)
+ nfs_uuid_put(nfs_uuid);
}
EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
-void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
+void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
+ spinlock_t *nn_local_clients_lock)
{
+ LIST_HEAD(local_clients);
nfs_uuid_t *nfs_uuid, *tmp;
+ struct nfs_client *clp;
- spin_lock(&nfs_uuids_lock);
- list_for_each_entry_safe(nfs_uuid, tmp, cl_uuid_list, list) {
- struct nfs_client *clp =
- container_of(nfs_uuid, struct nfs_client, cl_uuid);
-
+ spin_lock(nn_local_clients_lock);
+ list_splice_init(nn_local_clients, &local_clients);
+ spin_unlock(nn_local_clients_lock);
+ list_for_each_entry_safe(nfs_uuid, tmp, &local_clients, list) {
+ if (WARN_ON(nfs_uuid->list_lock != nn_local_clients_lock))
+ break;
+ clp = container_of(nfs_uuid, struct nfs_client, cl_uuid);
nfs_localio_disable_client(clp);
}
- spin_unlock(&nfs_uuids_lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
{
- spin_lock(&nfs_uuids_lock);
- if (!nfl->nfs_uuid)
+ /* Add nfl to nfs_uuid->files if it isn't already */
+ spin_lock(&nfs_uuid->lock);
+ if (list_empty(&nfl->list)) {
rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
- spin_unlock(&nfs_uuids_lock);
+ list_add_tail(&nfl->list, &nfs_uuid->files);
+ }
+ spin_unlock(&nfs_uuid->lock);
}
/*
@@ -217,14 +295,16 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
ro_nf = rcu_access_pointer(nfl->ro_file);
rw_nf = rcu_access_pointer(nfl->rw_file);
if (ro_nf || rw_nf) {
- spin_lock(&nfs_uuids_lock);
+ spin_lock(&nfs_uuid->lock);
if (ro_nf)
ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
if (rw_nf)
rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
+ /* Remove nfl from nfs_uuid->files list */
rcu_assign_pointer(nfl->nfs_uuid, NULL);
- spin_unlock(&nfs_uuids_lock);
+ list_del_init(&nfl->list);
+ spin_unlock(&nfs_uuid->lock);
rcu_read_unlock();
if (ro_nf)
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index ab9942e420543..c9ab64e3732ce 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -39,6 +39,7 @@
#include <linux/fsnotify.h>
#include <linux/seq_file.h>
#include <linux/rhashtable.h>
+#include <linux/nfslocalio.h>
#include "vfs.h"
#include "nfsd.h"
@@ -836,6 +837,14 @@ __nfsd_file_cache_purge(struct net *net)
struct nfsd_file *nf;
LIST_HEAD(dispose);
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ if (net) {
+ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ nfs_localio_invalidate_clients(&nn->local_clients,
+ &nn->local_clients_lock);
+ }
+#endif
+
rhltable_walk_enter(&nfsd_file_rhltable, &iter);
do {
rhashtable_walk_start(&iter);
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
index 2ae07161b9195..238647fa379e3 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -116,6 +116,7 @@ static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
+ &nn->local_clients_lock,
net, rqstp->rq_client, THIS_MODULE);
return rpc_success;
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 8faef59d71229..187c4140b1913 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -220,6 +220,7 @@ struct nfsd_net {
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
/* Local clients to be invalidated when net is shut down */
+ spinlock_t local_clients_lock;
struct list_head local_clients;
#endif
};
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 727904d8a4d01..70347b0ecdc43 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2259,6 +2259,7 @@ static __net_init int nfsd_net_init(struct net *net)
seqlock_init(&nn->writeverf_lock);
nfsd_proc_stat_init(net);
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ spin_lock_init(&nn->local_clients_lock);
INIT_LIST_HEAD(&nn->local_clients);
#endif
return 0;
@@ -2283,7 +2284,8 @@ static __net_exit void nfsd_net_pre_exit(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- nfs_localio_invalidate_clients(&nn->local_clients);
+ nfs_localio_invalidate_clients(&nn->local_clients,
+ &nn->local_clients_lock);
}
#endif
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index aa2b5c6561ab0..c68a529230c14 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -30,19 +30,23 @@ typedef struct {
/* sadly this struct is just over a cacheline, avoid bouncing */
spinlock_t ____cacheline_aligned lock;
struct list_head list;
+ spinlock_t *list_lock; /* nn->local_clients_lock */
struct net __rcu *net; /* nfsd's network namespace */
struct auth_domain *dom; /* auth_domain for localio */
+ /* Local files to close when net is shut down or exports change */
+ struct list_head files;
} nfs_uuid_t;
void nfs_uuid_init(nfs_uuid_t *);
bool nfs_uuid_begin(nfs_uuid_t *);
void nfs_uuid_end(nfs_uuid_t *);
-void nfs_uuid_is_local(const uuid_t *, struct list_head *,
+void nfs_uuid_is_local(const uuid_t *, struct list_head *, spinlock_t *,
struct net *, struct auth_domain *, struct module *);
void nfs_localio_enable_client(struct nfs_client *clp);
void nfs_localio_disable_client(struct nfs_client *clp);
-void nfs_localio_invalidate_clients(struct list_head *list);
+void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
+ spinlock_t *nn_local_clients_lock);
/* localio needs to map filehandle -> struct nfsd_file */
extern struct nfsd_file *
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client
2024-11-14 3:59 ` [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client Mike Snitzer
@ 2024-11-14 18:53 ` Jeff Layton
2024-11-14 23:22 ` Mike Snitzer
0 siblings, 1 reply; 23+ messages in thread
From: Jeff Layton @ 2024-11-14 18:53 UTC (permalink / raw)
To: Mike Snitzer, linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, NeilBrown
On Wed, 2024-11-13 at 22:59 -0500, Mike Snitzer wrote:
> This tracking enables __nfsd_file_cache_purge() to call
> nfs_localio_invalidate_clients(), upon shutdown or export change, to
> nfs_close_local_fh() all open nfsd_files that are still cached by the
> LOCALIO nfs clients associated with nfsd_net that is being shutdown.
>
> Now that the client must track all open nfsd_files there was more work
> than necessary being done with the global nfs_uuids_lock contended.
> This manifested in various RCU issues, e.g.:
> hrtimer: interrupt took 47969440 ns
> rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
>
> Use nfs_uuid->lock to protect all nfs_uuid_t members, instead of
> nfs_uuids_lock, once nfs_uuid_is_local() adds the client to
> nn->local_clients.
>
> Also add 'local_clients_lock' to 'struct nfsd_net' to protect
> nn->local_clients. And store a pointer to spinlock in the 'list_lock'
> member of nfs_uuid_t so nfs_localio_disable_client() can use it to
> avoid taking the global nfs_uuids_lock.
>
> In combination, these split out locks eliminate the use of the single
> nfslocalio.c global nfs_uuids_lock in the IO paths (open and close).
>
> Also refactored associated fs/nfs_common/nfslocalio.c methods' locking
> to reduce work performed with spinlocks held in general.
>
> Signed-off-by: Mike Snitzer <snitzer@kernel.org>
> ---
> fs/nfs_common/nfslocalio.c | 166 +++++++++++++++++++++++++++----------
> fs/nfsd/filecache.c | 9 ++
> fs/nfsd/localio.c | 1 +
> fs/nfsd/netns.h | 1 +
> fs/nfsd/nfsctl.c | 4 +-
> include/linux/nfslocalio.h | 8 +-
> 6 files changed, 143 insertions(+), 46 deletions(-)
>
> diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
> index 5fa3f47b442e9..e5c0912b9a025 100644
> --- a/fs/nfs_common/nfslocalio.c
> +++ b/fs/nfs_common/nfslocalio.c
> @@ -23,27 +23,49 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
> */
> static LIST_HEAD(nfs_uuids);
>
> +/*
> + * Lock ordering:
> + * 1: nfs_uuid->lock
> + * 2: nfs_uuids_lock
> + * 3: nfs_uuid->list_lock (aka nn->local_clients_lock)
> + *
> + * May skip locks in select cases, but never hold multiple
> + * locks out of order.
> + */
> +
> void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
> {
> nfs_uuid->net = NULL;
> nfs_uuid->dom = NULL;
> + nfs_uuid->list_lock = NULL;
> INIT_LIST_HEAD(&nfs_uuid->list);
> + INIT_LIST_HEAD(&nfs_uuid->files);
> spin_lock_init(&nfs_uuid->lock);
> }
> EXPORT_SYMBOL_GPL(nfs_uuid_init);
>
> bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
> {
> + spin_lock(&nfs_uuid->lock);
> + if (nfs_uuid->net) {
> + /* This nfs_uuid is already in use */
> + spin_unlock(&nfs_uuid->lock);
> + return false;
> + }
> +
> spin_lock(&nfs_uuids_lock);
> - /* Is this nfs_uuid already in use? */
> if (!list_empty(&nfs_uuid->list)) {
> + /* This nfs_uuid is already in use */
> spin_unlock(&nfs_uuids_lock);
> + spin_unlock(&nfs_uuid->lock);
> return false;
> }
> - uuid_gen(&nfs_uuid->uuid);
> list_add_tail(&nfs_uuid->list, &nfs_uuids);
> spin_unlock(&nfs_uuids_lock);
>
> + uuid_gen(&nfs_uuid->uuid);
> + spin_unlock(&nfs_uuid->lock);
> +
> return true;
> }
> EXPORT_SYMBOL_GPL(nfs_uuid_begin);
> @@ -51,11 +73,15 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
> void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
> {
> if (nfs_uuid->net == NULL) {
> - spin_lock(&nfs_uuids_lock);
> - if (nfs_uuid->net == NULL)
> + spin_lock(&nfs_uuid->lock);
> + if (nfs_uuid->net == NULL) {
> + /* Not local, remove from nfs_uuids */
> + spin_lock(&nfs_uuids_lock);
> list_del_init(&nfs_uuid->list);
> - spin_unlock(&nfs_uuids_lock);
> - }
> + spin_unlock(&nfs_uuids_lock);
> + }
> + spin_unlock(&nfs_uuid->lock);
> + }
> }
> EXPORT_SYMBOL_GPL(nfs_uuid_end);
>
> @@ -73,28 +99,39 @@ static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
> static struct module *nfsd_mod;
>
> void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
> - struct net *net, struct auth_domain *dom,
> - struct module *mod)
> + spinlock_t *list_lock, struct net *net,
> + struct auth_domain *dom, struct module *mod)
> {
> nfs_uuid_t *nfs_uuid;
>
> spin_lock(&nfs_uuids_lock);
> nfs_uuid = nfs_uuid_lookup_locked(uuid);
> - if (nfs_uuid) {
> - kref_get(&dom->ref);
> - nfs_uuid->dom = dom;
> - /*
> - * We don't hold a ref on the net, but instead put
> - * ourselves on a list so the net pointer can be
> - * invalidated.
> - */
> - list_move(&nfs_uuid->list, list);
> - rcu_assign_pointer(nfs_uuid->net, net);
> -
> - __module_get(mod);
> - nfsd_mod = mod;
> + if (!nfs_uuid) {
> + spin_unlock(&nfs_uuids_lock);
> + return;
> }
> +
> + /*
> + * We don't hold a ref on the net, but instead put
> + * ourselves on @list (nn->local_clients) so the net
> + * pointer can be invalidated.
> + */
> + spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
> + list_move(&nfs_uuid->list, list);
> + spin_unlock(list_lock);
> +
> spin_unlock(&nfs_uuids_lock);
> + /* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
> + spin_lock(&nfs_uuid->lock);
> +
> + __module_get(mod);
> + nfsd_mod = mod;
> +
> + nfs_uuid->list_lock = list_lock;
> + kref_get(&dom->ref);
> + nfs_uuid->dom = dom;
> + rcu_assign_pointer(nfs_uuid->net, net);
> + spin_unlock(&nfs_uuid->lock);
> }
> EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
>
> @@ -108,55 +145,96 @@ void nfs_localio_enable_client(struct nfs_client *clp)
> }
> EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
>
> -static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
> +/*
> + * Cleanup the nfs_uuid_t embedded in an nfs_client.
> + * This is the long-form of nfs_uuid_init().
> + */
> +static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
> {
> - if (!nfs_uuid->net)
> + LIST_HEAD(local_files);
> + struct nfs_file_localio *nfl, *tmp;
> +
> + spin_lock(&nfs_uuid->lock);
> + if (unlikely(!nfs_uuid->net)) {
> + spin_unlock(&nfs_uuid->lock);
> return;
> - module_put(nfsd_mod);
> + }
> RCU_INIT_POINTER(nfs_uuid->net, NULL);
>
> if (nfs_uuid->dom) {
> auth_domain_put(nfs_uuid->dom);
> nfs_uuid->dom = NULL;
> }
> - list_del_init(&nfs_uuid->list);
> +
> + list_splice_init(&nfs_uuid->files, &local_files);
> + spin_unlock(&nfs_uuid->lock);
> +
> + /* Walk list of files and ensure their last references dropped */
> + list_for_each_entry_safe(nfl, tmp, &local_files, list) {
> + nfs_close_local_fh(nfl);
> + cond_resched();
> + }
> +
> + spin_lock(&nfs_uuid->lock);
> + BUG_ON(!list_empty(&nfs_uuid->files));
> +
> + /* Remove client from nn->local_clients */
> + if (nfs_uuid->list_lock) {
> + spin_lock(nfs_uuid->list_lock);
> + BUG_ON(list_empty(&nfs_uuid->list));
> + list_del_init(&nfs_uuid->list);
> + spin_unlock(nfs_uuid->list_lock);
> + nfs_uuid->list_lock = NULL;
> + }
> +
> + module_put(nfsd_mod);
> + spin_unlock(&nfs_uuid->lock);
> }
>
> void nfs_localio_disable_client(struct nfs_client *clp)
> {
> - nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
> + nfs_uuid_t *nfs_uuid = NULL;
>
> - spin_lock(&nfs_uuid->lock);
> + spin_lock(&clp->cl_uuid.lock); /* aka &nfs_uuid->lock */
> if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
> - spin_lock(&nfs_uuids_lock);
> - nfs_uuid_put_locked(nfs_uuid);
> - spin_unlock(&nfs_uuids_lock);
> + /* &clp->cl_uuid is always not NULL, using as bool here */
> + nfs_uuid = &clp->cl_uuid;
> }
> - spin_unlock(&nfs_uuid->lock);
> + spin_unlock(&clp->cl_uuid.lock);
> +
> + if (nfs_uuid)
> + nfs_uuid_put(nfs_uuid);
> }
> EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
>
> -void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
> +void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
> + spinlock_t *nn_local_clients_lock)
> {
> + LIST_HEAD(local_clients);
> nfs_uuid_t *nfs_uuid, *tmp;
> + struct nfs_client *clp;
>
> - spin_lock(&nfs_uuids_lock);
> - list_for_each_entry_safe(nfs_uuid, tmp, cl_uuid_list, list) {
> - struct nfs_client *clp =
> - container_of(nfs_uuid, struct nfs_client, cl_uuid);
> -
> + spin_lock(nn_local_clients_lock);
> + list_splice_init(nn_local_clients, &local_clients);
> + spin_unlock(nn_local_clients_lock);
> + list_for_each_entry_safe(nfs_uuid, tmp, &local_clients, list) {
> + if (WARN_ON(nfs_uuid->list_lock != nn_local_clients_lock))
> + break;
> + clp = container_of(nfs_uuid, struct nfs_client, cl_uuid);
> nfs_localio_disable_client(clp);
> }
> - spin_unlock(&nfs_uuids_lock);
> }
> EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
>
> static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
> {
> - spin_lock(&nfs_uuids_lock);
> - if (!nfl->nfs_uuid)
> + /* Add nfl to nfs_uuid->files if it isn't already */
> + spin_lock(&nfs_uuid->lock);
> + if (list_empty(&nfl->list)) {
> rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
> - spin_unlock(&nfs_uuids_lock);
> + list_add_tail(&nfl->list, &nfs_uuid->files);
> + }
> + spin_unlock(&nfs_uuid->lock);
> }
>
> /*
> @@ -217,14 +295,16 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
> ro_nf = rcu_access_pointer(nfl->ro_file);
> rw_nf = rcu_access_pointer(nfl->rw_file);
> if (ro_nf || rw_nf) {
> - spin_lock(&nfs_uuids_lock);
> + spin_lock(&nfs_uuid->lock);
> if (ro_nf)
> ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
> if (rw_nf)
> rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
>
> + /* Remove nfl from nfs_uuid->files list */
> rcu_assign_pointer(nfl->nfs_uuid, NULL);
> - spin_unlock(&nfs_uuids_lock);
> + list_del_init(&nfl->list);
> + spin_unlock(&nfs_uuid->lock);
> rcu_read_unlock();
>
> if (ro_nf)
> diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
> index ab9942e420543..c9ab64e3732ce 100644
> --- a/fs/nfsd/filecache.c
> +++ b/fs/nfsd/filecache.c
> @@ -39,6 +39,7 @@
> #include <linux/fsnotify.h>
> #include <linux/seq_file.h>
> #include <linux/rhashtable.h>
> +#include <linux/nfslocalio.h>
>
> #include "vfs.h"
> #include "nfsd.h"
> @@ -836,6 +837,14 @@ __nfsd_file_cache_purge(struct net *net)
> struct nfsd_file *nf;
> LIST_HEAD(dispose);
>
> +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> + if (net) {
> + struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> + nfs_localio_invalidate_clients(&nn->local_clients,
> + &nn->local_clients_lock);
> + }
> +#endif
> +
If !net in this function, then nfsd is tearing down all the nfsd_files
in the cache. You probably need to issue the above on every net
namespace that is running nfsd in that case.
> rhltable_walk_enter(&nfsd_file_rhltable, &iter);
> do {
> rhashtable_walk_start(&iter);
> diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
> index 2ae07161b9195..238647fa379e3 100644
> --- a/fs/nfsd/localio.c
> +++ b/fs/nfsd/localio.c
> @@ -116,6 +116,7 @@ static __be32 localio_proc_uuid_is_local(struct svc_rqst *rqstp)
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
>
> nfs_uuid_is_local(&argp->uuid, &nn->local_clients,
> + &nn->local_clients_lock,
> net, rqstp->rq_client, THIS_MODULE);
>
> return rpc_success;
> diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
> index 8faef59d71229..187c4140b1913 100644
> --- a/fs/nfsd/netns.h
> +++ b/fs/nfsd/netns.h
> @@ -220,6 +220,7 @@ struct nfsd_net {
>
> #if IS_ENABLED(CONFIG_NFS_LOCALIO)
> /* Local clients to be invalidated when net is shut down */
> + spinlock_t local_clients_lock;
> struct list_head local_clients;
> #endif
> };
> diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
> index 727904d8a4d01..70347b0ecdc43 100644
> --- a/fs/nfsd/nfsctl.c
> +++ b/fs/nfsd/nfsctl.c
> @@ -2259,6 +2259,7 @@ static __net_init int nfsd_net_init(struct net *net)
> seqlock_init(&nn->writeverf_lock);
> nfsd_proc_stat_init(net);
> #if IS_ENABLED(CONFIG_NFS_LOCALIO)
> + spin_lock_init(&nn->local_clients_lock);
> INIT_LIST_HEAD(&nn->local_clients);
> #endif
> return 0;
> @@ -2283,7 +2284,8 @@ static __net_exit void nfsd_net_pre_exit(struct net *net)
> {
> struct nfsd_net *nn = net_generic(net, nfsd_net_id);
>
> - nfs_localio_invalidate_clients(&nn->local_clients);
> + nfs_localio_invalidate_clients(&nn->local_clients,
> + &nn->local_clients_lock);
> }
> #endif
>
> diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
> index aa2b5c6561ab0..c68a529230c14 100644
> --- a/include/linux/nfslocalio.h
> +++ b/include/linux/nfslocalio.h
> @@ -30,19 +30,23 @@ typedef struct {
> /* sadly this struct is just over a cacheline, avoid bouncing */
> spinlock_t ____cacheline_aligned lock;
> struct list_head list;
> + spinlock_t *list_lock; /* nn->local_clients_lock */
> struct net __rcu *net; /* nfsd's network namespace */
> struct auth_domain *dom; /* auth_domain for localio */
> + /* Local files to close when net is shut down or exports change */
> + struct list_head files;
> } nfs_uuid_t;
>
> void nfs_uuid_init(nfs_uuid_t *);
> bool nfs_uuid_begin(nfs_uuid_t *);
> void nfs_uuid_end(nfs_uuid_t *);
> -void nfs_uuid_is_local(const uuid_t *, struct list_head *,
> +void nfs_uuid_is_local(const uuid_t *, struct list_head *, spinlock_t *,
> struct net *, struct auth_domain *, struct module *);
>
> void nfs_localio_enable_client(struct nfs_client *clp);
> void nfs_localio_disable_client(struct nfs_client *clp);
> -void nfs_localio_invalidate_clients(struct list_head *list);
> +void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
> + spinlock_t *nn_local_clients_lock);
>
> /* localio needs to map filehandle -> struct nfsd_file */
> extern struct nfsd_file *
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client
2024-11-14 18:53 ` Jeff Layton
@ 2024-11-14 23:22 ` Mike Snitzer
0 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 23:22 UTC (permalink / raw)
To: Jeff Layton
Cc: linux-nfs, Anna Schumaker, Trond Myklebust, Chuck Lever,
NeilBrown
On Thu, Nov 14, 2024 at 01:53:48PM -0500, Jeff Layton wrote:
> On Wed, 2024-11-13 at 22:59 -0500, Mike Snitzer wrote:
> > This tracking enables __nfsd_file_cache_purge() to call
> > nfs_localio_invalidate_clients(), upon shutdown or export change, to
> > nfs_close_local_fh() all open nfsd_files that are still cached by the
> > LOCALIO nfs clients associated with nfsd_net that is being shutdown.
> >
> > Now that the client must track all open nfsd_files there was more work
> > than necessary being done with the global nfs_uuids_lock contended.
> > This manifested in various RCU issues, e.g.:
> > hrtimer: interrupt took 47969440 ns
> > rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
> >
> > Use nfs_uuid->lock to protect all nfs_uuid_t members, instead of
> > nfs_uuids_lock, once nfs_uuid_is_local() adds the client to
> > nn->local_clients.
> >
> > Also add 'local_clients_lock' to 'struct nfsd_net' to protect
> > nn->local_clients. And store a pointer to spinlock in the 'list_lock'
> > member of nfs_uuid_t so nfs_localio_disable_client() can use it to
> > avoid taking the global nfs_uuids_lock.
> >
> > In combination, these split out locks eliminate the use of the single
> > nfslocalio.c global nfs_uuids_lock in the IO paths (open and close).
> >
> > Also refactored associated fs/nfs_common/nfslocalio.c methods' locking
> > to reduce work performed with spinlocks held in general.
> >
> > Signed-off-by: Mike Snitzer <snitzer@kernel.org>
> > ---
> > fs/nfs_common/nfslocalio.c | 166 +++++++++++++++++++++++++++----------
> > fs/nfsd/filecache.c | 9 ++
> > fs/nfsd/localio.c | 1 +
> > fs/nfsd/netns.h | 1 +
> > fs/nfsd/nfsctl.c | 4 +-
> > include/linux/nfslocalio.h | 8 +-
> > 6 files changed, 143 insertions(+), 46 deletions(-)
> >
> > diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
> > index 5fa3f47b442e9..e5c0912b9a025 100644
> > --- a/fs/nfs_common/nfslocalio.c
> > +++ b/fs/nfs_common/nfslocalio.c
> > @@ -23,27 +23,49 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
> > */
> > static LIST_HEAD(nfs_uuids);
> >
> > +/*
> > + * Lock ordering:
> > + * 1: nfs_uuid->lock
> > + * 2: nfs_uuids_lock
> > + * 3: nfs_uuid->list_lock (aka nn->local_clients_lock)
> > + *
> > + * May skip locks in select cases, but never hold multiple
> > + * locks out of order.
> > + */
> > +
> > void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
> > {
> > nfs_uuid->net = NULL;
> > nfs_uuid->dom = NULL;
> > + nfs_uuid->list_lock = NULL;
> > INIT_LIST_HEAD(&nfs_uuid->list);
> > + INIT_LIST_HEAD(&nfs_uuid->files);
> > spin_lock_init(&nfs_uuid->lock);
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_init);
> >
> > bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
> > {
> > + spin_lock(&nfs_uuid->lock);
> > + if (nfs_uuid->net) {
> > + /* This nfs_uuid is already in use */
> > + spin_unlock(&nfs_uuid->lock);
> > + return false;
> > + }
> > +
> > spin_lock(&nfs_uuids_lock);
> > - /* Is this nfs_uuid already in use? */
> > if (!list_empty(&nfs_uuid->list)) {
> > + /* This nfs_uuid is already in use */
> > spin_unlock(&nfs_uuids_lock);
> > + spin_unlock(&nfs_uuid->lock);
> > return false;
> > }
> > - uuid_gen(&nfs_uuid->uuid);
> > list_add_tail(&nfs_uuid->list, &nfs_uuids);
> > spin_unlock(&nfs_uuids_lock);
> >
> > + uuid_gen(&nfs_uuid->uuid);
> > + spin_unlock(&nfs_uuid->lock);
> > +
> > return true;
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_begin);
> > @@ -51,11 +73,15 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
> > void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
> > {
> > if (nfs_uuid->net == NULL) {
> > - spin_lock(&nfs_uuids_lock);
> > - if (nfs_uuid->net == NULL)
> > + spin_lock(&nfs_uuid->lock);
> > + if (nfs_uuid->net == NULL) {
> > + /* Not local, remove from nfs_uuids */
> > + spin_lock(&nfs_uuids_lock);
> > list_del_init(&nfs_uuid->list);
> > - spin_unlock(&nfs_uuids_lock);
> > - }
> > + spin_unlock(&nfs_uuids_lock);
> > + }
> > + spin_unlock(&nfs_uuid->lock);
> > + }
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_end);
> >
> > @@ -73,28 +99,39 @@ static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
> > static struct module *nfsd_mod;
> >
> > void nfs_uuid_is_local(const uuid_t *uuid, struct list_head *list,
> > - struct net *net, struct auth_domain *dom,
> > - struct module *mod)
> > + spinlock_t *list_lock, struct net *net,
> > + struct auth_domain *dom, struct module *mod)
> > {
> > nfs_uuid_t *nfs_uuid;
> >
> > spin_lock(&nfs_uuids_lock);
> > nfs_uuid = nfs_uuid_lookup_locked(uuid);
> > - if (nfs_uuid) {
> > - kref_get(&dom->ref);
> > - nfs_uuid->dom = dom;
> > - /*
> > - * We don't hold a ref on the net, but instead put
> > - * ourselves on a list so the net pointer can be
> > - * invalidated.
> > - */
> > - list_move(&nfs_uuid->list, list);
> > - rcu_assign_pointer(nfs_uuid->net, net);
> > -
> > - __module_get(mod);
> > - nfsd_mod = mod;
> > + if (!nfs_uuid) {
> > + spin_unlock(&nfs_uuids_lock);
> > + return;
> > }
> > +
> > + /*
> > + * We don't hold a ref on the net, but instead put
> > + * ourselves on @list (nn->local_clients) so the net
> > + * pointer can be invalidated.
> > + */
> > + spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
> > + list_move(&nfs_uuid->list, list);
> > + spin_unlock(list_lock);
> > +
> > spin_unlock(&nfs_uuids_lock);
> > + /* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
> > + spin_lock(&nfs_uuid->lock);
> > +
> > + __module_get(mod);
> > + nfsd_mod = mod;
> > +
> > + nfs_uuid->list_lock = list_lock;
> > + kref_get(&dom->ref);
> > + nfs_uuid->dom = dom;
> > + rcu_assign_pointer(nfs_uuid->net, net);
> > + spin_unlock(&nfs_uuid->lock);
> > }
> > EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
> >
> > @@ -108,55 +145,96 @@ void nfs_localio_enable_client(struct nfs_client *clp)
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
> >
> > -static void nfs_uuid_put_locked(nfs_uuid_t *nfs_uuid)
> > +/*
> > + * Cleanup the nfs_uuid_t embedded in an nfs_client.
> > + * This is the long-form of nfs_uuid_init().
> > + */
> > +static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
> > {
> > - if (!nfs_uuid->net)
> > + LIST_HEAD(local_files);
> > + struct nfs_file_localio *nfl, *tmp;
> > +
> > + spin_lock(&nfs_uuid->lock);
> > + if (unlikely(!nfs_uuid->net)) {
> > + spin_unlock(&nfs_uuid->lock);
> > return;
> > - module_put(nfsd_mod);
> > + }
> > RCU_INIT_POINTER(nfs_uuid->net, NULL);
> >
> > if (nfs_uuid->dom) {
> > auth_domain_put(nfs_uuid->dom);
> > nfs_uuid->dom = NULL;
> > }
> > - list_del_init(&nfs_uuid->list);
> > +
> > + list_splice_init(&nfs_uuid->files, &local_files);
> > + spin_unlock(&nfs_uuid->lock);
> > +
> > + /* Walk list of files and ensure their last references dropped */
> > + list_for_each_entry_safe(nfl, tmp, &local_files, list) {
> > + nfs_close_local_fh(nfl);
> > + cond_resched();
> > + }
> > +
> > + spin_lock(&nfs_uuid->lock);
> > + BUG_ON(!list_empty(&nfs_uuid->files));
> > +
> > + /* Remove client from nn->local_clients */
> > + if (nfs_uuid->list_lock) {
> > + spin_lock(nfs_uuid->list_lock);
> > + BUG_ON(list_empty(&nfs_uuid->list));
> > + list_del_init(&nfs_uuid->list);
> > + spin_unlock(nfs_uuid->list_lock);
> > + nfs_uuid->list_lock = NULL;
> > + }
> > +
> > + module_put(nfsd_mod);
> > + spin_unlock(&nfs_uuid->lock);
> > }
> >
> > void nfs_localio_disable_client(struct nfs_client *clp)
> > {
> > - nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
> > + nfs_uuid_t *nfs_uuid = NULL;
> >
> > - spin_lock(&nfs_uuid->lock);
> > + spin_lock(&clp->cl_uuid.lock); /* aka &nfs_uuid->lock */
> > if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
> > - spin_lock(&nfs_uuids_lock);
> > - nfs_uuid_put_locked(nfs_uuid);
> > - spin_unlock(&nfs_uuids_lock);
> > + /* &clp->cl_uuid is always not NULL, using as bool here */
> > + nfs_uuid = &clp->cl_uuid;
> > }
> > - spin_unlock(&nfs_uuid->lock);
> > + spin_unlock(&clp->cl_uuid.lock);
> > +
> > + if (nfs_uuid)
> > + nfs_uuid_put(nfs_uuid);
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
> >
> > -void nfs_localio_invalidate_clients(struct list_head *cl_uuid_list)
> > +void nfs_localio_invalidate_clients(struct list_head *nn_local_clients,
> > + spinlock_t *nn_local_clients_lock)
> > {
> > + LIST_HEAD(local_clients);
> > nfs_uuid_t *nfs_uuid, *tmp;
> > + struct nfs_client *clp;
> >
> > - spin_lock(&nfs_uuids_lock);
> > - list_for_each_entry_safe(nfs_uuid, tmp, cl_uuid_list, list) {
> > - struct nfs_client *clp =
> > - container_of(nfs_uuid, struct nfs_client, cl_uuid);
> > -
> > + spin_lock(nn_local_clients_lock);
> > + list_splice_init(nn_local_clients, &local_clients);
> > + spin_unlock(nn_local_clients_lock);
> > + list_for_each_entry_safe(nfs_uuid, tmp, &local_clients, list) {
> > + if (WARN_ON(nfs_uuid->list_lock != nn_local_clients_lock))
> > + break;
> > + clp = container_of(nfs_uuid, struct nfs_client, cl_uuid);
> > nfs_localio_disable_client(clp);
> > }
> > - spin_unlock(&nfs_uuids_lock);
> > }
> > EXPORT_SYMBOL_GPL(nfs_localio_invalidate_clients);
> >
> > static void nfs_uuid_add_file(nfs_uuid_t *nfs_uuid, struct nfs_file_localio *nfl)
> > {
> > - spin_lock(&nfs_uuids_lock);
> > - if (!nfl->nfs_uuid)
> > + /* Add nfl to nfs_uuid->files if it isn't already */
> > + spin_lock(&nfs_uuid->lock);
> > + if (list_empty(&nfl->list)) {
> > rcu_assign_pointer(nfl->nfs_uuid, nfs_uuid);
> > - spin_unlock(&nfs_uuids_lock);
> > + list_add_tail(&nfl->list, &nfs_uuid->files);
> > + }
> > + spin_unlock(&nfs_uuid->lock);
> > }
> >
> > /*
> > @@ -217,14 +295,16 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
> > ro_nf = rcu_access_pointer(nfl->ro_file);
> > rw_nf = rcu_access_pointer(nfl->rw_file);
> > if (ro_nf || rw_nf) {
> > - spin_lock(&nfs_uuids_lock);
> > + spin_lock(&nfs_uuid->lock);
> > if (ro_nf)
> > ro_nf = rcu_dereference_protected(xchg(&nfl->ro_file, NULL), 1);
> > if (rw_nf)
> > rw_nf = rcu_dereference_protected(xchg(&nfl->rw_file, NULL), 1);
> >
> > + /* Remove nfl from nfs_uuid->files list */
> > rcu_assign_pointer(nfl->nfs_uuid, NULL);
> > - spin_unlock(&nfs_uuids_lock);
> > + list_del_init(&nfl->list);
> > + spin_unlock(&nfs_uuid->lock);
> > rcu_read_unlock();
> >
> > if (ro_nf)
> > diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
> > index ab9942e420543..c9ab64e3732ce 100644
> > --- a/fs/nfsd/filecache.c
> > +++ b/fs/nfsd/filecache.c
> > @@ -39,6 +39,7 @@
> > #include <linux/fsnotify.h>
> > #include <linux/seq_file.h>
> > #include <linux/rhashtable.h>
> > +#include <linux/nfslocalio.h>
> >
> > #include "vfs.h"
> > #include "nfsd.h"
> > @@ -836,6 +837,14 @@ __nfsd_file_cache_purge(struct net *net)
> > struct nfsd_file *nf;
> > LIST_HEAD(dispose);
> >
> > +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> > + if (net) {
> > + struct nfsd_net *nn = net_generic(net, nfsd_net_id);
> > + nfs_localio_invalidate_clients(&nn->local_clients,
> > + &nn->local_clients_lock);
> > + }
> > +#endif
> > +
>
> If !net in this function, then nfsd is tearing down all the nfsd_files
> in the cache. You probably need to issue the above on every net
> namespace that is running nfsd in that case.
Right, I looked closely at this but really should have updated the
patch header to make it have more permanence.. IIRC there are 2
passes: Once per net and then the final shutdown that passes NULL.
1)
nfsd_destroy_serv(net) -> nfsd_shutdown_net ->
nfsd_file_cache_shutdown_net -> nfsd_file_cache_purge ->
__nfsd_file_cache_purge(net)
2)
nfsd_file_cache_shutdown -> __nfsd_file_cache_purge(NULL)
So I concluded no need to be concerned with the !net case needing to
handle all nets...
Mike
^ permalink raw reply [flat|nested] 23+ messages in thread
* [for-6.13 PATCH v2 12/15] nfs_common: add nfs_localio trace events
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (10 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 11/15] nfs_common: track all open nfsd_files per LOCALIO nfs_client Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 13/15] nfs/localio: remove redundant code and simplify LOCALIO enablement Mike Snitzer
` (3 subsequent siblings)
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
The nfs_localio.ko now exposes /sys/kernel/tracing/events/nfs_localio
with nfs_localio_enable_client and nfs_localio_disable_client events.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs_common/Makefile | 3 +-
fs/nfs_common/localio_trace.c | 10 +++++++
fs/nfs_common/localio_trace.h | 56 +++++++++++++++++++++++++++++++++++
fs/nfs_common/nfslocalio.c | 4 +++
4 files changed, 72 insertions(+), 1 deletion(-)
create mode 100644 fs/nfs_common/localio_trace.c
create mode 100644 fs/nfs_common/localio_trace.h
diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
index a5e54809701e3..c10ead273ff2c 100644
--- a/fs/nfs_common/Makefile
+++ b/fs/nfs_common/Makefile
@@ -6,8 +6,9 @@
obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
nfs_acl-objs := nfsacl.o
+CFLAGS_localio_trace.o += -I$(src)
obj-$(CONFIG_NFS_COMMON_LOCALIO_SUPPORT) += nfs_localio.o
-nfs_localio-objs := nfslocalio.o
+nfs_localio-objs := nfslocalio.o localio_trace.o
obj-$(CONFIG_GRACE_PERIOD) += grace.o
obj-$(CONFIG_NFS_V4_2_SSC_HELPER) += nfs_ssc.o
diff --git a/fs/nfs_common/localio_trace.c b/fs/nfs_common/localio_trace.c
new file mode 100644
index 0000000000000..7decfe57abebb
--- /dev/null
+++ b/fs/nfs_common/localio_trace.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Trond Myklebust <trond.myklebust@hammerspace.com>
+ * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
+ */
+#include <linux/nfs_fs.h>
+#include <linux/namei.h>
+
+#define CREATE_TRACE_POINTS
+#include "localio_trace.h"
diff --git a/fs/nfs_common/localio_trace.h b/fs/nfs_common/localio_trace.h
new file mode 100644
index 0000000000000..4055aec9ff8dc
--- /dev/null
+++ b/fs/nfs_common/localio_trace.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 Trond Myklebust <trond.myklebust@hammerspace.com>
+ * Copyright (C) 2024 Mike Snitzer <snitzer@hammerspace.com>
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM nfs_localio
+
+#if !defined(_TRACE_NFS_COMMON_LOCALIO_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_NFS_COMMON_LOCALIO_H
+
+#include <linux/tracepoint.h>
+
+#include <trace/misc/fs.h>
+#include <trace/misc/nfs.h>
+#include <trace/misc/sunrpc.h>
+
+DECLARE_EVENT_CLASS(nfs_local_client_event,
+ TP_PROTO(
+ const struct nfs_client *clp
+ ),
+
+ TP_ARGS(clp),
+
+ TP_STRUCT__entry(
+ __field(unsigned int, protocol)
+ __string(server, clp->cl_hostname)
+ ),
+
+ TP_fast_assign(
+ __entry->protocol = clp->rpc_ops->version;
+ __assign_str(server);
+ ),
+
+ TP_printk(
+ "server=%s NFSv%u", __get_str(server), __entry->protocol
+ )
+);
+
+#define DEFINE_NFS_LOCAL_CLIENT_EVENT(name) \
+ DEFINE_EVENT(nfs_local_client_event, name, \
+ TP_PROTO( \
+ const struct nfs_client *clp \
+ ), \
+ TP_ARGS(clp))
+
+DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_localio_enable_client);
+DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_localio_disable_client);
+
+#endif /* _TRACE_NFS_COMMON_LOCALIO_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE localio_trace
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index e5c0912b9a025..4e4991e059413 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -12,6 +12,8 @@
#include <linux/nfs_fs.h>
#include <net/netns/generic.h>
+#include "localio_trace.h"
+
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("NFS localio protocol bypass support");
@@ -141,6 +143,7 @@ void nfs_localio_enable_client(struct nfs_client *clp)
spin_lock(&nfs_uuid->lock);
set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ trace_nfs_localio_enable_client(clp);
spin_unlock(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
@@ -199,6 +202,7 @@ void nfs_localio_disable_client(struct nfs_client *clp)
if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
/* &clp->cl_uuid is always not NULL, using as bool here */
nfs_uuid = &clp->cl_uuid;
+ trace_nfs_localio_disable_client(clp);
}
spin_unlock(&clp->cl_uuid.lock);
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 13/15] nfs/localio: remove redundant code and simplify LOCALIO enablement
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (11 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 12/15] nfs_common: add nfs_localio trace events Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 15:31 ` [for-6.13 PATCH v2-fixed " Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 14/15] nfs: probe for LOCALIO when v4 client reconnects to server Mike Snitzer
` (2 subsequent siblings)
15 siblings, 1 reply; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Remove nfs_local_enable and nfs_local_disable, instead use
nfs_localio_enable_client and nfs_localio_disable_client.
Discontinue use of the NFS_CS_LOCAL_IO bit in the nfs_client struct's
cl_flags to reflect that LOCALIO is enabled; instead just test if the
net member of the nfs_uuid_t struct is set.
Also remove trace_nfs_local_enable and trace_nfs_local_disable,
comparable traces are available from nfs_localio.ko.
Suggested-by: NeilBrown <neilb@suse.de>
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs/client.c | 2 +-
fs/nfs/internal.h | 2 --
fs/nfs/localio.c | 28 +++++-----------------------
fs/nfs/nfstrace.h | 32 --------------------------------
fs/nfs_common/nfslocalio.c | 34 +++++++++++-----------------------
5 files changed, 17 insertions(+), 81 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index e83e1ce046130..a657318fede46 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -243,7 +243,7 @@ static void pnfs_init_server(struct nfs_server *server)
*/
void nfs_free_client(struct nfs_client *clp)
{
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
/* -EIO all pending I/O */
if (!IS_ERR(clp->cl_rpcclient))
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 57af3ab3adbe5..a252191b9335c 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -454,7 +454,6 @@ extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
/* localio.c */
-extern void nfs_local_disable(struct nfs_client *);
extern void nfs_local_probe(struct nfs_client *);
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
const struct cred *,
@@ -471,7 +470,6 @@ extern int nfs_local_commit(struct nfsd_file *,
extern bool nfs_server_is_local(const struct nfs_client *clp);
#else /* CONFIG_NFS_LOCALIO */
-static inline void nfs_local_disable(struct nfs_client *clp) {}
static inline void nfs_local_probe(struct nfs_client *clp) {}
static inline struct nfsd_file *
nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 7e432057c3a1f..4b6bf4ea7d7fc 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -56,7 +56,7 @@ MODULE_PARM_DESC(localio_O_DIRECT_semantics,
static inline bool nfs_client_is_local(const struct nfs_client *clp)
{
- return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ return !!rcu_access_pointer(clp->cl_uuid.net);
}
bool nfs_server_is_local(const struct nfs_client *clp)
@@ -121,24 +121,6 @@ const struct rpc_program nfslocalio_program = {
.stats = &nfslocalio_rpcstat,
};
-/*
- * nfs_local_enable - enable local i/o for an nfs_client
- */
-static void nfs_local_enable(struct nfs_client *clp)
-{
- trace_nfs_local_enable(clp);
- nfs_localio_enable_client(clp);
-}
-
-/*
- * nfs_local_disable - disable local i/o for an nfs_client
- */
-void nfs_local_disable(struct nfs_client *clp)
-{
- trace_nfs_local_disable(clp);
- nfs_localio_disable_client(clp);
-}
-
/*
* nfs_init_localioclient - Initialise an NFS localio client connection
*/
@@ -194,19 +176,19 @@ void nfs_local_probe(struct nfs_client *clp)
/* Disallow localio if disabled via sysfs or AUTH_SYS isn't used */
if (!localio_enabled ||
clp->cl_rpcclient->cl_auth->au_flavor != RPC_AUTH_UNIX) {
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
return;
}
if (nfs_client_is_local(clp)) {
/* If already enabled, disable and re-enable */
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
}
if (!nfs_uuid_begin(&clp->cl_uuid))
return;
if (nfs_server_uuid_is_local(clp))
- nfs_local_enable(clp);
+ nfs_localio_enable_client(clp);
nfs_uuid_end(&clp->cl_uuid);
}
EXPORT_SYMBOL_GPL(nfs_local_probe);
@@ -748,7 +730,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
if (status != 0) {
if (status == -EAGAIN)
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
nfs_local_file_put(localio);
hdr->task.tk_status = status;
nfs_local_hdr_release(hdr, call_ops);
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index 1eab98c277fab..7a058bd8c566e 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -1714,38 +1714,6 @@ TRACE_EVENT(nfs_local_open_fh,
)
);
-DECLARE_EVENT_CLASS(nfs_local_client_event,
- TP_PROTO(
- const struct nfs_client *clp
- ),
-
- TP_ARGS(clp),
-
- TP_STRUCT__entry(
- __field(unsigned int, protocol)
- __string(server, clp->cl_hostname)
- ),
-
- TP_fast_assign(
- __entry->protocol = clp->rpc_ops->version;
- __assign_str(server);
- ),
-
- TP_printk(
- "server=%s NFSv%u", __get_str(server), __entry->protocol
- )
-);
-
-#define DEFINE_NFS_LOCAL_CLIENT_EVENT(name) \
- DEFINE_EVENT(nfs_local_client_event, name, \
- TP_PROTO( \
- const struct nfs_client *clp \
- ), \
- TP_ARGS(clp))
-
-DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_local_enable);
-DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_local_disable);
-
DECLARE_EVENT_CLASS(nfs_xdr_event,
TP_PROTO(
const struct xdr_stream *xdr,
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 4e4991e059413..0a26c0ca99c21 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -37,7 +37,7 @@ static LIST_HEAD(nfs_uuids);
void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
{
- nfs_uuid->net = NULL;
+ RCU_INIT_POINTER(nfs_uuid->net, NULL);
nfs_uuid->dom = NULL;
nfs_uuid->list_lock = NULL;
INIT_LIST_HEAD(&nfs_uuid->list);
@@ -49,7 +49,7 @@ EXPORT_SYMBOL_GPL(nfs_uuid_init);
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
spin_lock(&nfs_uuid->lock);
- if (nfs_uuid->net) {
+ if (rcu_access_pointer(nfs_uuid->net)) {
/* This nfs_uuid is already in use */
spin_unlock(&nfs_uuid->lock);
return false;
@@ -74,9 +74,9 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{
- if (nfs_uuid->net == NULL) {
+ if (!rcu_access_pointer(nfs_uuid->net)) {
spin_lock(&nfs_uuid->lock);
- if (nfs_uuid->net == NULL) {
+ if (!rcu_access_pointer(nfs_uuid->net)) {
/* Not local, remove from nfs_uuids */
spin_lock(&nfs_uuids_lock);
list_del_init(&nfs_uuid->list);
@@ -139,12 +139,8 @@ EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
void nfs_localio_enable_client(struct nfs_client *clp)
{
- nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
-
- spin_lock(&nfs_uuid->lock);
- set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ /* nfs_uuid_is_local() does the actual enablement */
trace_nfs_localio_enable_client(clp);
- spin_unlock(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
@@ -152,15 +148,15 @@ EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
* Cleanup the nfs_uuid_t embedded in an nfs_client.
* This is the long-form of nfs_uuid_init().
*/
-static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
+static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
{
LIST_HEAD(local_files);
struct nfs_file_localio *nfl, *tmp;
spin_lock(&nfs_uuid->lock);
- if (unlikely(!nfs_uuid->net)) {
+ if (unlikely(!rcu_access_pointer(nfs_uuid->net))) {
spin_unlock(&nfs_uuid->lock);
- return;
+ return false;
}
RCU_INIT_POINTER(nfs_uuid->net, NULL);
@@ -192,22 +188,14 @@ static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
module_put(nfsd_mod);
spin_unlock(&nfs_uuid->lock);
+
+ return true;
}
void nfs_localio_disable_client(struct nfs_client *clp)
{
- nfs_uuid_t *nfs_uuid = NULL;
-
- spin_lock(&clp->cl_uuid.lock); /* aka &nfs_uuid->lock */
- if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
- /* &clp->cl_uuid is always not NULL, using as bool here */
- nfs_uuid = &clp->cl_uuid;
+ if (nfs_uuid_put(&clp->cl_uuid))
trace_nfs_localio_disable_client(clp);
- }
- spin_unlock(&clp->cl_uuid.lock);
-
- if (nfs_uuid)
- nfs_uuid_put(nfs_uuid);
}
EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2-fixed 13/15] nfs/localio: remove redundant code and simplify LOCALIO enablement
2024-11-14 3:59 ` [for-6.13 PATCH v2 13/15] nfs/localio: remove redundant code and simplify LOCALIO enablement Mike Snitzer
@ 2024-11-14 15:31 ` Mike Snitzer
0 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 15:31 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Remove nfs_local_enable and nfs_local_disable, instead use
nfs_localio_enable_client and nfs_localio_disable_client.
Discontinue use of the NFS_CS_LOCAL_IO bit in the nfs_client struct's
cl_flags to reflect that LOCALIO is enabled; instead just test if the
net member of the nfs_uuid_t struct is set.
Also remove trace_nfs_local_enable and trace_nfs_local_disable,
comparable traces are available from nfs_localio.ko.
Suggested-by: NeilBrown <neilb@suse.de>
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs/client.c | 4 ++--
fs/nfs/internal.h | 2 --
fs/nfs/localio.c | 28 +++++-----------------------
fs/nfs/nfstrace.h | 32 --------------------------------
fs/nfs_common/nfslocalio.c | 34 +++++++++++-----------------------
include/linux/nfslocalio.h | 4 ++++
6 files changed, 22 insertions(+), 82 deletions(-)
[this 'v2-fixed" addresses build failure due to missing
nfs_localio_disable_client if/when LOCALIO isn't enabled in Kconfig]
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index e83e1ce046130..16530c71fd152 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -38,7 +38,7 @@
#include <linux/sunrpc/bc_xprt.h>
#include <linux/nsproxy.h>
#include <linux/pid_namespace.h>
-
+#include <linux/nfslocalio.h>
#include "nfs4_fs.h"
#include "callback.h"
@@ -243,7 +243,7 @@ static void pnfs_init_server(struct nfs_server *server)
*/
void nfs_free_client(struct nfs_client *clp)
{
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
/* -EIO all pending I/O */
if (!IS_ERR(clp->cl_rpcclient))
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 57af3ab3adbe5..a252191b9335c 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -454,7 +454,6 @@ extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
/* localio.c */
-extern void nfs_local_disable(struct nfs_client *);
extern void nfs_local_probe(struct nfs_client *);
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
const struct cred *,
@@ -471,7 +470,6 @@ extern int nfs_local_commit(struct nfsd_file *,
extern bool nfs_server_is_local(const struct nfs_client *clp);
#else /* CONFIG_NFS_LOCALIO */
-static inline void nfs_local_disable(struct nfs_client *clp) {}
static inline void nfs_local_probe(struct nfs_client *clp) {}
static inline struct nfsd_file *
nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 7e432057c3a1f..4b6bf4ea7d7fc 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -56,7 +56,7 @@ MODULE_PARM_DESC(localio_O_DIRECT_semantics,
static inline bool nfs_client_is_local(const struct nfs_client *clp)
{
- return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ return !!rcu_access_pointer(clp->cl_uuid.net);
}
bool nfs_server_is_local(const struct nfs_client *clp)
@@ -121,24 +121,6 @@ const struct rpc_program nfslocalio_program = {
.stats = &nfslocalio_rpcstat,
};
-/*
- * nfs_local_enable - enable local i/o for an nfs_client
- */
-static void nfs_local_enable(struct nfs_client *clp)
-{
- trace_nfs_local_enable(clp);
- nfs_localio_enable_client(clp);
-}
-
-/*
- * nfs_local_disable - disable local i/o for an nfs_client
- */
-void nfs_local_disable(struct nfs_client *clp)
-{
- trace_nfs_local_disable(clp);
- nfs_localio_disable_client(clp);
-}
-
/*
* nfs_init_localioclient - Initialise an NFS localio client connection
*/
@@ -194,19 +176,19 @@ void nfs_local_probe(struct nfs_client *clp)
/* Disallow localio if disabled via sysfs or AUTH_SYS isn't used */
if (!localio_enabled ||
clp->cl_rpcclient->cl_auth->au_flavor != RPC_AUTH_UNIX) {
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
return;
}
if (nfs_client_is_local(clp)) {
/* If already enabled, disable and re-enable */
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
}
if (!nfs_uuid_begin(&clp->cl_uuid))
return;
if (nfs_server_uuid_is_local(clp))
- nfs_local_enable(clp);
+ nfs_localio_enable_client(clp);
nfs_uuid_end(&clp->cl_uuid);
}
EXPORT_SYMBOL_GPL(nfs_local_probe);
@@ -748,7 +730,7 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio,
if (status != 0) {
if (status == -EAGAIN)
- nfs_local_disable(clp);
+ nfs_localio_disable_client(clp);
nfs_local_file_put(localio);
hdr->task.tk_status = status;
nfs_local_hdr_release(hdr, call_ops);
diff --git a/fs/nfs/nfstrace.h b/fs/nfs/nfstrace.h
index 1eab98c277fab..7a058bd8c566e 100644
--- a/fs/nfs/nfstrace.h
+++ b/fs/nfs/nfstrace.h
@@ -1714,38 +1714,6 @@ TRACE_EVENT(nfs_local_open_fh,
)
);
-DECLARE_EVENT_CLASS(nfs_local_client_event,
- TP_PROTO(
- const struct nfs_client *clp
- ),
-
- TP_ARGS(clp),
-
- TP_STRUCT__entry(
- __field(unsigned int, protocol)
- __string(server, clp->cl_hostname)
- ),
-
- TP_fast_assign(
- __entry->protocol = clp->rpc_ops->version;
- __assign_str(server);
- ),
-
- TP_printk(
- "server=%s NFSv%u", __get_str(server), __entry->protocol
- )
-);
-
-#define DEFINE_NFS_LOCAL_CLIENT_EVENT(name) \
- DEFINE_EVENT(nfs_local_client_event, name, \
- TP_PROTO( \
- const struct nfs_client *clp \
- ), \
- TP_ARGS(clp))
-
-DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_local_enable);
-DEFINE_NFS_LOCAL_CLIENT_EVENT(nfs_local_disable);
-
DECLARE_EVENT_CLASS(nfs_xdr_event,
TP_PROTO(
const struct xdr_stream *xdr,
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 4e4991e059413..0a26c0ca99c21 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -37,7 +37,7 @@ static LIST_HEAD(nfs_uuids);
void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
{
- nfs_uuid->net = NULL;
+ RCU_INIT_POINTER(nfs_uuid->net, NULL);
nfs_uuid->dom = NULL;
nfs_uuid->list_lock = NULL;
INIT_LIST_HEAD(&nfs_uuid->list);
@@ -49,7 +49,7 @@ EXPORT_SYMBOL_GPL(nfs_uuid_init);
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
spin_lock(&nfs_uuid->lock);
- if (nfs_uuid->net) {
+ if (rcu_access_pointer(nfs_uuid->net)) {
/* This nfs_uuid is already in use */
spin_unlock(&nfs_uuid->lock);
return false;
@@ -74,9 +74,9 @@ EXPORT_SYMBOL_GPL(nfs_uuid_begin);
void nfs_uuid_end(nfs_uuid_t *nfs_uuid)
{
- if (nfs_uuid->net == NULL) {
+ if (!rcu_access_pointer(nfs_uuid->net)) {
spin_lock(&nfs_uuid->lock);
- if (nfs_uuid->net == NULL) {
+ if (!rcu_access_pointer(nfs_uuid->net)) {
/* Not local, remove from nfs_uuids */
spin_lock(&nfs_uuids_lock);
list_del_init(&nfs_uuid->list);
@@ -139,12 +139,8 @@ EXPORT_SYMBOL_GPL(nfs_uuid_is_local);
void nfs_localio_enable_client(struct nfs_client *clp)
{
- nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
-
- spin_lock(&nfs_uuid->lock);
- set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ /* nfs_uuid_is_local() does the actual enablement */
trace_nfs_localio_enable_client(clp);
- spin_unlock(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
@@ -152,15 +148,15 @@ EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
* Cleanup the nfs_uuid_t embedded in an nfs_client.
* This is the long-form of nfs_uuid_init().
*/
-static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
+static bool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
{
LIST_HEAD(local_files);
struct nfs_file_localio *nfl, *tmp;
spin_lock(&nfs_uuid->lock);
- if (unlikely(!nfs_uuid->net)) {
+ if (unlikely(!rcu_access_pointer(nfs_uuid->net))) {
spin_unlock(&nfs_uuid->lock);
- return;
+ return false;
}
RCU_INIT_POINTER(nfs_uuid->net, NULL);
@@ -192,22 +188,14 @@ static void nfs_uuid_put(nfs_uuid_t *nfs_uuid)
module_put(nfsd_mod);
spin_unlock(&nfs_uuid->lock);
+
+ return true;
}
void nfs_localio_disable_client(struct nfs_client *clp)
{
- nfs_uuid_t *nfs_uuid = NULL;
-
- spin_lock(&clp->cl_uuid.lock); /* aka &nfs_uuid->lock */
- if (test_and_clear_bit(NFS_CS_LOCAL_IO, &clp->cl_flags)) {
- /* &clp->cl_uuid is always not NULL, using as bool here */
- nfs_uuid = &clp->cl_uuid;
+ if (nfs_uuid_put(&clp->cl_uuid))
trace_nfs_localio_disable_client(clp);
- }
- spin_unlock(&clp->cl_uuid.lock);
-
- if (nfs_uuid)
- nfs_uuid_put(nfs_uuid);
}
EXPORT_SYMBOL_GPL(nfs_localio_disable_client);
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index c68a529230c14..05817d6ef3d1e 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -111,6 +111,10 @@ static inline void nfs_close_local_fh(struct nfs_file_localio *nfl)
static inline void nfsd_localio_ops_init(void)
{
}
+struct nfs_client;
+static inline void nfs_localio_disable_client(struct nfs_client *clp)
+{
+}
#endif /* CONFIG_NFS_LOCALIO */
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [for-6.13 PATCH v2 14/15] nfs: probe for LOCALIO when v4 client reconnects to server
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (12 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 13/15] nfs/localio: remove redundant code and simplify LOCALIO enablement Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-14 3:59 ` [for-6.13 PATCH v2 15/15] nfs: probe for LOCALIO when v3 " Mike Snitzer
2024-11-14 16:14 ` (subset) [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO cel
15 siblings, 0 replies; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Introduce nfs_local_probe_async() for the NFS client to initiate
if/when it reconnects with server. For NFSv4 it is a simple matter to
call nfs_local_probe_async() from nfs4_do_reclaim (during NFSv4
grace).
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs/client.c | 1 +
fs/nfs/internal.h | 3 +++
fs/nfs/localio.c | 14 ++++++++++++++
fs/nfs/nfs4state.c | 1 +
include/linux/nfs_fs_sb.h | 1 +
5 files changed, 20 insertions(+)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index a657318fede46..9765aad6e3dab 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -186,6 +186,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
seqlock_init(&clp->cl_boot_lock);
ktime_get_real_ts64(&clp->cl_nfssvc_boot);
nfs_uuid_init(&clp->cl_uuid);
+ INIT_WORK(&clp->cl_local_probe_work, nfs_local_probe_async_work);
#endif /* CONFIG_NFS_LOCALIO */
clp->cl_principal = "*";
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index a252191b9335c..ad9c56bc977bf 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -455,6 +455,8 @@ extern int nfs_wait_bit_killable(struct wait_bit_key *key, int mode);
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
/* localio.c */
extern void nfs_local_probe(struct nfs_client *);
+extern void nfs_local_probe_async(struct nfs_client *);
+extern void nfs_local_probe_async_work(struct work_struct *);
extern struct nfsd_file *nfs_local_open_fh(struct nfs_client *,
const struct cred *,
struct nfs_fh *,
@@ -471,6 +473,7 @@ extern bool nfs_server_is_local(const struct nfs_client *clp);
#else /* CONFIG_NFS_LOCALIO */
static inline void nfs_local_probe(struct nfs_client *clp) {}
+static inline void nfs_local_probe_async(struct nfs_client *clp) {}
static inline struct nfsd_file *
nfs_local_open_fh(struct nfs_client *clp, const struct cred *cred,
struct nfs_fh *fh, struct nfs_file_localio *nfl,
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 4b6bf4ea7d7fc..1eee5aac28843 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -193,6 +193,20 @@ void nfs_local_probe(struct nfs_client *clp)
}
EXPORT_SYMBOL_GPL(nfs_local_probe);
+void nfs_local_probe_async_work(struct work_struct *work)
+{
+ struct nfs_client *clp =
+ container_of(work, struct nfs_client, cl_local_probe_work);
+
+ nfs_local_probe(clp);
+}
+
+void nfs_local_probe_async(struct nfs_client *clp)
+{
+ queue_work(nfsiod_workqueue, &clp->cl_local_probe_work);
+}
+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);
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 9a9f60a2291b4..542cdf71229fe 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1955,6 +1955,7 @@ static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recov
}
rcu_read_unlock();
nfs4_free_state_owners(&freeme);
+ nfs_local_probe_async(clp);
if (lost_locks)
pr_warn("NFS: %s: lost %d locks\n",
clp->cl_hostname, lost_locks);
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 239d86ef166c0..63d7e0f478d89 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -132,6 +132,7 @@ struct nfs_client {
struct timespec64 cl_nfssvc_boot;
seqlock_t cl_boot_lock;
nfs_uuid_t cl_uuid;
+ struct work_struct cl_local_probe_work;
#endif /* CONFIG_NFS_LOCALIO */
};
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [for-6.13 PATCH v2 15/15] nfs: probe for LOCALIO when v3 client reconnects to server
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (13 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 14/15] nfs: probe for LOCALIO when v4 client reconnects to server Mike Snitzer
@ 2024-11-14 3:59 ` Mike Snitzer
2024-11-15 3:55 ` NeilBrown
2024-11-14 16:14 ` (subset) [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO cel
15 siblings, 1 reply; 23+ messages in thread
From: Mike Snitzer @ 2024-11-14 3:59 UTC (permalink / raw)
To: linux-nfs
Cc: Anna Schumaker, Trond Myklebust, Chuck Lever, Jeff Layton,
NeilBrown
Re-enabling NFSv3 LOCALIO is made more complex (than NFSv4) because v3
is stateless. As such, the hueristic used to identify a LOCALIO probe
point is more adhoc by nature: if/when NFSv3 client IO begins to
complete again in terms of normal RPC-based NFSv3 server IO, attempt
nfs_local_probe_async().
Care is taken to throttle the frequency of nfs_local_probe_async(),
otherwise there could be a flood of repeat calls to
nfs_local_probe_async().
This approach works for most use-cases but it doesn't handle the
possibility of using HA to migrate an NFS server local to the same
host as an NFS client that is actively connected to the migrated NFS
server. NFSv3 LOCALIO won't handle this case given it relies on the
client having been flagged with NFS_CS_LOCAL_IO when the client was
created on the host (e.g. mount time).
Alternatve approaches have been discussed but there isn't clear
consensus yet.
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
---
fs/nfs/internal.h | 5 +++++
fs/nfs/localio.c | 18 +++++++++++++++++-
fs/nfs/nfs3proc.c | 32 +++++++++++++++++++++++++++++---
fs/nfs_common/nfslocalio.c | 1 +
include/linux/nfslocalio.h | 3 ++-
5 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index ad9c56bc977bf..14f4d871b6a30 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -470,6 +470,7 @@ extern int nfs_local_commit(struct nfsd_file *,
struct nfs_commit_data *,
const struct rpc_call_ops *, int);
extern bool nfs_server_is_local(const struct nfs_client *clp);
+extern bool nfs_server_was_local(const struct nfs_client *clp);
#else /* CONFIG_NFS_LOCALIO */
static inline void nfs_local_probe(struct nfs_client *clp) {}
@@ -498,6 +499,10 @@ static inline bool nfs_server_is_local(const struct nfs_client *clp)
{
return false;
}
+static inline bool nfs_server_was_local(const struct nfs_client *clp)
+{
+ return false;
+}
#endif /* CONFIG_NFS_LOCALIO */
/* super.c */
diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
index 1eee5aac28843..d5152aa46813c 100644
--- a/fs/nfs/localio.c
+++ b/fs/nfs/localio.c
@@ -65,6 +65,17 @@ bool nfs_server_is_local(const struct nfs_client *clp)
}
EXPORT_SYMBOL_GPL(nfs_server_is_local);
+static inline bool nfs_client_was_local(const struct nfs_client *clp)
+{
+ return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+}
+
+bool nfs_server_was_local(const struct nfs_client *clp)
+{
+ return nfs_client_was_local(clp) && localio_enabled;
+}
+EXPORT_SYMBOL_GPL(nfs_server_was_local);
+
/*
* UUID_IS_LOCAL XDR functions
*/
@@ -187,8 +198,13 @@ void nfs_local_probe(struct nfs_client *clp)
if (!nfs_uuid_begin(&clp->cl_uuid))
return;
- if (nfs_server_uuid_is_local(clp))
+ if (nfs_server_uuid_is_local(clp)) {
nfs_localio_enable_client(clp);
+ /* Set hint that client and server are LOCALIO capable */
+ spin_lock(&clp->cl_uuid.lock);
+ set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
+ spin_unlock(&clp->cl_uuid.lock);
+ }
nfs_uuid_end(&clp->cl_uuid);
}
EXPORT_SYMBOL_GPL(nfs_local_probe);
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 1566163c6d85b..6ed2e4466002d 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -844,6 +844,27 @@ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
return status;
}
+static void nfs3_local_probe(struct nfs_server *server)
+{
+#if IS_ENABLED(CONFIG_NFS_LOCALIO)
+ struct nfs_client *clp = server->nfs_client;
+ nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
+
+ if (likely(!nfs_server_was_local(clp)))
+ return;
+ /*
+ * Try re-enabling LOCALIO if it was previously enabled, but
+ * was disabled due to server restart, and IO has successfully
+ * completed in terms of normal RPC.
+ */
+ /* Arbitrary throttle to reduce nfs_local_probe_async() frequency */
+ if ((nfs_uuid->local_probe_count++ & 511) == 0) {
+ if (unlikely(!nfs_server_is_local(clp) && nfs_server_was_local(clp)))
+ nfs_local_probe_async(clp);
+ }
+#endif
+}
+
static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
{
struct inode *inode = hdr->inode;
@@ -855,8 +876,11 @@ static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
if (nfs3_async_handle_jukebox(task, inode))
return -EAGAIN;
- if (task->tk_status >= 0 && !server->read_hdrsize)
- cmpxchg(&server->read_hdrsize, 0, hdr->res.replen);
+ if (task->tk_status >= 0) {
+ if (!server->read_hdrsize)
+ cmpxchg(&server->read_hdrsize, 0, hdr->res.replen);
+ nfs3_local_probe(server);
+ }
nfs_invalidate_atime(inode);
nfs_refresh_inode(inode, &hdr->fattr);
@@ -886,8 +910,10 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
if (nfs3_async_handle_jukebox(task, inode))
return -EAGAIN;
- if (task->tk_status >= 0)
+ if (task->tk_status >= 0) {
nfs_writeback_update_inode(hdr);
+ nfs3_local_probe(NFS_SERVER(inode));
+ }
return 0;
}
diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
index 0a26c0ca99c21..41c0077ead721 100644
--- a/fs/nfs_common/nfslocalio.c
+++ b/fs/nfs_common/nfslocalio.c
@@ -43,6 +43,7 @@ void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
INIT_LIST_HEAD(&nfs_uuid->list);
INIT_LIST_HEAD(&nfs_uuid->files);
spin_lock_init(&nfs_uuid->lock);
+ nfs_uuid->local_probe_count = 0;
}
EXPORT_SYMBOL_GPL(nfs_uuid_init);
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index c68a529230c14..e05f1cf3cf476 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -27,7 +27,8 @@ struct nfs_file_localio;
*/
typedef struct {
uuid_t uuid;
- /* sadly this struct is just over a cacheline, avoid bouncing */
+ unsigned local_probe_count;
+ /* sadly this struct is over a cacheline, avoid bouncing */
spinlock_t ____cacheline_aligned lock;
struct list_head list;
spinlock_t *list_lock; /* nn->local_clients_lock */
--
2.44.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [for-6.13 PATCH v2 15/15] nfs: probe for LOCALIO when v3 client reconnects to server
2024-11-14 3:59 ` [for-6.13 PATCH v2 15/15] nfs: probe for LOCALIO when v3 " Mike Snitzer
@ 2024-11-15 3:55 ` NeilBrown
0 siblings, 0 replies; 23+ messages in thread
From: NeilBrown @ 2024-11-15 3:55 UTC (permalink / raw)
To: Mike Snitzer
Cc: linux-nfs, Anna Schumaker, Trond Myklebust, Chuck Lever,
Jeff Layton
On Thu, 14 Nov 2024, Mike Snitzer wrote:
> Re-enabling NFSv3 LOCALIO is made more complex (than NFSv4) because v3
> is stateless. As such, the hueristic used to identify a LOCALIO probe
> point is more adhoc by nature: if/when NFSv3 client IO begins to
> complete again in terms of normal RPC-based NFSv3 server IO, attempt
> nfs_local_probe_async().
>
> Care is taken to throttle the frequency of nfs_local_probe_async(),
> otherwise there could be a flood of repeat calls to
> nfs_local_probe_async().
>
> This approach works for most use-cases but it doesn't handle the
> possibility of using HA to migrate an NFS server local to the same
> host as an NFS client that is actively connected to the migrated NFS
> server. NFSv3 LOCALIO won't handle this case given it relies on the
> client having been flagged with NFS_CS_LOCAL_IO when the client was
> created on the host (e.g. mount time).
You could simply remove the NFS_CS_LOCAL_IO flag from the patch
completely, and always probe every 512 IO requests. That would not be
much worse, and would be substantially better.
Then we would be well placed to remove "struct nfs_client" from
nfs_common.
NeilBrown
>
> Alternatve approaches have been discussed but there isn't clear
> consensus yet.
>
> Signed-off-by: Mike Snitzer <snitzer@kernel.org>
> ---
> fs/nfs/internal.h | 5 +++++
> fs/nfs/localio.c | 18 +++++++++++++++++-
> fs/nfs/nfs3proc.c | 32 +++++++++++++++++++++++++++++---
> fs/nfs_common/nfslocalio.c | 1 +
> include/linux/nfslocalio.h | 3 ++-
> 5 files changed, 54 insertions(+), 5 deletions(-)
>
> diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
> index ad9c56bc977bf..14f4d871b6a30 100644
> --- a/fs/nfs/internal.h
> +++ b/fs/nfs/internal.h
> @@ -470,6 +470,7 @@ extern int nfs_local_commit(struct nfsd_file *,
> struct nfs_commit_data *,
> const struct rpc_call_ops *, int);
> extern bool nfs_server_is_local(const struct nfs_client *clp);
> +extern bool nfs_server_was_local(const struct nfs_client *clp);
>
> #else /* CONFIG_NFS_LOCALIO */
> static inline void nfs_local_probe(struct nfs_client *clp) {}
> @@ -498,6 +499,10 @@ static inline bool nfs_server_is_local(const struct nfs_client *clp)
> {
> return false;
> }
> +static inline bool nfs_server_was_local(const struct nfs_client *clp)
> +{
> + return false;
> +}
> #endif /* CONFIG_NFS_LOCALIO */
>
> /* super.c */
> diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c
> index 1eee5aac28843..d5152aa46813c 100644
> --- a/fs/nfs/localio.c
> +++ b/fs/nfs/localio.c
> @@ -65,6 +65,17 @@ bool nfs_server_is_local(const struct nfs_client *clp)
> }
> EXPORT_SYMBOL_GPL(nfs_server_is_local);
>
> +static inline bool nfs_client_was_local(const struct nfs_client *clp)
> +{
> + return !!test_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
> +}
> +
> +bool nfs_server_was_local(const struct nfs_client *clp)
> +{
> + return nfs_client_was_local(clp) && localio_enabled;
> +}
> +EXPORT_SYMBOL_GPL(nfs_server_was_local);
> +
> /*
> * UUID_IS_LOCAL XDR functions
> */
> @@ -187,8 +198,13 @@ void nfs_local_probe(struct nfs_client *clp)
>
> if (!nfs_uuid_begin(&clp->cl_uuid))
> return;
> - if (nfs_server_uuid_is_local(clp))
> + if (nfs_server_uuid_is_local(clp)) {
> nfs_localio_enable_client(clp);
> + /* Set hint that client and server are LOCALIO capable */
> + spin_lock(&clp->cl_uuid.lock);
> + set_bit(NFS_CS_LOCAL_IO, &clp->cl_flags);
> + spin_unlock(&clp->cl_uuid.lock);
> + }
> nfs_uuid_end(&clp->cl_uuid);
> }
> EXPORT_SYMBOL_GPL(nfs_local_probe);
> diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
> index 1566163c6d85b..6ed2e4466002d 100644
> --- a/fs/nfs/nfs3proc.c
> +++ b/fs/nfs/nfs3proc.c
> @@ -844,6 +844,27 @@ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
> return status;
> }
>
> +static void nfs3_local_probe(struct nfs_server *server)
> +{
> +#if IS_ENABLED(CONFIG_NFS_LOCALIO)
> + struct nfs_client *clp = server->nfs_client;
> + nfs_uuid_t *nfs_uuid = &clp->cl_uuid;
> +
> + if (likely(!nfs_server_was_local(clp)))
> + return;
> + /*
> + * Try re-enabling LOCALIO if it was previously enabled, but
> + * was disabled due to server restart, and IO has successfully
> + * completed in terms of normal RPC.
> + */
> + /* Arbitrary throttle to reduce nfs_local_probe_async() frequency */
> + if ((nfs_uuid->local_probe_count++ & 511) == 0) {
> + if (unlikely(!nfs_server_is_local(clp) && nfs_server_was_local(clp)))
> + nfs_local_probe_async(clp);
> + }
> +#endif
> +}
> +
> static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
> {
> struct inode *inode = hdr->inode;
> @@ -855,8 +876,11 @@ static int nfs3_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
> if (nfs3_async_handle_jukebox(task, inode))
> return -EAGAIN;
>
> - if (task->tk_status >= 0 && !server->read_hdrsize)
> - cmpxchg(&server->read_hdrsize, 0, hdr->res.replen);
> + if (task->tk_status >= 0) {
> + if (!server->read_hdrsize)
> + cmpxchg(&server->read_hdrsize, 0, hdr->res.replen);
> + nfs3_local_probe(server);
> + }
>
> nfs_invalidate_atime(inode);
> nfs_refresh_inode(inode, &hdr->fattr);
> @@ -886,8 +910,10 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
>
> if (nfs3_async_handle_jukebox(task, inode))
> return -EAGAIN;
> - if (task->tk_status >= 0)
> + if (task->tk_status >= 0) {
> nfs_writeback_update_inode(hdr);
> + nfs3_local_probe(NFS_SERVER(inode));
> + }
> return 0;
> }
>
> diff --git a/fs/nfs_common/nfslocalio.c b/fs/nfs_common/nfslocalio.c
> index 0a26c0ca99c21..41c0077ead721 100644
> --- a/fs/nfs_common/nfslocalio.c
> +++ b/fs/nfs_common/nfslocalio.c
> @@ -43,6 +43,7 @@ void nfs_uuid_init(nfs_uuid_t *nfs_uuid)
> INIT_LIST_HEAD(&nfs_uuid->list);
> INIT_LIST_HEAD(&nfs_uuid->files);
> spin_lock_init(&nfs_uuid->lock);
> + nfs_uuid->local_probe_count = 0;
> }
> EXPORT_SYMBOL_GPL(nfs_uuid_init);
>
> diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
> index c68a529230c14..e05f1cf3cf476 100644
> --- a/include/linux/nfslocalio.h
> +++ b/include/linux/nfslocalio.h
> @@ -27,7 +27,8 @@ struct nfs_file_localio;
> */
> typedef struct {
> uuid_t uuid;
> - /* sadly this struct is just over a cacheline, avoid bouncing */
> + unsigned local_probe_count;
> + /* sadly this struct is over a cacheline, avoid bouncing */
> spinlock_t ____cacheline_aligned lock;
> struct list_head list;
> spinlock_t *list_lock; /* nn->local_clients_lock */
> --
> 2.44.0
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: (subset) [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO
2024-11-14 3:59 [for-6.13 PATCH v2 00/15] nfs/nfsd: fixes and improvements for LOCALIO Mike Snitzer
` (14 preceding siblings ...)
2024-11-14 3:59 ` [for-6.13 PATCH v2 15/15] nfs: probe for LOCALIO when v3 " Mike Snitzer
@ 2024-11-14 16:14 ` cel
15 siblings, 0 replies; 23+ messages in thread
From: cel @ 2024-11-14 16:14 UTC (permalink / raw)
To: linux-nfs, Mike Snitzer
Cc: Chuck Lever, Anna Schumaker, Trond Myklebust, Jeff Layton,
NeilBrown
From: Chuck Lever <chuck.lever@oracle.com>
On Wed, 13 Nov 2024 22:59:37 -0500, Mike Snitzer wrote:
> Move holding the RCU from nfs_to_nfsd_file_put_local to
> nfs_to_nfsd_net_put. It is the call to nfs_to->nfsd_serv_put that
> requires the RCU anyway (the puts for nfsd_file and netns were
> combined to avoid an extra indirect reference but that
> micro-optimization isn't possible now).
>
> [...]
Applied to nfsd-next for v6.13, thanks!
[01/15] nfs_common: must not hold RCU while calling nfsd_file_put_local
commit: f055a6cafaea151c2df2a75f31c3ecfaa8b90b9e
--
Chuck Lever
^ permalink raw reply [flat|nested] 23+ messages in thread