public inbox for linux-nfs@vger.kernel.org
 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 v2 3/4] nfsdctl: query netlink policy before sending the minthreads attribute to kernel
Date: Wed, 04 Feb 2026 11:48:24 -0500	[thread overview]
Message-ID: <20260204-minthreads-v2-3-a7eba34201e9@kernel.org> (raw)
In-Reply-To: <20260204-minthreads-v2-0-a7eba34201e9@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 | 95 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
index 86a4a944d4e131f1114ca358d81779de0a034872..4a3744a1c22e6beac7c039bded05fc087a121200 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;
@@ -480,6 +483,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 NL_SKIP;
+}
+
+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);
@@ -639,6 +719,11 @@ static int threads_func(struct nl_sock *sock, int argc, char **argv)
 			threads_usage();
 			return 0;
 		case 'm':
+			if (nfsd_threads_max_nlattr < NFSD_A_SERVER_MIN_THREADS) {
+				xlog(L_ERROR, "This kernel does not support dynamic threading.");
+				return 1;
+			}
+
 			errno = 0;
 			minthreads = strtoul(optarg, NULL, 0);
 			if (minthreads == ULONG_MAX && errno != 0) {
@@ -1743,7 +1828,12 @@ 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);
+	minthreads = conf_get_num("nfsd", "min-threads", -1);
+
+	if (minthreads >= 0 && nfsd_threads_max_nlattr < NFSD_A_SERVER_MIN_THREADS) {
+		xlog(L_WARNING, "This kernel does not support dynamic threading. min-threads setting ignored.");
+		minthreads = -1;
+	}
 
 	ret = threads_doit(sock, NFSD_CMD_THREADS_SET, grace, lease, pools,
 			   threads, scope, minthreads);
@@ -1936,6 +2026,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 16:48 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-04 16:48 [PATCH nfs-utils v2 0/4] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
2026-02-04 16:48 ` [PATCH nfs-utils v2 1/4] nfsdctl: unconditionally enable support for min-threads Jeff Layton
2026-02-04 16:48 ` [PATCH nfs-utils v2 2/4] nfsdctl: only resolve netlink family names once Jeff Layton
2026-02-04 16:48 ` Jeff Layton [this message]
2026-02-04 16:48 ` [PATCH nfs-utils v2 4/4] nfsdctl: remove unneeded newlines from xlog() format strings Jeff Layton
2026-02-18 14:19 ` [PATCH nfs-utils v2 0/4] nfsdctl: properly handle older kernels that don't support min-threads Jeff Layton
2026-02-18 17:26   ` Steve Dickson
2026-02-18 17:59 ` Benjamin Coddington
2026-02-23 15:19 ` Steve Dickson

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-v2-3-a7eba34201e9@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