Linux NFS development
 help / color / mirror / Atom feed
From: Jeff Layton <jlayton@kernel.org>
To: Steve Dickson <steved@redhat.com>
Cc: Ben Coddington <bcodding@hammerspace.com>,
	 Chuck Lever <chuck.lever@oracle.com>,
	linux-nfs@vger.kernel.org,  Jeff Layton <jlayton@kernel.org>
Subject: [PATCH nfs-utils 2/3] nfsdctl: query netlink policy before sending the minthreads attribute to kernel
Date: Wed, 04 Feb 2026 07:28:27 -0500	[thread overview]
Message-ID: <20260204-minthreads-v1-2-9f348608f884@kernel.org> (raw)
In-Reply-To: <20260204-minthreads-v1-0-9f348608f884@kernel.org>

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


  parent reply	other threads:[~2026-02-04 12:28 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

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=20260204-minthreads-v1-2-9f348608f884@kernel.org \
    --to=jlayton@kernel.org \
    --cc=bcodding@hammerspace.com \
    --cc=chuck.lever@oracle.com \
    --cc=linux-nfs@vger.kernel.org \
    --cc=steved@redhat.com \
    /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