Linux NFS development
 help / color / mirror / Atom feed
* [PATCH v1 0/3] nfsdctl: list NFSv4 clients
@ 2026-08-25  6:52 Prabhakar Pujeri
  2026-08-25  6:52 ` [PATCH v1 1/3] nfsdctl: report NFSv4 grace state Prabhakar Pujeri
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Prabhakar Pujeri @ 2026-08-25  6:52 UTC (permalink / raw)
  To: steved; +Cc: cel, jlayton, linux-nfs, Prabhakar Pujeri

Add the userspace consumer for the companion NFSD Netlink client-state
series, whose cover Message-ID is:

  <cover.1787638668.git.prabhakar.pujeri@dell.com>

The series mirrors the kernel ABI split:

  1. Display the explicit recovery-grace value returned by threads-get.
  2. Add a privileged `nfsdctl clients` table for basic client state.
  3. Extend the table with session and stateid record counts.

Attribute presence remains the compatibility boundary. An older kernel
omits the grace value, and missing count attributes are displayed as
dashes. The bundled UAPI header permits building before the new kernel
header is installed. The configure probe requires the final count
attribute, so a system header containing only the base client command also
falls back to the complete bundled copy.

Validation performed:

  - full nfs-utils build at each applicable patch state
  - final `make check`: no failures; one environment-dependent test skipped
  - bundled nfsd Netlink UAPI compared byte-for-byte with the kernel copy
  - two-vCPU QEMU test with a live NFSv4.2 loopback mount
  - grace yes/no transitions displayed by `nfsdctl threads`
  - live client row showing one session, open stateid, lock stateid, and
    delegation stateid; zero layout stateids
  - callback, reclaim, lease, peer-address, and client-ID fields checked

The series is based on the current nfs-utils master branch. The exact base
is recorded in the base-commit trailer below.

Prabhakar Pujeri (3):
  nfsdctl: report NFSv4 grace state
  nfsdctl: add a command to list NFSv4 clients
  nfsdctl: display per-client NFSv4 state usage

 configure.ac                   |   2 +-
 support/include/nfsd_netlink.h |  41 +++++
 utils/nfsdctl/nfsdctl.8        |  22 ++-
 utils/nfsdctl/nfsdctl.adoc     |  15 +-
 utils/nfsdctl/nfsdctl.c        | 268 +++++++++++++++++++++++++++++++++
 5 files changed, 345 insertions(+), 3 deletions(-)


base-commit: 67ed1bdb1af1c70a5fd3b377a997401b7676b827
-- 
2.54.0

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v1 1/3] nfsdctl: report NFSv4 grace state
  2026-08-25  6:52 [PATCH v1 0/3] nfsdctl: list NFSv4 clients Prabhakar Pujeri
@ 2026-08-25  6:52 ` Prabhakar Pujeri
  2026-08-25  6:52 ` [PATCH v1 2/3] nfsdctl: add a command to list NFSv4 clients Prabhakar Pujeri
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Prabhakar Pujeri @ 2026-08-25  6:52 UTC (permalink / raw)
  To: steved; +Cc: cel, jlayton, linux-nfs, Prabhakar Pujeri

The threads command reports the server's thread configuration, but an
operator also needs to know whether the NFSv4 server is accepting normal
state operations or only recovery requests.

Display the kernel's new in-grace value when the attribute is present.
Checking its presence prevents a new nfsdctl from reporting an older
kernel's lack of the attribute as "no".

Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
 configure.ac                   | 2 +-
 support/include/nfsd_netlink.h | 1 +
 utils/nfsdctl/nfsdctl.8        | 3 ++-
 utils/nfsdctl/nfsdctl.adoc     | 3 ++-
 utils/nfsdctl/nfsdctl.c        | 8 ++++++++
 5 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/configure.ac b/configure.ac
index 2ce1e12..96aa82c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -259,7 +259,7 @@ AC_CHECK_HEADERS(linux/nfsd_netlink.h)
 
 # ensure the system netlink headers have the latest features
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
-			                   [[int foo = NFSD_CMD_SERVER_STATS_GET;]])],
+			                   [[int foo = NFSD_A_SERVER_IN_GRACE;]])],
 			   [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1,
 				      ["Use system's linux/nfsd_netlink.h"])])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/lockd_netlink.h>]],
diff --git a/support/include/nfsd_netlink.h b/support/include/nfsd_netlink.h
index 87da1d0..e1fc2db 100644
--- a/support/include/nfsd_netlink.h
+++ b/support/include/nfsd_netlink.h
@@ -84,6 +84,7 @@ enum {
 	NFSD_A_SERVER_SCOPE,
 	NFSD_A_SERVER_MIN_THREADS,
 	NFSD_A_SERVER_FH_KEY,
+	NFSD_A_SERVER_IN_GRACE,
 
 	__NFSD_A_SERVER_MAX,
 	NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1)
diff --git a/utils/nfsdctl/nfsdctl.8 b/utils/nfsdctl/nfsdctl.8
index 1f526e7..58d5cd9 100644
--- a/utils/nfsdctl/nfsdctl.8
+++ b/utils/nfsdctl/nfsdctl.8
@@ -144,7 +144,8 @@ takes no arguments.
 Get/set the number of running nfsd threads in each pool. Pass a list of
 integers to change the currently active number of threads. Passing it a
 value of 0 will shut down the NFS server. Run this without arguments to
-get the current number of running threads in each pool.
+get the current number of running threads in each pool and whether the
+NFSv4 server is in its recovery grace period.
 .RE
 .sp
 .if n .RS 4
diff --git a/utils/nfsdctl/nfsdctl.adoc b/utils/nfsdctl/nfsdctl.adoc
index e85348e..7e03933 100644
--- a/utils/nfsdctl/nfsdctl.adoc
+++ b/utils/nfsdctl/nfsdctl.adoc
@@ -82,7 +82,8 @@ Each subcommand can also accept its own set of options and arguments. The
   Get/set the number of running nfsd threads in each pool. Pass a list of
   integers to change the currently active number of threads. Passing it a
   value of 0 will shut down the NFS server. Run this without arguments to
-  get the current number of running threads in each pool.
+  get the current number of running threads in each pool and whether the
+  NFSv4 server is in its recovery grace period.
 
 [source,bash]
 ----
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index c712674..2b1faef 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -311,6 +311,8 @@ static void parse_threads_get(struct genlmsghdr *gnlh)
 	struct nlattr *attr;
 	int rem, pools = 0, i = 0;
 	uint32_t *pool_threads = NULL;
+	bool in_grace = false;
+	bool have_in_grace = false;
 
 	nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0),
 			  genlmsg_attrlen(gnlh, 0), rem)
@@ -337,6 +339,10 @@ static void parse_threads_get(struct genlmsghdr *gnlh)
 		case NFSD_A_SERVER_MIN_THREADS:
 			printf("min-threads: %u\n", nla_get_u32(attr));
 			break;
+		case NFSD_A_SERVER_IN_GRACE:
+			in_grace = nla_get_u8(attr);
+			have_in_grace = true;
+			break;
 		default:
 			break;
 		}
@@ -346,6 +352,8 @@ static void parse_threads_get(struct genlmsghdr *gnlh)
 	for (i = 0; i < pools; ++i)
 		printf(" %d", pool_threads[i]);
 	putchar('\n');
+	if (have_in_grace)
+		printf("in-grace: %s\n", in_grace ? "yes" : "no");
 }
 
 static void parse_pool_mode_get(struct genlmsghdr *gnlh)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v1 2/3] nfsdctl: add a command to list NFSv4 clients
  2026-08-25  6:52 [PATCH v1 0/3] nfsdctl: list NFSv4 clients Prabhakar Pujeri
  2026-08-25  6:52 ` [PATCH v1 1/3] nfsdctl: report NFSv4 grace state Prabhakar Pujeri
@ 2026-08-25  6:52 ` Prabhakar Pujeri
  2026-08-25  6:52 ` [PATCH v1 3/3] nfsdctl: display per-client NFSv4 state usage Prabhakar Pujeri
  2026-08-28 14:37 ` [PATCH v1 0/3] nfsdctl: list NFSv4 clients Jeff Layton
  3 siblings, 0 replies; 6+ messages in thread
From: Prabhakar Pujeri @ 2026-08-25  6:52 UTC (permalink / raw)
  To: steved; +Cc: cel, jlayton, linux-nfs, Prabhakar Pujeri

Monitoring NFSv4 clients currently requires walking the nfsd filesystem
and parsing a separate info file for every client. Add a clients command
that consumes the new NFSD_CMD_CLIENT_GET Netlink dump and presents one
row per client.

Report the server-generated client ID, peer address, minor version,
client state, lease time remaining, reclaim status, and callback state.
Format both IPv4 and IPv6 socket addresses, including their ports and
IPv6 scope IDs. Keep unknown enum values visible as "unknown" so newer
kernels remain usable with this client.

Update the bundled UAPI header and require NFSD_CMD_CLIENT_GET before
selecting a system copy of that header.

Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
 configure.ac                   |   2 +-
 support/include/nfsd_netlink.h |  35 ++++++
 utils/nfsdctl/nfsdctl.8        |  18 +++
 utils/nfsdctl/nfsdctl.adoc     |  11 ++
 utils/nfsdctl/nfsdctl.c        | 217 +++++++++++++++++++++++++++++++++
 5 files changed, 282 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 96aa82c..66330d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -259,7 +259,7 @@ AC_CHECK_HEADERS(linux/nfsd_netlink.h)
 
 # ensure the system netlink headers have the latest features
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
-			                   [[int foo = NFSD_A_SERVER_IN_GRACE;]])],
+			                   [[int foo = NFSD_CMD_CLIENT_GET;]])],
 			   [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1,
 				      ["Use system's linux/nfsd_netlink.h"])])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/lockd_netlink.h>]],
diff --git a/support/include/nfsd_netlink.h b/support/include/nfsd_netlink.h
index e1fc2db..ae49538 100644
--- a/support/include/nfsd_netlink.h
+++ b/support/include/nfsd_netlink.h
@@ -50,6 +50,26 @@ enum nfsd_xprtsec_mode {
 	NFSD_XPRTSEC_MODE_MTLS = 4,
 };
 
+/*
+ * State of an NFSv4 client record.
+ */
+enum nfsd_client_state {
+	NFSD_CLIENT_STATE_UNCONFIRMED,
+	NFSD_CLIENT_STATE_ACTIVE,
+	NFSD_CLIENT_STATE_COURTESY,
+	NFSD_CLIENT_STATE_EXPIRABLE,
+};
+
+/*
+ * State of an NFSv4 client's callback channel.
+ */
+enum nfsd_callback_state {
+	NFSD_CALLBACK_STATE_UP,
+	NFSD_CALLBACK_STATE_UNKNOWN,
+	NFSD_CALLBACK_STATE_DOWN,
+	NFSD_CALLBACK_STATE_FAULT,
+};
+
 enum {
 	NFSD_A_CACHE_NOTIFY_CACHE_TYPE = 1,
 
@@ -261,6 +281,20 @@ enum {
 	NFSD_A_SERVER_STATS_MAX = (__NFSD_A_SERVER_STATS_MAX - 1)
 };
 
+enum {
+	NFSD_A_CLIENT_CLIENTID = 1,
+	NFSD_A_CLIENT_PAD,
+	NFSD_A_CLIENT_ADDRESS,
+	NFSD_A_CLIENT_MINOR_VERSION,
+	NFSD_A_CLIENT_STATE,
+	NFSD_A_CLIENT_LEASE_REMAINING,
+	NFSD_A_CLIENT_RECLAIM_COMPLETE,
+	NFSD_A_CLIENT_CALLBACK_STATE,
+
+	__NFSD_A_CLIENT_MAX,
+	NFSD_A_CLIENT_MAX = (__NFSD_A_CLIENT_MAX - 1)
+};
+
 enum {
 	NFSD_CMD_RPC_STATUS_GET = 1,
 	NFSD_CMD_THREADS_SET,
@@ -281,6 +315,7 @@ enum {
 	NFSD_CMD_UNLOCK_FILESYSTEM,
 	NFSD_CMD_UNLOCK_EXPORT,
 	NFSD_CMD_SERVER_STATS_GET,
+	NFSD_CMD_CLIENT_GET,
 
 	__NFSD_CMD_MAX,
 	NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
diff --git a/utils/nfsdctl/nfsdctl.8 b/utils/nfsdctl/nfsdctl.8
index 58d5cd9..36af317 100644
--- a/utils/nfsdctl/nfsdctl.8
+++ b/utils/nfsdctl/nfsdctl.8
@@ -81,6 +81,14 @@ This subcommand takes no arguments. Note that if a "threads=" value is not set i
 nfs.conf, 16 server threads will be brought online.
 .RE
 .sp
+\fBclients\fP
+.RS 4
+List NFSv4 clients known to the server. The output reports each client\(cqs
+server-generated client ID, peer address, minor version, lease and callback
+states, lease time remaining, and reclaim status. This subcommand requires
+elevated privileges.
+.RE
+.sp
 \fBlistener\fP
 .RS 4
 Get/set the listening sockets for the server. Run this without arguments to
@@ -260,6 +268,16 @@ nfsdctl listener
 .fi
 .if n .RE
 .sp
+Show the NFSv4 clients known to the server:
+.sp
+.if n .RS 4
+.nf
+.fam C
+nfsdctl clients
+.fam
+.fi
+.if n .RE
+.sp
 Show the supported and enabled NFS versions:
 .sp
 .if n .RS 4
diff --git a/utils/nfsdctl/nfsdctl.adoc b/utils/nfsdctl/nfsdctl.adoc
index 7e03933..3e70b99 100644
--- a/utils/nfsdctl/nfsdctl.adoc
+++ b/utils/nfsdctl/nfsdctl.adoc
@@ -46,6 +46,13 @@ Each subcommand can also accept its own set of options and arguments. The
   This subcommand takes no arguments. Note that if a "threads=" value is not set in
   nfs.conf, 16 server threads will be brought online.
 
+*clients*::
+
+  List NFSv4 clients known to the server. The output reports each client's
+  server-generated client ID, peer address, minor version, lease and callback
+  states, lease time remaining, and reclaim status. This subcommand requires
+  elevated privileges.
+
 *listener*::
 
   Get/set the listening sockets for the server. Run this without arguments to
@@ -139,6 +146,10 @@ Get a list of current listening sockets:
 
   nfsdctl listener
 
+Show the NFSv4 clients known to the server:
+
+  nfsdctl clients
+
 Show the supported and enabled NFS versions:
 
   nfsdctl version
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index 2b1faef..7f12860 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -8,6 +8,7 @@
 #include <linux/version.h>
 #include <netlink/genl/genl.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -84,6 +85,22 @@ struct server_socket {
 	bool active;
 };
 
+#define MAX_CLIENT_ADDRESS_LEN	80
+
+struct nfsd_client {
+	char		address[MAX_CLIENT_ADDRESS_LEN];
+	uint64_t	clientid;
+	int64_t		lease_remaining;
+	uint32_t	minor_version;
+	uint32_t	state;
+	uint32_t	callback_state;
+	bool		reclaim_complete;
+};
+
+struct client_dump_ctx {
+	bool header_printed;
+};
+
 #define MAX_NFSD_SOCKETS		256
 
 int nfsd_socket_count;
@@ -241,6 +258,132 @@ static void parse_rpc_status_get(struct genlmsghdr *gnlh)
 	printf("\n");
 }
 
+static const char *client_state_name(uint32_t state)
+{
+	switch (state) {
+	case NFSD_CLIENT_STATE_UNCONFIRMED:
+		return "unconfirmed";
+	case NFSD_CLIENT_STATE_ACTIVE:
+		return "active";
+	case NFSD_CLIENT_STATE_COURTESY:
+		return "courtesy";
+	case NFSD_CLIENT_STATE_EXPIRABLE:
+		return "expirable";
+	default:
+		return "unknown";
+	}
+}
+
+static const char *callback_state_name(uint32_t state)
+{
+	switch (state) {
+	case NFSD_CALLBACK_STATE_UP:
+		return "up";
+	case NFSD_CALLBACK_STATE_UNKNOWN:
+		return "unknown";
+	case NFSD_CALLBACK_STATE_DOWN:
+		return "down";
+	case NFSD_CALLBACK_STATE_FAULT:
+		return "fault";
+	default:
+		return "unknown";
+	}
+}
+
+static void format_client_address(const struct nlattr *attr, char *buf,
+				  size_t buflen)
+{
+	const struct sockaddr *sa = nla_data(attr);
+	char host[INET6_ADDRSTRLEN];
+
+	snprintf(buf, buflen, "%s", "unknown");
+	if (nla_len(attr) < sizeof(sa->sa_family))
+		return;
+
+	switch (sa->sa_family) {
+	case AF_INET: {
+		const struct sockaddr_in *sin = nla_data(attr);
+
+		if (nla_len(attr) < sizeof(*sin) ||
+		    !inet_ntop(AF_INET, &sin->sin_addr, host, sizeof(host)))
+			return;
+		snprintf(buf, buflen, "%s:%u", host, ntohs(sin->sin_port));
+		break;
+	}
+	case AF_INET6: {
+		const struct sockaddr_in6 *sin6 = nla_data(attr);
+
+		if (nla_len(attr) < sizeof(*sin6) ||
+		    !inet_ntop(AF_INET6, &sin6->sin6_addr, host, sizeof(host)))
+			return;
+		if (sin6->sin6_scope_id)
+			snprintf(buf, buflen, "[%s%%%u]:%u", host,
+				 sin6->sin6_scope_id, ntohs(sin6->sin6_port));
+		else
+			snprintf(buf, buflen, "[%s]:%u", host,
+				 ntohs(sin6->sin6_port));
+		break;
+	}
+	}
+}
+
+static void parse_client_get(struct genlmsghdr *gnlh,
+			     struct client_dump_ctx *ctx)
+{
+	struct nfsd_client client = {
+		.address = "unknown",
+		.state = UINT32_MAX,
+		.callback_state = UINT32_MAX,
+	};
+	struct nlattr *attr;
+	int rem;
+
+	nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0),
+			  genlmsg_attrlen(gnlh, 0), rem) {
+		switch (nla_type(attr)) {
+		case NFSD_A_CLIENT_CLIENTID:
+			client.clientid = nla_get_u64(attr);
+			break;
+		case NFSD_A_CLIENT_ADDRESS:
+			format_client_address(attr, client.address,
+					      sizeof(client.address));
+			break;
+		case NFSD_A_CLIENT_MINOR_VERSION:
+			client.minor_version = nla_get_u32(attr);
+			break;
+		case NFSD_A_CLIENT_STATE:
+			client.state = nla_get_u32(attr);
+			break;
+		case NFSD_A_CLIENT_LEASE_REMAINING:
+			client.lease_remaining = nla_get_s64(attr);
+			break;
+		case NFSD_A_CLIENT_RECLAIM_COMPLETE:
+			client.reclaim_complete = true;
+			break;
+		case NFSD_A_CLIENT_CALLBACK_STATE:
+			client.callback_state = nla_get_u32(attr);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!ctx->header_printed) {
+		printf("%-18s %-47s %-4s %-11s %8s %7s %8s\n",
+		       "clientid", "address", "vers", "state", "lease",
+		       "reclaim", "callback");
+		ctx->header_printed = true;
+	}
+
+	printf("0x%016" PRIx64 " %-47s 4.%-2u %-11s %8" PRId64
+	       " %7s %8s\n",
+	       client.clientid, client.address, client.minor_version,
+	       client_state_name(client.state), client.lease_remaining,
+	       client.minor_version ?
+		(client.reclaim_complete ? "yes" : "no") : "-",
+	       callback_state_name(client.callback_state));
+}
+
 static void parse_version_get(struct genlmsghdr *gnlh)
 {
 	struct nlattr *attr;
@@ -418,6 +561,9 @@ static int recv_handler(struct nl_msg *msg, void *arg)
 	case NFSD_CMD_POOL_MODE_GET:
 		parse_pool_mode_get(gnlh);
 		break;
+	case NFSD_CMD_CLIENT_GET:
+		parse_client_get(gnlh, arg);
+		break;
 	case LOCKD_CMD_SERVER_GET:
 		parse_lockd_get(gnlh);
 		break;
@@ -647,6 +793,72 @@ out:
 	return ret;
 }
 
+static void clients_usage(void)
+{
+	printf("Usage: %s clients\n", taskname);
+	printf("    Display NFSv4 clients and their current state.\n");
+}
+
+static int clients_func(struct nl_sock *sock, int argc, char **argv)
+{
+	struct client_dump_ctx ctx = {};
+	struct genlmsghdr *ghdr;
+	struct nlmsghdr *nlh;
+	struct nl_msg *msg;
+	struct nl_cb *cb;
+	int opt, ret;
+
+	optind = 1;
+	while ((opt = getopt_long(argc, argv, "h", help_only_options, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			clients_usage();
+			return 0;
+		}
+	}
+
+	if (!nfsd_nl_family_setup(sock))
+		return 1;
+
+	msg = netlink_msg_alloc(sock, nfsd_nl_family);
+	if (!msg)
+		return 1;
+
+	nlh = nlmsg_hdr(msg);
+	nlh->nlmsg_flags |= NLM_F_DUMP;
+	ghdr = nlmsg_data(nlh);
+	ghdr->cmd = NFSD_CMD_CLIENT_GET;
+
+	cb = nl_cb_alloc(NL_CB_CUSTOM);
+	if (!cb) {
+		xlog(L_ERROR, "failed to allocate netlink callbacks");
+		ret = 1;
+		goto out;
+	}
+
+	ret = nl_send_auto(sock, msg);
+	if (ret < 0)
+		goto out_cb;
+
+	ret = 1;
+	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
+	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &ret);
+	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, recv_handler, &ctx);
+
+	while (ret > 0)
+		nl_recvmsgs(sock, cb);
+	if (ret < 0) {
+		xlog(L_ERROR, "Error: %s", strerror(-ret));
+		ret = 1;
+	}
+out_cb:
+	nl_cb_put(cb);
+out:
+	nlmsg_free(msg);
+	return ret;
+}
+
 static int threads_doit(struct nl_sock *sock, int cmd, int grace, int lease,
 			int pool_count, int *pool_threads, char *scope, int minthreads,
 			uuid_t fh_key)
@@ -1950,6 +2162,7 @@ static int nlm_func(struct nl_sock *sock, int argc, char ** argv)
 
 enum nfsdctl_commands {
 	NFSDCTL_STATUS,
+	NFSDCTL_CLIENTS,
 	NFSDCTL_THREADS,
 	NFSDCTL_VERSION,
 	NFSDCTL_LISTENER,
@@ -1962,6 +2175,8 @@ static int parse_command(char *str)
 {
 	if (!strcmp(str, "status"))
 		return NFSDCTL_STATUS;
+	if (!strcmp(str, "clients"))
+		return NFSDCTL_CLIENTS;
 	if (!strcmp(str, "threads"))
 		return NFSDCTL_THREADS;
 	if (!strcmp(str, "version"))
@@ -1981,6 +2196,7 @@ typedef int (*nfsdctl_func)(struct nl_sock *sock, int argc, char **argv);
 
 static nfsdctl_func func[] = {
 	[NFSDCTL_STATUS] = status_func,
+	[NFSDCTL_CLIENTS] = clients_func,
 	[NFSDCTL_THREADS] = threads_func,
 	[NFSDCTL_VERSION] = version_func,
 	[NFSDCTL_LISTENER] = listener_func,
@@ -2005,6 +2221,7 @@ static void usage(void)
 	printf("    threads              get/set nfsd thread settings\n");
 	printf("    nlm                  get current nlm settings\n");
 	printf("    status               get current RPC processing info\n");
+	printf("    clients              get current NFSv4 client information\n");
 	printf("    autostart            start server with settings from /etc/nfs.conf\n");
 }
 
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v1 3/3] nfsdctl: display per-client NFSv4 state usage
  2026-08-25  6:52 [PATCH v1 0/3] nfsdctl: list NFSv4 clients Prabhakar Pujeri
  2026-08-25  6:52 ` [PATCH v1 1/3] nfsdctl: report NFSv4 grace state Prabhakar Pujeri
  2026-08-25  6:52 ` [PATCH v1 2/3] nfsdctl: add a command to list NFSv4 clients Prabhakar Pujeri
@ 2026-08-25  6:52 ` Prabhakar Pujeri
  2026-08-28 14:37 ` [PATCH v1 0/3] nfsdctl: list NFSv4 clients Jeff Layton
  3 siblings, 0 replies; 6+ messages in thread
From: Prabhakar Pujeri @ 2026-08-25  6:52 UTC (permalink / raw)
  To: steved; +Cc: cel, jlayton, linux-nfs, Prabhakar Pujeri

Extend the clients table with the session and stateid record counts
exported by NFSD. These totals help an operator identify which client is
retaining server-side state before using the detailed nfsd filesystem
views.

Display a dash when a count attribute is absent. This keeps the command
accurate when a new nfsdctl is used with a kernel that supports the base
client dump but not the count extension. Require the last count attribute
before selecting the system UAPI header so building against a header with
only the base command still uses the bundled copy.

Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
 configure.ac                   |  2 +-
 support/include/nfsd_netlink.h |  5 ++++
 utils/nfsdctl/nfsdctl.8        |  5 ++--
 utils/nfsdctl/nfsdctl.adoc     |  5 ++--
 utils/nfsdctl/nfsdctl.c        | 51 +++++++++++++++++++++++++++++++---
 5 files changed, 59 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 66330d0..0b22523 100644
--- a/configure.ac
+++ b/configure.ac
@@ -259,7 +259,7 @@ AC_CHECK_HEADERS(linux/nfsd_netlink.h)
 
 # ensure the system netlink headers have the latest features
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
-			                   [[int foo = NFSD_CMD_CLIENT_GET;]])],
+			                   [[int foo = NFSD_A_CLIENT_LAYOUT_STATEIDS;]])],
 			   [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1,
 				      ["Use system's linux/nfsd_netlink.h"])])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/lockd_netlink.h>]],
diff --git a/support/include/nfsd_netlink.h b/support/include/nfsd_netlink.h
index ae49538..49c9531 100644
--- a/support/include/nfsd_netlink.h
+++ b/support/include/nfsd_netlink.h
@@ -290,6 +290,11 @@ enum {
 	NFSD_A_CLIENT_LEASE_REMAINING,
 	NFSD_A_CLIENT_RECLAIM_COMPLETE,
 	NFSD_A_CLIENT_CALLBACK_STATE,
+	NFSD_A_CLIENT_SESSIONS,
+	NFSD_A_CLIENT_OPEN_STATEIDS,
+	NFSD_A_CLIENT_LOCK_STATEIDS,
+	NFSD_A_CLIENT_DELEGATION_STATEIDS,
+	NFSD_A_CLIENT_LAYOUT_STATEIDS,
 
 	__NFSD_A_CLIENT_MAX,
 	NFSD_A_CLIENT_MAX = (__NFSD_A_CLIENT_MAX - 1)
diff --git a/utils/nfsdctl/nfsdctl.8 b/utils/nfsdctl/nfsdctl.8
index 36af317..3850461 100644
--- a/utils/nfsdctl/nfsdctl.8
+++ b/utils/nfsdctl/nfsdctl.8
@@ -85,8 +85,9 @@ nfs.conf, 16 server threads will be brought online.
 .RS 4
 List NFSv4 clients known to the server. The output reports each client\(cqs
 server-generated client ID, peer address, minor version, lease and callback
-states, lease time remaining, and reclaim status. This subcommand requires
-elevated privileges.
+states, lease time remaining, reclaim status, session count, and counts of
+open, lock, delegation, and layout stateids. This subcommand requires elevated
+privileges.
 .RE
 .sp
 \fBlistener\fP
diff --git a/utils/nfsdctl/nfsdctl.adoc b/utils/nfsdctl/nfsdctl.adoc
index 3e70b99..55575de 100644
--- a/utils/nfsdctl/nfsdctl.adoc
+++ b/utils/nfsdctl/nfsdctl.adoc
@@ -50,8 +50,9 @@ Each subcommand can also accept its own set of options and arguments. The
 
   List NFSv4 clients known to the server. The output reports each client's
   server-generated client ID, peer address, minor version, lease and callback
-  states, lease time remaining, and reclaim status. This subcommand requires
-  elevated privileges.
+  states, lease time remaining, reclaim status, session count, and counts of
+  open, lock, delegation, and layout stateids. This subcommand requires elevated
+  privileges.
 
 *listener*::
 
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index 7f12860..9e5505d 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -94,6 +94,11 @@ struct nfsd_client {
 	uint32_t	minor_version;
 	uint32_t	state;
 	uint32_t	callback_state;
+	uint32_t	sessions;
+	uint32_t	open_stateids;
+	uint32_t	lock_stateids;
+	uint32_t	delegation_stateids;
+	uint32_t	layout_stateids;
 	bool		reclaim_complete;
 };
 
@@ -290,6 +295,14 @@ static const char *callback_state_name(uint32_t state)
 	}
 }
 
+static void format_client_count(uint32_t count, char *buf, size_t buflen)
+{
+	if (count == UINT32_MAX)
+		snprintf(buf, buflen, "%s", "-");
+	else
+		snprintf(buf, buflen, "%u", count);
+}
+
 static void format_client_address(const struct nlattr *attr, char *buf,
 				  size_t buflen)
 {
@@ -334,7 +347,13 @@ static void parse_client_get(struct genlmsghdr *gnlh,
 		.address = "unknown",
 		.state = UINT32_MAX,
 		.callback_state = UINT32_MAX,
+		.sessions = UINT32_MAX,
+		.open_stateids = UINT32_MAX,
+		.lock_stateids = UINT32_MAX,
+		.delegation_stateids = UINT32_MAX,
+		.layout_stateids = UINT32_MAX,
 	};
+	char sessions[11], opens[11], locks[11], delegations[11], layouts[11];
 	struct nlattr *attr;
 	int rem;
 
@@ -363,22 +382,46 @@ static void parse_client_get(struct genlmsghdr *gnlh,
 		case NFSD_A_CLIENT_CALLBACK_STATE:
 			client.callback_state = nla_get_u32(attr);
 			break;
+		case NFSD_A_CLIENT_SESSIONS:
+			client.sessions = nla_get_u32(attr);
+			break;
+		case NFSD_A_CLIENT_OPEN_STATEIDS:
+			client.open_stateids = nla_get_u32(attr);
+			break;
+		case NFSD_A_CLIENT_LOCK_STATEIDS:
+			client.lock_stateids = nla_get_u32(attr);
+			break;
+		case NFSD_A_CLIENT_DELEGATION_STATEIDS:
+			client.delegation_stateids = nla_get_u32(attr);
+			break;
+		case NFSD_A_CLIENT_LAYOUT_STATEIDS:
+			client.layout_stateids = nla_get_u32(attr);
+			break;
 		default:
 			break;
 		}
 	}
 
+	format_client_count(client.sessions, sessions, sizeof(sessions));
+	format_client_count(client.open_stateids, opens, sizeof(opens));
+	format_client_count(client.lock_stateids, locks, sizeof(locks));
+	format_client_count(client.delegation_stateids, delegations,
+			    sizeof(delegations));
+	format_client_count(client.layout_stateids, layouts, sizeof(layouts));
+
 	if (!ctx->header_printed) {
-		printf("%-18s %-47s %-4s %-11s %8s %7s %8s\n",
+		printf("%-18s %-47s %-4s %-11s %8s %5s %5s %5s %5s %6s %7s %8s\n",
 		       "clientid", "address", "vers", "state", "lease",
-		       "reclaim", "callback");
+		       "sess", "open", "lock", "deleg", "layout", "reclaim",
+		       "callback");
 		ctx->header_printed = true;
 	}
 
 	printf("0x%016" PRIx64 " %-47s 4.%-2u %-11s %8" PRId64
-	       " %7s %8s\n",
+	       " %5s %5s %5s %5s %6s %7s %8s\n",
 	       client.clientid, client.address, client.minor_version,
 	       client_state_name(client.state), client.lease_remaining,
+	       sessions, opens, locks, delegations, layouts,
 	       client.minor_version ?
 		(client.reclaim_complete ? "yes" : "no") : "-",
 	       callback_state_name(client.callback_state));
@@ -796,7 +839,7 @@ out:
 static void clients_usage(void)
 {
 	printf("Usage: %s clients\n", taskname);
-	printf("    Display NFSv4 clients and their current state.\n");
+	printf("    Display NFSv4 clients and their current state usage.\n");
 }
 
 static int clients_func(struct nl_sock *sock, int argc, char **argv)
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v1 0/3] nfsdctl: list NFSv4 clients
  2026-08-25  6:52 [PATCH v1 0/3] nfsdctl: list NFSv4 clients Prabhakar Pujeri
                   ` (2 preceding siblings ...)
  2026-08-25  6:52 ` [PATCH v1 3/3] nfsdctl: display per-client NFSv4 state usage Prabhakar Pujeri
@ 2026-08-28 14:37 ` Jeff Layton
  2026-08-29 21:28   ` Steve Dickson
  3 siblings, 1 reply; 6+ messages in thread
From: Jeff Layton @ 2026-08-28 14:37 UTC (permalink / raw)
  To: Prabhakar Pujeri, steved; +Cc: cel, linux-nfs

On Tue, 2026-08-25 at 06:52 +0000, Prabhakar Pujeri wrote:
> Add the userspace consumer for the companion NFSD Netlink client-state
> series, whose cover Message-ID is:
> 
>   <cover.1787638668.git.prabhakar.pujeri@dell.com>
> 
> The series mirrors the kernel ABI split:
> 
>   1. Display the explicit recovery-grace value returned by threads-get.
>   2. Add a privileged `nfsdctl clients` table for basic client state.
>   3. Extend the table with session and stateid record counts.
> 
> Attribute presence remains the compatibility boundary. An older kernel
> omits the grace value, and missing count attributes are displayed as
> dashes. The bundled UAPI header permits building before the new kernel
> header is installed. The configure probe requires the final count
> attribute, so a system header containing only the base client command also
> falls back to the complete bundled copy.
> 
> Validation performed:
> 
>   - full nfs-utils build at each applicable patch state
>   - final `make check`: no failures; one environment-dependent test skipped
>   - bundled nfsd Netlink UAPI compared byte-for-byte with the kernel copy
>   - two-vCPU QEMU test with a live NFSv4.2 loopback mount
>   - grace yes/no transitions displayed by `nfsdctl threads`
>   - live client row showing one session, open stateid, lock stateid, and
>     delegation stateid; zero layout stateids
>   - callback, reclaim, lease, peer-address, and client-ID fields checked
> 
> The series is based on the current nfs-utils master branch. The exact base
> is recorded in the base-commit trailer below.
> 
> Prabhakar Pujeri (3):
>   nfsdctl: report NFSv4 grace state
>   nfsdctl: add a command to list NFSv4 clients
>   nfsdctl: display per-client NFSv4 state usage
> 
>  configure.ac                   |   2 +-
>  support/include/nfsd_netlink.h |  41 +++++
>  utils/nfsdctl/nfsdctl.8        |  22 ++-
>  utils/nfsdctl/nfsdctl.adoc     |  15 +-
>  utils/nfsdctl/nfsdctl.c        | 268 +++++++++++++++++++++++++++++++++
>  5 files changed, 345 insertions(+), 3 deletions(-)
> 
> 
> base-commit: 67ed1bdb1af1c70a5fd3b377a997401b7676b827

Sashiko has some comments about this that probably need to be addressed
before we apply these. There is potential for a softlockup in patch #3,
in particular:

https://sashiko.dev/#/patchset/cover.1787638668.git.prabhakar.pujeri%40dell.com
-- 
Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v1 0/3] nfsdctl: list NFSv4 clients
  2026-08-28 14:37 ` [PATCH v1 0/3] nfsdctl: list NFSv4 clients Jeff Layton
@ 2026-08-29 21:28   ` Steve Dickson
  0 siblings, 0 replies; 6+ messages in thread
From: Steve Dickson @ 2026-08-29 21:28 UTC (permalink / raw)
  To: Jeff Layton, Prabhakar Pujeri; +Cc: cel, linux-nfs



On 8/28/26 10:37 AM, Jeff Layton wrote:
> On Tue, 2026-08-25 at 06:52 +0000, Prabhakar Pujeri wrote:
>> Add the userspace consumer for the companion NFSD Netlink client-state
>> series, whose cover Message-ID is:
>>
>>    <cover.1787638668.git.prabhakar.pujeri@dell.com>
>>
>> The series mirrors the kernel ABI split:
>>
>>    1. Display the explicit recovery-grace value returned by threads-get.
>>    2. Add a privileged `nfsdctl clients` table for basic client state.
>>    3. Extend the table with session and stateid record counts.
>>
>> Attribute presence remains the compatibility boundary. An older kernel
>> omits the grace value, and missing count attributes are displayed as
>> dashes. The bundled UAPI header permits building before the new kernel
>> header is installed. The configure probe requires the final count
>> attribute, so a system header containing only the base client command also
>> falls back to the complete bundled copy.
>>
>> Validation performed:
>>
>>    - full nfs-utils build at each applicable patch state
>>    - final `make check`: no failures; one environment-dependent test skipped
>>    - bundled nfsd Netlink UAPI compared byte-for-byte with the kernel copy
>>    - two-vCPU QEMU test with a live NFSv4.2 loopback mount
>>    - grace yes/no transitions displayed by `nfsdctl threads`
>>    - live client row showing one session, open stateid, lock stateid, and
>>      delegation stateid; zero layout stateids
>>    - callback, reclaim, lease, peer-address, and client-ID fields checked
>>
>> The series is based on the current nfs-utils master branch. The exact base
>> is recorded in the base-commit trailer below.
>>
>> Prabhakar Pujeri (3):
>>    nfsdctl: report NFSv4 grace state
>>    nfsdctl: add a command to list NFSv4 clients
>>    nfsdctl: display per-client NFSv4 state usage
>>
>>   configure.ac                   |   2 +-
>>   support/include/nfsd_netlink.h |  41 +++++
>>   utils/nfsdctl/nfsdctl.8        |  22 ++-
>>   utils/nfsdctl/nfsdctl.adoc     |  15 +-
>>   utils/nfsdctl/nfsdctl.c        | 268 +++++++++++++++++++++++++++++++++
>>   5 files changed, 345 insertions(+), 3 deletions(-)
>>
>>
>> base-commit: 67ed1bdb1af1c70a5fd3b377a997401b7676b827
> 
> Sashiko has some comments about this that probably need to be addressed
> before we apply these. There is potential for a softlockup in patch #3,
> in particular:
> 
> https://sashiko.dev/#/patchset/cover.1787638668.git.prabhakar.pujeri%40dell.com
Okay... I'll wait to apply them until things get sorted out... but
I will keep them on my todo list...

steved.


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-08-29 21:28 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25  6:52 [PATCH v1 0/3] nfsdctl: list NFSv4 clients Prabhakar Pujeri
2026-08-25  6:52 ` [PATCH v1 1/3] nfsdctl: report NFSv4 grace state Prabhakar Pujeri
2026-08-25  6:52 ` [PATCH v1 2/3] nfsdctl: add a command to list NFSv4 clients Prabhakar Pujeri
2026-08-25  6:52 ` [PATCH v1 3/3] nfsdctl: display per-client NFSv4 state usage Prabhakar Pujeri
2026-08-28 14:37 ` [PATCH v1 0/3] nfsdctl: list NFSv4 clients Jeff Layton
2026-08-29 21:28   ` Steve Dickson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox