* [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads
@ 2026-02-04 12:28 Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 1/3] nfsdctl: only resolve netlink family names once Jeff Layton
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Jeff Layton @ 2026-02-04 12:28 UTC (permalink / raw)
To: Steve Dickson; +Cc: Ben Coddington, Chuck Lever, linux-nfs, Jeff Layton
Ben reported a problem with using new userland with old kernel. If he
tried to send down a setting that the kernel doesn't support, it returns
-EINVAL to the call.
This patch series adds a mechanism for nfsdctl to tell what attributes
are supported by the "threads" command. If can then use that to
determine whether to pass down the min-threads attribute or report an
error or warning.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
Jeff Layton (3):
nfsdctl: only resolve netlink family names once
nfsdctl: query netlink policy before sending the minthreads attribute to kernel
nfsdctl: remove unneeded newlines from xlog() format strings
utils/nfsdctl/nfsdctl.c | 193 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 168 insertions(+), 25 deletions(-)
---
base-commit: 8f54511aefe1455161a6c4406ed8c770139f61e3
change-id: 20260203-minthreads-402ce87096e0
Best regards,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH nfs-utils 1/3] nfsdctl: only resolve netlink family names once
2026-02-04 12:28 [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
@ 2026-02-04 12:28 ` Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 2/3] nfsdctl: query netlink policy before sending the minthreads attribute to kernel Jeff Layton
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-02-04 12:28 UTC (permalink / raw)
To: Steve Dickson; +Cc: Ben Coddington, Chuck Lever, linux-nfs, Jeff Layton
The current code resolves the string name to an id for every netlink
call. Just resolve the family names once and keep them.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
utils/nfsdctl/nfsdctl.c | 83 ++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 64 insertions(+), 19 deletions(-)
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index c906a2c8ba6d357e182d341a30610e367e74c093..0d624d4de6016e3c9189d488736eb1b9b2f6fe6e 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -46,9 +46,11 @@
#include "conffile.h"
#include "xlog.h"
-/* compile note:
- * gcc -I/usr/include/libnl3/ -o <prog-name> <prog-name>.c -lnl-3 -lnl-genl-3
- */
+/* The index of the "lockd" netlink family */
+static int lockd_nl_family;
+
+/* The index of the "nfsd" netlink family */
+static int nfsd_nl_family;
struct nfs_version {
uint8_t major;
@@ -435,24 +437,18 @@ static struct nl_sock *netlink_sock_alloc(void)
return sock;
}
-static struct nl_msg *netlink_msg_alloc(struct nl_sock *sock, const char *family)
+static struct nl_msg *netlink_msg_alloc(struct nl_sock *sock, int family)
{
struct nl_msg *msg;
int id;
- id = genl_ctrl_resolve(sock, family);
- if (id < 0) {
- xlog(L_ERROR, "failed to resolve %s generic netlink family", family);
- return NULL;
- }
-
msg = nlmsg_alloc();
if (!msg) {
xlog(L_ERROR, "failed to allocate netlink message");
return NULL;
}
- if (!genlmsg_put(msg, 0, 0, id, 0, 0, 0, 0)) {
+ if (!genlmsg_put(msg, 0, 0, family, 0, 0, 0, 0)) {
xlog(L_ERROR, "failed to add generic netlink headers to netlink message");
nlmsg_free(msg);
return NULL;
@@ -461,6 +457,31 @@ static struct nl_msg *netlink_msg_alloc(struct nl_sock *sock, const char *family
return msg;
}
+static int resolve_family(struct nl_sock *sock, const char *name)
+{
+ int family = genl_ctrl_resolve(sock, name);
+
+ if (family < 0) {
+ xlog(L_ERROR, "failed to resolve %s generic netlink family: %d", name, family);
+ family = 0;
+ }
+ return family;
+}
+
+static int lockd_nl_family_setup(struct nl_sock *sock)
+{
+ if (!lockd_nl_family)
+ lockd_nl_family = resolve_family(sock, LOCKD_FAMILY_NAME);
+ return lockd_nl_family;
+}
+
+static int nfsd_nl_family_setup(struct nl_sock *sock)
+{
+ if (!nfsd_nl_family)
+ nfsd_nl_family = resolve_family(sock, NFSD_FAMILY_NAME);
+ return nfsd_nl_family;
+}
+
static void status_usage(void)
{
printf("Usage: %s status\n", taskname);
@@ -484,7 +505,10 @@ static int status_func(struct nl_sock *sock, int argc, char ** argv)
}
}
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -532,7 +556,10 @@ static int threads_doit(struct nl_sock *sock, int cmd, int grace, int lease,
struct nl_cb *cb;
int ret;
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -674,7 +701,10 @@ static int fetch_nfsd_versions(struct nl_sock *sock)
struct nl_cb *cb;
int ret;
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -739,7 +769,10 @@ static int set_nfsd_versions(struct nl_sock *sock)
struct nl_cb *cb;
int i, ret;
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -920,7 +953,10 @@ static int fetch_current_listeners(struct nl_sock *sock)
struct nl_cb *cb;
int ret;
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -1165,7 +1201,10 @@ static int set_listeners(struct nl_sock *sock)
struct nl_cb *cb;
int i, ret;
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -1286,7 +1325,10 @@ static int pool_mode_doit(struct nl_sock *sock, int cmd, const char *pool_mode)
struct nl_cb *cb;
int ret;
- msg = netlink_msg_alloc(sock, NFSD_FAMILY_NAME);
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, nfsd_nl_family);
if (!msg)
return 1;
@@ -1379,7 +1421,10 @@ static int lockd_config_doit(struct nl_sock *sock, int cmd, int grace, int tcppo
return 0;
}
- msg = netlink_msg_alloc(sock, LOCKD_FAMILY_NAME);
+ if (!lockd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, lockd_nl_family);
if (!msg)
return 1;
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH nfs-utils 2/3] nfsdctl: query netlink policy before sending the minthreads attribute to kernel
2026-02-04 12:28 [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 1/3] nfsdctl: only resolve netlink family names once Jeff Layton
@ 2026-02-04 12:28 ` Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 3/3] nfsdctl: remove unneeded newlines from xlog() format strings Jeff Layton
2026-02-05 21:18 ` [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Benjamin Coddington
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-02-04 12:28 UTC (permalink / raw)
To: Steve Dickson; +Cc: Ben Coddington, Chuck Lever, linux-nfs, Jeff Layton
Ben reported a problem when using new nfs-utils with an old kernel that
doesn't support the min-threads setting. While netlink is an extensible
format, genetlink (which we are using) will reject unknown attributes by
default with -EINVAL.
We could fix this in the kernel by having it ignore unknown attributes,
but there is no way to fix old kernels and silently ignoring it is less
than ideal. By handling this in userland, we can properly error out when
the kernel doesn't support this attribute.
When starting, have nfsdctl query the kernel for the "policy" of the
threads operation, and determine the highest attribute index it
supports. For the "threads" command, have it fail if the --min-threads
option is passed and the kernel doesn't support it. For "autostart", log
a warning and ignore the setting.
Fixes: 00e2e62b8998 ("nfsdctl: add support for min-threads parameter")
Reported-by: Ben Coddington <bcodding@hammerspace.com>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
utils/nfsdctl/nfsdctl.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 99 insertions(+), 1 deletion(-)
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index 0d624d4de6016e3c9189d488736eb1b9b2f6fe6e..68c93039f3440a73285fd3f1e8b1131c1c945efb 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -52,6 +52,9 @@ static int lockd_nl_family;
/* The index of the "nfsd" netlink family */
static int nfsd_nl_family;
+/* The highest attribute index supported by NFSD_CMD_THREADS_SET on this kernel */
+int nfsd_threads_max_nlattr;
+
struct nfs_version {
uint8_t major;
uint8_t minor;
@@ -482,6 +485,83 @@ static int nfsd_nl_family_setup(struct nl_sock *sock)
return nfsd_nl_family;
}
+static int getpolicy_handler(struct nl_msg *msg, void *arg)
+{
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *attr;
+ int rem;
+
+ nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), rem) {
+ struct nlattr *a, *b;
+ int i, j, index;
+
+ if (nla_type(attr) == CTRL_ATTR_POLICY) {
+ nla_for_each_nested(a, attr, i) {
+ nla_for_each_nested(b, a, j) {
+ int idx = nla_type(b);
+
+ if (nfsd_threads_max_nlattr < idx)
+ nfsd_threads_max_nlattr = idx;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int query_nfsd_nl_policy(struct nl_sock *sock)
+{
+ struct genlmsghdr *ghdr;
+ struct nlmsghdr *nlh;
+ struct nl_msg *msg;
+ struct nl_cb *cb;
+ int opt, ret, id;
+
+ if (!nfsd_nl_family_setup(sock))
+ return 1;
+
+ msg = netlink_msg_alloc(sock, GENL_ID_CTRL);
+ if (!msg)
+ return 1;
+
+ nlh = nlmsg_hdr(msg);
+ nlh->nlmsg_flags |= NLM_F_DUMP;
+ ghdr = nlmsg_data(nlh);
+ ghdr->cmd = CTRL_CMD_GETPOLICY;
+
+ cb = nl_cb_alloc(NL_CB_CUSTOM);
+ if (!cb) {
+ xlog(L_ERROR, "failed to allocate netlink callbacks");
+ ret = 1;
+ goto out;
+ }
+
+ nla_put_u16(msg, CTRL_ATTR_FAMILY_ID, nfsd_nl_family);
+ nla_put_u32(msg, CTRL_ATTR_OP, NFSD_CMD_THREADS_SET);
+
+ 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, getpolicy_handler, NULL);
+
+ 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 void status_usage(void)
{
printf("Usage: %s status\n", taskname);
@@ -652,6 +732,11 @@ static int threads_func(struct nl_sock *sock, int argc, char **argv)
return 0;
#if HAVE_DECL_NFSD_A_SERVER_MIN_THREADS
case 'm':
+ if (nfsd_threads_max_nlattr < NFSD_A_SERVER_MIN_THREADS) {
+ xlog(L_ERROR, "Kernel does not support dynamic threading.");
+ return 1;
+ }
+
errno = 0;
minthreads = strtoul(optarg, NULL, 0);
if (minthreads == ULONG_MAX && errno != 0) {
@@ -1757,7 +1842,17 @@ static int autostart_func(struct nl_sock *sock, int argc, char ** argv)
lease = conf_get_num("nfsd", "lease-time", 0);
scope = conf_get_str("nfsd", "scope");
- minthreads = conf_get_num("nfsd", "min-threads", 0);
+
+#if HAVE_DECL_NFSD_A_SERVER_MIN_THREADS
+ minthreads = conf_get_num("nfsd", "min-threads", -1);
+
+ if (minthreads >= 0 && nfsd_threads_max_nlattr < NFSD_A_SERVER_MIN_THREADS) {
+ xlog(L_WARNING, "kernel does not support dynamic threading. min-threads setting ignored.");
+ minthreads = -1;
+ }
+#else
+ minthreads = -1;
+#endif
ret = threads_doit(sock, NFSD_CMD_THREADS_SET, grace, lease, pools,
threads, scope, minthreads);
@@ -1950,6 +2045,9 @@ int main(int argc, char **argv)
return 1;
}
+ if (query_nfsd_nl_policy(sock))
+ return 1;
+
if (optind > argc) {
usage();
return 1;
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH nfs-utils 3/3] nfsdctl: remove unneeded newlines from xlog() format strings
2026-02-04 12:28 [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 1/3] nfsdctl: only resolve netlink family names once Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 2/3] nfsdctl: query netlink policy before sending the minthreads attribute to kernel Jeff Layton
@ 2026-02-04 12:28 ` Jeff Layton
2026-02-05 21:18 ` [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Benjamin Coddington
3 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2026-02-04 12:28 UTC (permalink / raw)
To: Steve Dickson; +Cc: Ben Coddington, Chuck Lever, linux-nfs, Jeff Layton
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
utils/nfsdctl/nfsdctl.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index 68c93039f3440a73285fd3f1e8b1131c1c945efb..250b6f6d962d33986a2a7d809f001ad3dca52da2 100644
--- a/utils/nfsdctl/nfsdctl.c
+++ b/utils/nfsdctl/nfsdctl.c
@@ -1528,14 +1528,14 @@ static int lockd_config_doit(struct nl_sock *sock, int cmd, int grace, int tcppo
cb = nl_cb_alloc(NL_CB_CUSTOM);
if (!cb) {
- xlog(L_ERROR, "failed to allocate netlink callbacks\n");
+ xlog(L_ERROR, "failed to allocate netlink callbacks");
ret = 1;
goto out;
}
ret = nl_send_auto(sock, msg);
if (ret < 0) {
- xlog(L_ERROR, "send failed (%d)!\n", ret);
+ xlog(L_ERROR, "send failed (%d)!", ret);
goto out_cb;
}
@@ -1548,7 +1548,7 @@ static int lockd_config_doit(struct nl_sock *sock, int cmd, int grace, int tcppo
while (ret > 0)
nl_recvmsgs(sock, cb);
if (ret < 0) {
- xlog(L_ERROR, "Error: %s\n", strerror(-ret));
+ xlog(L_ERROR, "Error: %s", strerror(-ret));
ret = 1;
}
out_cb:
@@ -1568,7 +1568,7 @@ static int get_service(const char *svc)
ret = getaddrinfo(NULL, svc, &hints, &res);
if (ret) {
- xlog(L_ERROR, "getaddrinfo of \"%s\" failed: %s\n",
+ xlog(L_ERROR, "getaddrinfo of \"%s\" failed: %s",
svc, gai_strerror(ret));
return -1;
}
@@ -1589,7 +1589,7 @@ static int get_service(const char *svc)
}
break;
default:
- xlog(L_ERROR, "Bad address family: %d\n", res->ai_family);
+ xlog(L_ERROR, "Bad address family: %d", res->ai_family);
port = -1;
}
freeaddrinfo(res);
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads
2026-02-04 12:28 [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
` (2 preceding siblings ...)
2026-02-04 12:28 ` [PATCH nfs-utils 3/3] nfsdctl: remove unneeded newlines from xlog() format strings Jeff Layton
@ 2026-02-05 21:18 ` Benjamin Coddington
3 siblings, 0 replies; 5+ messages in thread
From: Benjamin Coddington @ 2026-02-05 21:18 UTC (permalink / raw)
To: Jeff Layton; +Cc: Steve Dickson, Chuck Lever, linux-nfs
On 4 Feb 2026, at 7:28, Jeff Layton wrote:
> Ben reported a problem with using new userland with old kernel. If he
> tried to send down a setting that the kernel doesn't support, it returns
> -EINVAL to the call.
>
> This patch series adds a mechanism for nfsdctl to tell what attributes
> are supported by the "threads" command. If can then use that to
> determine whether to pass down the min-threads attribute or report an
> error or warning.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
Thanks for these Jeff!
For all three:
Reviewed-by: Benjamin Coddington <bcodding@hammerspace.com>
Tested-by: Benjamin Coddington <bcodding@hammerspace.com>
Ben
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-02-05 21:18 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-04 12:28 [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 1/3] nfsdctl: only resolve netlink family names once Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 2/3] nfsdctl: query netlink policy before sending the minthreads attribute to kernel Jeff Layton
2026-02-04 12:28 ` [PATCH nfs-utils 3/3] nfsdctl: remove unneeded newlines from xlog() format strings Jeff Layton
2026-02-05 21:18 ` [PATCH nfs-utils 0/3] nfsdctl: properly handle older kernels that don't support min-threads Benjamin Coddington
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox