From: Jeff Layton <jlayton@kernel.org>
To: NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
Chuck Lever <cel@kernel.org>
Cc: Trond Myklebust <trondmy@kernel.org>,
Anna Schumaker <anna@kernel.org>,
Steve Dickson <steved@redhat.com>,
linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org,
Jeff Layton <jlayton@kernel.org>
Subject: [PATCH v6 5/6] nfsd: count NFSv4 callback operations per netns
Date: Fri, 19 Jun 2026 11:26:40 -0400 [thread overview]
Message-ID: <20260619-exportd-netlink-v6-5-ddef3499793c@kernel.org> (raw)
In-Reply-To: <20260619-exportd-netlink-v6-0-ddef3499793c@kernel.org>
The NFS server tracks per-operation call counts for the forward channel
(proc4ops) but keeps no statistics for the NFSv4 backchannel (callback)
operations it sends to clients.
Add a per-netns array of percpu counters for callback operations, indexed
by RFC 8881 callback opcode (OP_CB_GETATTR..OP_CB_OFFLOAD), and bump the
relevant counter in nfsd4_run_cb(), which is hit exactly once per callback
that is actually queued.
CB_GETATTR is sent when a GETATTR conflicts with an outstanding write
delegation, which is roughly what the dedicated wdeleg_getattr counter
tracked. The two are not identical: the old counter incremented on every
such conflict, whereas the CB_GETATTR counter only counts callbacks that
are actually queued, so concurrent conflicts that coalesce onto an
already in-flight CB_GETATTR are now counted once rather than once per
conflict. Report the procfs "wdeleg_getattr" line from the CB_GETATTR
counter and drop the now-redundant NFSD_STATS_WDELEG_GETATTR counter, its
helper, and its increment site.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/netns.h | 12 +++++++++++-
fs/nfsd/nfs4callback.c | 22 +++++++++++++++++++++-
fs/nfsd/nfs4state.c | 2 --
fs/nfsd/nfsctl.c | 14 ++++++++++++++
fs/nfsd/stats.c | 2 +-
fs/nfsd/stats.h | 5 +++--
6 files changed, 50 insertions(+), 7 deletions(-)
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 03724bef10a7..37daba0d4747 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -53,11 +53,16 @@ enum {
NFSD_STATS_FIRST_NFS4_OP, /* count of individual nfsv4 operations */
NFSD_STATS_LAST_NFS4_OP = NFSD_STATS_FIRST_NFS4_OP + LAST_NFS4_OP,
#define NFSD_STATS_NFS4_OP(op) (NFSD_STATS_FIRST_NFS4_OP + (op))
- NFSD_STATS_WDELEG_GETATTR, /* count of getattr conflict with wdeleg */
#endif
NFSD_STATS_COUNTERS_NUM
};
+/*
+ * Per-netns NFSv4 callback (backchannel) per-operation counters, indexed
+ * directly by RFC 8881 callback opcode (OP_CB_GETATTR..OP_CB_OFFLOAD).
+ */
+#define NFSD_STATS_CB_OPS_NUM (OP_CB_OFFLOAD + 1)
+
/*
* Represents a nfsd "container". With respect to nfsv4 state tracking, the
* fields of interest are the *_id_hashtbls and the *_name_tree. These track
@@ -198,6 +203,11 @@ struct nfsd_net {
/* Per-netns stats counters */
struct percpu_counter counter[NFSD_STATS_COUNTERS_NUM];
+#ifdef CONFIG_NFSD_V4
+ /* Per-netns NFSv4 callback (backchannel) per-operation counters */
+ struct percpu_counter cb_counter[NFSD_STATS_CB_OPS_NUM];
+#endif
+
/* sunrpc svc stats */
struct svc_stat nfsd_svcstats;
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 71dcb448fa0a..b171257da6ba 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -1921,12 +1921,32 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
bool nfsd4_run_cb(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
+ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+ const struct nfsd4_callback_ops *ops = cb->cb_ops;
+ u32 minorversion = clp->cl_minorversion;
bool queued;
+ /*
+ * Snapshot the opcode, minorversion, and the per-net pointer before
+ * queuing: once nfsd4_queue_cb() has queued the work, the callback (and
+ * the object that embeds it) may be processed and freed concurrently, so
+ * neither cb nor clp can be dereferenced afterward.
+ */
nfsd41_cb_inflight_begin(clp);
queued = nfsd4_queue_cb(cb);
- if (!queued)
+ if (queued) {
+ if (ops)
+ nfsd_stats_cb_op_inc(nn, ops->opcode);
+ /*
+ * Minorversion > 0 callbacks prepend a CB_SEQUENCE op (see
+ * encode_cb_sequence4args()); count it like the forechannel
+ * counts SEQUENCE, so it isn't perpetually reported as zero.
+ */
+ if (minorversion > 0)
+ nfsd_stats_cb_op_inc(nn, OP_CB_SEQUENCE);
+ } else {
nfsd41_cb_inflight_end(clp);
+ }
return queued;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index b830aed7ae39..8f8da1312231 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -9904,7 +9904,6 @@ __be32
nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_delegation **pdp)
{
- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfsd_thread_local_info *ntli = rqstp->rq_private;
struct file_lock_context *ctx;
struct nfs4_delegation *dp = NULL;
@@ -9944,7 +9943,6 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
return 0;
}
- nfsd_stats_wdeleg_getattr_inc(nn);
refcount_inc(&dp->dl_stid.sc_count);
ncf = &dp->dl_cb_fattr;
nfs4_cb_getattr(&dp->dl_cb_fattr);
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index f0514d8149cd..4710415790e2 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2691,6 +2691,13 @@ static __net_init int nfsd_net_init(struct net *net)
if (retval)
goto out_repcache_error;
+#ifdef CONFIG_NFSD_V4
+ retval = percpu_counter_init_many(nn->cb_counter, 0, GFP_KERNEL,
+ NFSD_STATS_CB_OPS_NUM);
+ if (retval)
+ goto out_cb_counter_error;
+#endif
+
memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats));
nn->nfsd_svcstats.program = &nfsd_programs[0];
retval = svc_stat_alloc_counts(&nn->nfsd_svcstats);
@@ -2719,6 +2726,10 @@ static __net_init int nfsd_net_init(struct net *net)
out_svcstats_error:
svc_stat_free_counts(&nn->nfsd_svcstats);
out_proc_error:
+#ifdef CONFIG_NFSD_V4
+ percpu_counter_destroy_many(nn->cb_counter, NFSD_STATS_CB_OPS_NUM);
+out_cb_counter_error:
+#endif
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
out_repcache_error:
nfsd_idmap_shutdown(net);
@@ -2759,6 +2770,9 @@ static __net_exit void nfsd_net_exit(struct net *net)
nfsd_net_cb_shutdown(nn);
nfsd_proc_stat_shutdown(net);
svc_stat_free_counts(&nn->nfsd_svcstats);
+#ifdef CONFIG_NFSD_V4
+ percpu_counter_destroy_many(nn->cb_counter, NFSD_STATS_CB_OPS_NUM);
+#endif
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net);
diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c
index f7eaf95e20fc..35d9a5571946 100644
--- a/fs/nfsd/stats.c
+++ b/fs/nfsd/stats.c
@@ -63,7 +63,7 @@ static int nfsd_show(struct seq_file *seq, void *v)
percpu_counter_sum_positive(&nn->counter[NFSD_STATS_NFS4_OP(i)]));
}
seq_printf(seq, "\nwdeleg_getattr %lld",
- percpu_counter_sum_positive(&nn->counter[NFSD_STATS_WDELEG_GETATTR]));
+ percpu_counter_sum_positive(&nn->cb_counter[OP_CB_GETATTR]));
seq_putc(seq, '\n');
#endif
diff --git a/fs/nfsd/stats.h b/fs/nfsd/stats.h
index e4efb0e4e56d..f90cd565f7cb 100644
--- a/fs/nfsd/stats.h
+++ b/fs/nfsd/stats.h
@@ -68,9 +68,10 @@ static inline void nfsd_stats_drc_mem_usage_sub(struct nfsd_net *nn, s64 amount)
}
#ifdef CONFIG_NFSD_V4
-static inline void nfsd_stats_wdeleg_getattr_inc(struct nfsd_net *nn)
+static inline void nfsd_stats_cb_op_inc(struct nfsd_net *nn, u32 opcode)
{
- percpu_counter_inc(&nn->counter[NFSD_STATS_WDELEG_GETATTR]);
+ if (opcode >= OP_CB_GETATTR && opcode <= OP_CB_OFFLOAD)
+ percpu_counter_inc(&nn->cb_counter[opcode]);
}
#endif
#endif /* _NFSD_STATS_H */
--
2.54.0
next prev parent reply other threads:[~2026-06-19 15:26 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-19 15:26 [PATCH v6 0/6] nfsd/sunrpc: convert nfsstat server-side interfaces to use netlink Jeff Layton
2026-06-19 15:26 ` [PATCH v6 1/6] sunrpc: add per-netns per-procedure call counts to svc_stat Jeff Layton
2026-06-19 15:26 ` [PATCH v6 2/6] sunrpc: use per-net counts in svc_seq_show() Jeff Layton
2026-06-19 15:26 ` [PATCH v6 3/6] nfsd: implement server-stats-get netlink handler Jeff Layton
2026-06-19 20:40 ` Chuck Lever
2026-06-19 15:26 ` [PATCH v6 4/6] sunrpc: remove unused svc_version vs_count field Jeff Layton
2026-06-19 15:26 ` Jeff Layton [this message]
2026-06-19 20:41 ` [PATCH v6 5/6] nfsd: count NFSv4 callback operations per netns Chuck Lever
2026-06-19 15:26 ` [PATCH v6 6/6] nfsd: export NFSv4 callback op stats via netlink Jeff Layton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260619-exportd-netlink-v6-5-ddef3499793c@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=anna@kernel.org \
--cc=cel@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=steved@redhat.com \
--cc=tom@talpey.com \
--cc=trondmy@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox