* [PATCH v2 1/2] nfsdctl: add a command to list NFSv4 clients
2026-08-31 9:27 [PATCH v2 0/2] nfsdctl: list NFSv4 clients Prabhakar Pujeri
@ 2026-08-31 9:27 ` Prabhakar Pujeri
2026-08-31 9:27 ` [PATCH v2 2/2] nfsdctl: display per-client NFSv4 state usage Prabhakar Pujeri
1 sibling, 0 replies; 3+ messages in thread
From: Prabhakar Pujeri @ 2026-08-31 9:27 UTC (permalink / raw)
To: steved; +Cc: Prabhakar Pujeri, cel, jlayton, linux-nfs
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.
Decode the peer from separate IPv4 or IPv6 address, port, and scope-ID
attributes. Keep unknown enum values visible as "unknown" so newer
kernels remain usable with this client.
Detect NLM_F_DUMP_INTR on both client records and NLMSG_DONE. Report an
error instead of silently presenting a client list that may have skipped
or repeated a record.
Update the bundled UAPI header and require NFSD_CMD_CLIENT_GET before
selecting a system copy of that header.
Assisted-by: LLM
Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
Changes since v1:
- decode separate address, port, and scope-ID attributes
- reject interrupted dumps and ask the administrator to retry
v1: https://lore.kernel.org/r/b595db7a74fb55fc4c6b3b200c7970e4bb06007b.1787639206.git.prabhakar.pujeri@dell.com
configure.ac | 2 +-
support/include/nfsd_netlink.h | 38 +++++
utils/nfsdctl/nfsdctl.8 | 19 +++
utils/nfsdctl/nfsdctl.adoc | 12 ++
utils/nfsdctl/nfsdctl.c | 255 +++++++++++++++++++++++++++++++++
5 files changed, 325 insertions(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 2ce1e12..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_CMD_SERVER_STATS_GET;]])],
+ [[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 87da1d0..715acb4 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,
@@ -260,6 +280,23 @@ 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_ADDRESS4,
+ NFSD_A_CLIENT_ADDRESS6,
+ NFSD_A_CLIENT_ADDRESS_PORT,
+ NFSD_A_CLIENT_ADDRESS_SCOPE_ID,
+ 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,
@@ -280,6 +317,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 1f526e7..18a7a56 100644
--- a/utils/nfsdctl/nfsdctl.8
+++ b/utils/nfsdctl/nfsdctl.8
@@ -81,6 +81,15 @@ 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. If the client set changes in a way that interrupts the
+dump, the command reports an error and should be retried.
+.RE
+.sp
\fBlistener\fP
.RS 4
Get/set the listening sockets for the server. Run this without arguments to
@@ -259,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 e85348e..d227562 100644
--- a/utils/nfsdctl/nfsdctl.adoc
+++ b/utils/nfsdctl/nfsdctl.adoc
@@ -46,6 +46,14 @@ 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. If the client set changes in a way that interrupts the
+ dump, the command reports an error and should be retried.
+
*listener*::
Get/set the listening sockets for the server. Run this without arguments to
@@ -138,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 c712674..c0a6d5f 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,31 @@ struct server_socket {
bool active;
};
+#define MAX_CLIENT_ADDRESS_LEN 80
+
+struct nfsd_client {
+ char address[MAX_CLIENT_ADDRESS_LEN];
+ struct in_addr address4;
+ struct in6_addr address6;
+ uint64_t clientid;
+ int64_t lease_remaining;
+ uint32_t address_scope_id;
+ uint32_t minor_version;
+ uint32_t state;
+ uint32_t callback_state;
+ uint16_t address_port;
+ bool have_address4;
+ bool have_address6;
+ bool have_address_port;
+ bool reclaim_complete;
+};
+
+struct client_dump_ctx {
+ int *ret;
+ bool header_printed;
+ bool dump_interrupted;
+};
+
#define MAX_NFSD_SOCKETS 256
int nfsd_socket_count;
@@ -184,6 +210,16 @@ static int finish_handler(struct nl_msg *msg, void *arg)
return NL_SKIP;
}
+static int client_finish_handler(struct nl_msg *msg, void *arg)
+{
+ struct client_dump_ctx *ctx = arg;
+
+ if (nlmsg_hdr(msg)->nlmsg_flags & NLM_F_DUMP_INTR)
+ ctx->dump_interrupted = true;
+ *ctx->ret = 0;
+ return NL_SKIP;
+}
+
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
@@ -241,6 +277,145 @@ 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 nfsd_client *client, char *buf,
+ size_t buflen)
+{
+ char host[INET6_ADDRSTRLEN];
+
+ snprintf(buf, buflen, "%s", "unknown");
+ if (!client->have_address_port ||
+ client->have_address4 == client->have_address6)
+ return;
+
+ if (client->have_address4) {
+ if (!inet_ntop(AF_INET, &client->address4, host, sizeof(host)))
+ return;
+ snprintf(buf, buflen, "%s:%u", host,
+ ntohs(client->address_port));
+ return;
+ }
+
+ if (!inet_ntop(AF_INET6, &client->address6, host, sizeof(host)))
+ return;
+ if (client->address_scope_id)
+ snprintf(buf, buflen, "[%s%%%u]:%u", host,
+ client->address_scope_id, ntohs(client->address_port));
+ else
+ snprintf(buf, buflen, "[%s]:%u", host,
+ ntohs(client->address_port));
+}
+
+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_ADDRESS4:
+ if (nla_len(attr) == sizeof(client.address4)) {
+ memcpy(&client.address4, nla_data(attr),
+ sizeof(client.address4));
+ client.have_address4 = true;
+ }
+ break;
+ case NFSD_A_CLIENT_ADDRESS6:
+ if (nla_len(attr) == sizeof(client.address6)) {
+ memcpy(&client.address6, nla_data(attr),
+ sizeof(client.address6));
+ client.have_address6 = true;
+ }
+ break;
+ case NFSD_A_CLIENT_ADDRESS_PORT:
+ if (nla_len(attr) == sizeof(client.address_port)) {
+ memcpy(&client.address_port, nla_data(attr),
+ sizeof(client.address_port));
+ client.have_address_port = true;
+ }
+ break;
+ case NFSD_A_CLIENT_ADDRESS_SCOPE_ID:
+ if (nla_len(attr) == sizeof(client.address_scope_id))
+ client.address_scope_id = nla_get_u32(attr);
+ 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;
+ }
+ }
+ format_client_address(&client, client.address, sizeof(client.address));
+
+ 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;
@@ -410,6 +585,11 @@ 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:
+ if (nlmsg_hdr(msg)->nlmsg_flags & NLM_F_DUMP_INTR)
+ ((struct client_dump_ctx *)arg)->dump_interrupted = true;
+ parse_client_get(gnlh, arg);
+ break;
case LOCKD_CMD_SERVER_GET:
parse_lockd_get(gnlh);
break;
@@ -639,6 +819,76 @@ 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;
+ ctx.ret = &ret;
+ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, client_finish_handler, &ctx);
+ 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;
+ } else if (ctx.dump_interrupted) {
+ xlog(L_ERROR, "NFSv4 client list changed during dump; retry");
+ 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)
@@ -1942,6 +2192,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,
@@ -1954,6 +2205,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"))
@@ -1973,6 +2226,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,
@@ -1997,6 +2251,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] 3+ messages in thread* [PATCH v2 2/2] nfsdctl: display per-client NFSv4 state usage
2026-08-31 9:27 [PATCH v2 0/2] nfsdctl: list NFSv4 clients Prabhakar Pujeri
2026-08-31 9:27 ` [PATCH v2 1/2] nfsdctl: add a command to " Prabhakar Pujeri
@ 2026-08-31 9:27 ` Prabhakar Pujeri
1 sibling, 0 replies; 3+ messages in thread
From: Prabhakar Pujeri @ 2026-08-31 9:27 UTC (permalink / raw)
To: steved; +Cc: Prabhakar Pujeri, cel, jlayton, linux-nfs
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.
Decode both four-byte and eight-byte representations of the Netlink uint
attributes and display the complete u64 value. Display a dash when a
count attribute is absent or malformed. 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.
Assisted-by: LLM
Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
Changes since v1:
- decode 32-bit and 64-bit variable-width uint count attributes
- store and format all counts as uint64_t values
v1: https://lore.kernel.org/r/05399eaee964c52a935f65b2c793f77cb8a2a97a.1787639206.git.prabhakar.pujeri@dell.com
configure.ac | 2 +-
support/include/nfsd_netlink.h | 5 +++
utils/nfsdctl/nfsdctl.8 | 8 +++--
utils/nfsdctl/nfsdctl.adoc | 8 +++--
utils/nfsdctl/nfsdctl.c | 63 +++++++++++++++++++++++++++++++---
5 files changed, 75 insertions(+), 11 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 715acb4..d977833 100644
--- a/support/include/nfsd_netlink.h
+++ b/support/include/nfsd_netlink.h
@@ -292,6 +292,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 18a7a56..e4e823e 100644
--- a/utils/nfsdctl/nfsdctl.8
+++ b/utils/nfsdctl/nfsdctl.8
@@ -85,9 +85,11 @@ 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. If the client set changes in a way that interrupts the
-dump, the command reports an error and should be retried.
+states, lease time remaining, reclaim status, session count, and counts of
+open, lock, delegation, and layout stateids. This subcommand requires elevated
+privileges.
+If the client set changes in a way that interrupts the dump, the command
+reports an error and should be retried.
.RE
.sp
\fBlistener\fP
diff --git a/utils/nfsdctl/nfsdctl.adoc b/utils/nfsdctl/nfsdctl.adoc
index d227562..a25dc0a 100644
--- a/utils/nfsdctl/nfsdctl.adoc
+++ b/utils/nfsdctl/nfsdctl.adoc
@@ -50,9 +50,11 @@ 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. If the client set changes in a way that interrupts the
- dump, the command reports an error and should be retried.
+ states, lease time remaining, reclaim status, session count, and counts of
+ open, lock, delegation, and layout stateids. This subcommand requires elevated
+ privileges.
+ If the client set changes in a way that interrupts the dump, the command
+ reports an error and should be retried.
*listener*::
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index c0a6d5f..096714f 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -101,6 +101,11 @@ struct nfsd_client {
bool have_address4;
bool have_address6;
bool have_address_port;
+ uint64_t sessions;
+ uint64_t open_stateids;
+ uint64_t lock_stateids;
+ uint64_t delegation_stateids;
+ uint64_t layout_stateids;
bool reclaim_complete;
};
@@ -309,6 +314,26 @@ static const char *callback_state_name(uint32_t state)
}
}
+static void parse_client_uint(const struct nlattr *attr, uint64_t *value)
+{
+ switch (nla_len(attr)) {
+ case sizeof(uint32_t):
+ *value = nla_get_u32(attr);
+ break;
+ case sizeof(uint64_t):
+ *value = nla_get_u64(attr);
+ break;
+ }
+}
+
+static void format_client_count(uint64_t count, char *buf, size_t buflen)
+{
+ if (count == UINT64_MAX)
+ snprintf(buf, buflen, "%s", "-");
+ else
+ snprintf(buf, buflen, "%" PRIu64, count);
+}
+
static void format_client_address(const struct nfsd_client *client, char *buf,
size_t buflen)
{
@@ -344,7 +369,13 @@ static void parse_client_get(struct genlmsghdr *gnlh,
.address = "unknown",
.state = UINT32_MAX,
.callback_state = UINT32_MAX,
+ .sessions = UINT64_MAX,
+ .open_stateids = UINT64_MAX,
+ .lock_stateids = UINT64_MAX,
+ .delegation_stateids = UINT64_MAX,
+ .layout_stateids = UINT64_MAX,
};
+ char sessions[21], opens[21], locks[21], delegations[21], layouts[21];
struct nlattr *attr;
int rem;
@@ -394,23 +425,47 @@ 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:
+ parse_client_uint(attr, &client.sessions);
+ break;
+ case NFSD_A_CLIENT_OPEN_STATEIDS:
+ parse_client_uint(attr, &client.open_stateids);
+ break;
+ case NFSD_A_CLIENT_LOCK_STATEIDS:
+ parse_client_uint(attr, &client.lock_stateids);
+ break;
+ case NFSD_A_CLIENT_DELEGATION_STATEIDS:
+ parse_client_uint(attr, &client.delegation_stateids);
+ break;
+ case NFSD_A_CLIENT_LAYOUT_STATEIDS:
+ parse_client_uint(attr, &client.layout_stateids);
+ break;
default:
break;
}
}
format_client_address(&client, client.address, sizeof(client.address));
+ 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));
@@ -822,7 +877,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] 3+ messages in thread