From patchwork Thu Jun 13 16:44:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 13697136 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A66631494B5 for ; Thu, 13 Jun 2024 16:44:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297067; cv=none; b=suxuC2TSlZLIL6s7JuCzrUf/Uy3nCxuqg3iII7v9BWxH3YeBKOiryxKqAyj9yCpuj1MrU/owWl2zy+L7zxBt5C2w9nDnkutEzysgMxC8lrdCDBtCD4BIvvIjFrZFHT1sC99iFJPLyI60AwowsYldFNEuRwLSJvPb60LeVcSJY10= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297067; c=relaxed/simple; bh=4qb2kmkoK+Rx/cWHSvyD8FXQMkyziBIjjNQ6TXmH+Ho=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=nMfc+E+xISb0ttzzSWuypct+Wmy7efaAZQ6lJiG/yYEq5zDGg4yTSsk/zWBVsa4LGwdS5qysEZSRoTQaT3hAlVpj6QYh+usseg7ZKlKlc5CBKTdoDCsgjzA2BuIdADwE9jGxxuKrdfjTCoNKA9te7uPiEAsBKupqU9+rLDDC6Ag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dO3RLUbX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dO3RLUbX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5A291C32789; Thu, 13 Jun 2024 16:44:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1718297067; bh=4qb2kmkoK+Rx/cWHSvyD8FXQMkyziBIjjNQ6TXmH+Ho=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=dO3RLUbXDqS3brBhuYlxM3kGxY3VTPnxDPYSKnEWt50ONCY+k49DekV1p6IVvqDR5 2pgsY1E06Dl+gQBGQP3PTQ9xX8tXGQPi7NpcbVlJobh7J1XZqPUHvkMyPZeK7ryJLR 9/3FdGx5yD2szFArTymfKORf8U9Q3fMD9wzX4rlOnxIlJnDS0a3LlKLJLP5tVylayx 5Kz8pvjo84/e+vJMYkDZ1asb/r5XKMXBTGNZXNJaoVKJCAqhvb6nZfAgMc31xrVVVC u5qKTWKoTHmG16qRHUamMCat1aeKMhryBwmHsZRSU/ybQDLw+V9g7dG6w1C7KJXX9e b1Qht8xtCvv4g== From: Jeff Layton Date: Thu, 13 Jun 2024 12:44:06 -0400 Subject: [PATCH nfs-utils v5 1/4] nfsdctl: add the nfsdctl utility to nfs-utils Precedence: bulk X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240613-nfsdctl-v5-1-0b7a39102bac@kernel.org> References: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> In-Reply-To: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> To: Steve Dickson Cc: linux-nfs@vger.kernel.org, Lorenzo Bianconi , Chuck Lever , Neil Brown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Jeff Layton X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=50619; i=jlayton@kernel.org; h=from:subject:message-id; bh=gWb9e6aeUk7g1y77egltRiZ8y4QwNlEKd4N54L9ublk=; b=owEBbQKS/ZANAwAIAQAOaEEZVoIVAcsmYgBmayHoZB1jivxkDknAZHR8LyaGjDiMd1L5rUB5X fLJtdPwkbuJAjMEAAEIAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCZmsh6AAKCRAADmhBGVaC FW6sD/90Ckra/VNbzKx0gkLrEfD4ULDRWsCZoYp+cVoYaJTST7zsGB50JoaiKaX3iLzo3pIyZ1G Z7fFj/5ZJAjKIHla7jORZsN34YmF10jWXfqxXRATzugIE3xUZ+jm9fYYn/4E6QHgvI+aJtTaQ8T R/50gDtTrO5gLKhs0bLcXZ5eRw8jzS4nZ7jOal4ew8RaS86DsvQdmFHpN9c2qN66RvQKLGIa9bP RI0ZgcQMlx/ycsKeu1RBHMRSb4iva5CS2ffhoo9swItZ7PuhIxY268rRPzAa/7x1xnT2/Ro5SQX zIKwUYGUrdAham/RPzIFgZjbHyJH+sxkglkeXznkXUYKYlBq69pD9mdLO6pPeWu1DnNCV7MyQs/ e3vJYi7V66j6MB45GhZOlxHn8quxd1DukEOYTWGCtgAwM10YjiUZoF/ofqgfSF+OzYQjwfndp9E jaGPKw3dRhapwUQscIR16GOBi96z20WcyRWkAtiOU7QwD6TagsWXCYRXpRh/XMrGgYCGau3fhp7 agiEfsKntAKGm/ypBSkNaFQv9jLVHGyS4mEQPzNl3hHjnvPRZhLWwGgv+pgI9fQCogbfP1hLVSS GsrYbpS01qYGb8Uc67Yr2mUk8MtQWUx66XZzHjTbVC5GVSkFWC8iniPlWvhTU0HK4QdWkfG8RM4 0yepvSEk1rpb6wA== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 From: Lorenzo Bianconi This tool is based on Lorenzo's original nfsdctl tool [1]. His original tool used getopt_long to indicate the command, but that's somewhat limiting. This converts it to a subcommand-based interface, where each subcommand can take its own options, in the spirit of commands like nmcli or virsh. There are currently 5 different subcommands: listener get/set listener info version get/set supported NFS versions threads get/set nfsd thread settings status get current RPC processing info autostart start server with settings from /etc/nfs.conf Each can take different options, and we can expand this interface later with more commands as necessary. [1]: https://github.com/LorenzoBianconi/nfsdctl Signed-off-by: Jeff Layton --- configure.ac | 19 + utils/Makefile.am | 4 + utils/nfsdctl/Makefile.am | 13 + utils/nfsdctl/nfsd_netlink.h | 86 +++ utils/nfsdctl/nfsdctl.8 | 274 ++++++++ utils/nfsdctl/nfsdctl.c | 1460 ++++++++++++++++++++++++++++++++++++++++++ utils/nfsdctl/nfsdctl.h | 93 +++ 7 files changed, 1949 insertions(+) diff --git a/configure.ac b/configure.ac index 58d1728c5bc6..8cb42ebc5846 100644 --- a/configure.ac +++ b/configure.ac @@ -252,6 +252,24 @@ AC_ARG_ENABLE(nfsdcltrack, enable_nfsdcltrack=$enableval, enable_nfsdcltrack="yes") +AC_ARG_ENABLE(nfsdctl, + [AS_HELP_STRING([--disable-nfsdctl],[disable nfsdctl program for controlling nfsd@<:@default=no@:>@])], + enable_nfsdctl=$enableval, + enable_nfsdctl="yes") + AM_CONDITIONAL(CONFIG_NFSDCTL, [test "$enable_nfsdctl" = "yes" ]) + if test "$enable_nfsdctl" = yes; then + PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1) + PKG_CHECK_MODULES(LIBNLGENL3, libnl-genl-3.0 >= 3.1) + PKG_CHECK_MODULES(LIBREADLINE, readline) + AC_CHECK_HEADERS(linux/nfsd_netlink.h) + + # ensure we have the pool-mode commands + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int foo = NFSD_CMD_LISTENER_GET;]])], + [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1, + ["Use system's linux/nfsd_netlink.h"])]) + fi + AC_ARG_ENABLE(nfsv4server, [AS_HELP_STRING([--enable-nfsv4server],[enable support for NFSv4 only server @<:@default=no@:>@])], enable_nfsv4server=$enableval, @@ -739,6 +757,7 @@ AC_CONFIG_FILES([ utils/mountd/Makefile utils/exportd/Makefile utils/nfsd/Makefile + utils/nfsdctl/Makefile utils/nfsref/Makefile utils/nfsstat/Makefile utils/nfsidmap/Makefile diff --git a/utils/Makefile.am b/utils/Makefile.am index ab584190f17e..e5cb81e796bd 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -35,6 +35,10 @@ if CONFIG_JUNCTION OPTDIRS += nfsref endif +if CONFIG_NFSDCTL +OPTDIRS += nfsdctl +endif + SUBDIRS = \ exportfs \ mountd \ diff --git a/utils/nfsdctl/Makefile.am b/utils/nfsdctl/Makefile.am new file mode 100644 index 000000000000..89c7ecd6f30b --- /dev/null +++ b/utils/nfsdctl/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in + +man8_MANS = nfsdctl.8 +EXTRA_DIST = $(man8_MANS) + +sbin_PROGRAMS = nfsdctl + +noinst_HEADERS = nfsdctl.h +nfsdctl_SOURCES = nfsdctl.c +nfsdctl_CFLAGS = $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS) $(LIBREADLINE_CFLAGS) +nfsdctl_LDADD = ../../support/nfs/libnfs.la $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS) $(LIBREADLINE_LIBS) + +MAINTAINERCLEANFILES = Makefile.in diff --git a/utils/nfsdctl/nfsd_netlink.h b/utils/nfsdctl/nfsd_netlink.h new file mode 100644 index 000000000000..24c86dbc7ed5 --- /dev/null +++ b/utils/nfsdctl/nfsd_netlink.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/nfsd.yaml */ +/* YNL-GEN uapi header */ + +#ifndef _UAPI_LINUX_NFSD_NETLINK_H +#define _UAPI_LINUX_NFSD_NETLINK_H + +#define NFSD_FAMILY_NAME "nfsd" +#define NFSD_FAMILY_VERSION 1 + +enum { + NFSD_A_RPC_STATUS_XID = 1, + NFSD_A_RPC_STATUS_FLAGS, + NFSD_A_RPC_STATUS_PROG, + NFSD_A_RPC_STATUS_VERSION, + NFSD_A_RPC_STATUS_PROC, + NFSD_A_RPC_STATUS_SERVICE_TIME, + NFSD_A_RPC_STATUS_PAD, + NFSD_A_RPC_STATUS_SADDR4, + NFSD_A_RPC_STATUS_DADDR4, + NFSD_A_RPC_STATUS_SADDR6, + NFSD_A_RPC_STATUS_DADDR6, + NFSD_A_RPC_STATUS_SPORT, + NFSD_A_RPC_STATUS_DPORT, + NFSD_A_RPC_STATUS_COMPOUND_OPS, + + __NFSD_A_RPC_STATUS_MAX, + NFSD_A_RPC_STATUS_MAX = (__NFSD_A_RPC_STATUS_MAX - 1) +}; + +enum { + NFSD_A_SERVER_THREADS = 1, + NFSD_A_SERVER_GRACETIME, + NFSD_A_SERVER_LEASETIME, + NFSD_A_SERVER_SCOPE, + + __NFSD_A_SERVER_MAX, + NFSD_A_SERVER_MAX = (__NFSD_A_SERVER_MAX - 1) +}; + +enum { + NFSD_A_VERSION_MAJOR = 1, + NFSD_A_VERSION_MINOR, + NFSD_A_VERSION_ENABLED, + + __NFSD_A_VERSION_MAX, + NFSD_A_VERSION_MAX = (__NFSD_A_VERSION_MAX - 1) +}; + +enum { + NFSD_A_SERVER_PROTO_VERSION = 1, + + __NFSD_A_SERVER_PROTO_MAX, + NFSD_A_SERVER_PROTO_MAX = (__NFSD_A_SERVER_PROTO_MAX - 1) +}; + +enum { + NFSD_A_SOCK_ADDR = 1, + NFSD_A_SOCK_TRANSPORT_NAME, + + __NFSD_A_SOCK_MAX, + NFSD_A_SOCK_MAX = (__NFSD_A_SOCK_MAX - 1) +}; + +enum { + NFSD_A_SERVER_SOCK_ADDR = 1, + + __NFSD_A_SERVER_SOCK_MAX, + NFSD_A_SERVER_SOCK_MAX = (__NFSD_A_SERVER_SOCK_MAX - 1) +}; + +enum { + NFSD_CMD_RPC_STATUS_GET = 1, + NFSD_CMD_THREADS_SET, + NFSD_CMD_THREADS_GET, + NFSD_CMD_VERSION_SET, + NFSD_CMD_VERSION_GET, + NFSD_CMD_LISTENER_SET, + NFSD_CMD_LISTENER_GET, + + __NFSD_CMD_MAX, + NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1) +}; + +#endif /* _UAPI_LINUX_NFSD_NETLINK_H */ diff --git a/utils/nfsdctl/nfsdctl.8 b/utils/nfsdctl/nfsdctl.8 new file mode 100644 index 000000000000..7f5d981042a5 --- /dev/null +++ b/utils/nfsdctl/nfsdctl.8 @@ -0,0 +1,274 @@ +'\" t +.\" Title: nfsdctl +.\" Author: Jeff Layton +.\" Generator: Asciidoctor 2.0.20 +.\" Date: 2024-04-23 +.\" Manual: \ \& +.\" Source: \ \& +.\" Language: English +.\" +.TH "NFSDCTL" "8" "2024-04-23" "\ \&" "\ \&" +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.ss \n[.ss] 0 +.nh +.ad l +.de URL +\fI\\$2\fP <\\$1>\\$3 +.. +.als MTO URL +.if \n[.g] \{\ +. mso www.tmac +. am URL +. ad l +. . +. am MTO +. ad l +. . +. LINKSTYLE blue R < > +.\} +.SH "NAME" +nfsdctl \- control program for the Linux kernel NFS server +.SH "SYNOPSIS" +.sp +\fBnfsdctl\fP [ \fIOPTION\fP ] COMMAND ... +.SH "DESCRIPTION" +.sp +nfsdctl is a control and query program for the in\-kernel NFS server. There are several +subcommands (documented below) that allow the admin to configure or query different +aspects of the NFS server. +.sp +To get information about different subcommand usage, pass the subcommand the +\-\-help parameter. For example: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl listener \-\-help +.fam +.fi +.if n .RE +.SH "OPTIONS" +.sp +\fB\-d, \-\-debug\fP +.RS 4 +Enable debug logging +.RE +.sp +\fB\-h, \-\-help\fP +.RS 4 +Print program help text +.RE +.sp +\fB\-V, \-\-version\fP +.RS 4 +Print program version +.RE +.SH "SUBCOMMANDS" +.sp +Each subcommand can also accept its own set of options and arguments. The +\-\-help option is standard for all subcommands: +.sp +\fBautostart\fP +.RS 4 +Start the server using the settings in the [nfsd] section of /etc/nfs.conf. +This subcommand takes no arguments. +.RE +.sp +\fBlistener\fP +.RS 4 +Get/set the listening sockets for the server. Run this without arguments to +get a list of the sockets on which the server is currently listening. To add +or remove sockets, pass it whitespace\-separated strings in the format: +.sp +.if n .RS 4 +.nf +.fam C +{ +|\- }{ protocol }:{ address }:{ port } +.fam +.fi +.if n .RE +.sp +.if n .RS 4 +.nf +.fam C +The fields are: +.fam +.fi +.if n .RE +.sp +.if n .RS 4 +.nf +.fam C ++ to add a listener, \- to remove one +protocol: protocol name (e.g. tcp, udp, rdma) +address: hostname or address +port: port number or service name +.fam +.fi +.if n .RE +.sp +.if n .RS 4 +.nf +.fam C +All fields are required, except for the address. If address is an empty string, +then the listeners will be opened for INADDR_ANY and IN6ADDR_ANY_INIT for ipv6 +(if enabled). The address can be either a hostname or an IP address. IPv4 +addresses must be in dotted\-quad form. IPv6 addresses should be in standard +colon separated form, and must be enclosed in square brackets. +.fam +.fi +.if n .RE +.RE +.sp +\fBstatus\fP +.RS 4 +Get information about RPCs currently executing in the server. This subcommand +takes no arguments. +.RE +.sp +\fBthreads\fP +.RS 4 +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. +.RE +.sp +\fBversion\fP +.RS 4 +Get/set the enabled NFS versions for the server. Run without arguments to +get a list of supported versions and whether they are currently enabled or +disabled. To enable or disable a version, pass it a string in the format: +.sp +.if n .RS 4 +.nf +.fam C +{ +|\- }{ MAJOR }{.{ MINOR }} +.fam +.fi +.if n .RE +.sp +.if n .RS 4 +.nf +.fam C +The fields are: +.fam +.fi +.if n .RE +.sp +.if n .RS 4 +.nf +.fam C ++ to enable a version, \- to disable +MAJOR: the major version integer value +MINOR: the minor version integet value +.fam +.fi +.if n .RE +.sp +.if n .RS 4 +.nf +.fam C +The minorversion field is optional. If not given, it will disable or enable +all minorversions for that major version. +.fam +.fi +.if n .RE +.RE +.SH "EXAMPLES" +.sp +Start the server with the settings in nfs.conf: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl autostart +.fam +.fi +.if n .RE +.sp +Get a list of current listening sockets: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl listener +.fam +.fi +.if n .RE +.sp +Show the supported and enabled NFS versions: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl version +.fam +.fi +.if n .RE +.sp +Add TCP listener on all addresses (both v4 and v6), port 2049: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl listener +tcp::2049 +.fam +.fi +.if n .RE +.sp +Add RDMA listener on 1.2.3.4 port 20049: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl listener +rdma:1.2.3.4:20049 +.fam +.fi +.if n .RE +.sp +Add same listener on IPv6 address f00::ba4 port 20050: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl listener +rdma:[f00::ba4]:20050 +.fam +.fi +.if n .RE +.sp +Enable NFS version 3, disable v4.0: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl version +3 \-4.0 +.fam +.fi +.if n .RE +.sp +Change the number of running threads in first pool to 256: +.sp +.if n .RS 4 +.nf +.fam C +nfsdctl threads 256 +.fam +.fi +.if n .RE +.SH "NOTES" +.sp +nfsdctl is intended to supersede rpc.nfsd(8), which controls the nfs server +using the files under /proc/fs/nfsd. nfsdctl instead uses a netlink(7) +interface to achieve the same goals. +.sp +Most of the commands that query the NFS server can be run as an unprivileged +user, but configuring the server typically requires an account with elevated +privileges. +.SH "SEE ALSO" +.sp +nfs.conf(5), rpc.nfsd(8), rpc.mountd(8), exports(5), exportfs(8), nfs.conf(5), rpc.rquotad(8), nfsstat(8), netconfig(5) +.SH "AUTHOR" +.sp +Jeff Layton \ No newline at end of file diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c new file mode 100644 index 000000000000..986c937b620d --- /dev/null +++ b/utils/nfsdctl/nfsdctl.c @@ -0,0 +1,1460 @@ +#define _GNU_SOURCE 1 + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef USE_SYSTEM_NFSD_NETLINK_H +#include +#else +#include "nfsd_netlink.h" +#endif + +#include "nfsdctl.h" +#include "conffile.h" +#include "xlog.h" + +/* compile note: + * gcc -I/usr/include/libnl3/ -o .c -lnl-3 -lnl-genl-3 + */ + +static int debug_level; +static int nl_family_id; + +struct nfs_version { + uint8_t major; + uint8_t minor; + bool enabled; +}; + +/* + * The NFS server should only have around 5 versions or so, so we don't bother + * with memory allocation here, and just use a global array. + */ +#define MAX_NFS_VERSIONS 16 + +struct nfs_version nfsd_versions[MAX_NFS_VERSIONS]; + +/* + * All of the existing netids are short strings (3-4 chars), but let's allow + * for up to 16. + */ +#define MAX_CLASS_NAME_LEN 16 + +struct server_socket { + struct sockaddr_storage ss; + char name[MAX_CLASS_NAME_LEN]; + bool active; +}; + +#define MAX_NFSD_SOCKETS 256 + +int nfsd_socket_count; +struct server_socket nfsd_sockets[MAX_NFSD_SOCKETS]; + +const char *taskname; + +static const struct option help_only_options[] = { + { "help", no_argument, NULL, 'h' }, + { }, +}; + +static void debug(int level, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (level <= debug_level) + vprintf(fmt, args); + va_end(args); +} + +#define NFSD4_OPS_MAX_LEN sizeof(nfsd4_ops) / sizeof(nfsd4_ops[0]) +static const char *nfsd4_ops[] = { + [OP_ACCESS] = "OP_ACCESS", + [OP_CLOSE] = "OP_CLOSE", + [OP_COMMIT] = "OP_COMMIT", + [OP_CREATE] = "OP_CREATE", + [OP_DELEGRETURN] = "OP_DELEGRETURN", + [OP_GETATTR] = "OP_GETATTR", + [OP_GETFH] = "OP_GETFH", + [OP_LINK] = "OP_LINK", + [OP_LOCK] = "OP_LOCK", + [OP_LOCKT] = "OP_LOCKT", + [OP_LOCKU] = "OP_LOCKU", + [OP_LOOKUP] = "OP_LOOKUP", + [OP_LOOKUPP] = "OP_LOOKUPP", + [OP_NVERIFY] = "OP_NVERIFY", + [OP_OPEN] = "OP_OPEN", + [OP_OPEN_CONFIRM] = "OP_OPEN_CONFIRM", + [OP_OPEN_DOWNGRADE] = "OP_OPEN_DOWNGRADE", + [OP_PUTFH] = "OP_PUTFH", + [OP_PUTPUBFH] = "OP_PUTPUBFH", + [OP_PUTROOTFH] = "OP_PUTROOTFH", + [OP_READ] = "OP_READ", + [OP_READDIR] = "OP_READDIR", + [OP_READLINK] = "OP_READLINK", + [OP_REMOVE] = "OP_REMOVE", + [OP_RENAME] = "OP_RENAME", + [OP_RENEW] = "OP_RENEW", + [OP_RESTOREFH] = "OP_RESTOREFH", + [OP_SAVEFH] = "OP_SAVEFH", + [OP_SECINFO] = "OP_SECINFO", + [OP_SETATTR] = "OP_SETATTR", + [OP_SETCLIENTID] = "OP_SETCLIENTID", + [OP_SETCLIENTID_CONFIRM] = "OP_SETCLIENTID_CONFIRM", + [OP_VERIFY] = "OP_VERIFY", + [OP_WRITE] = "OP_WRITE", + [OP_RELEASE_LOCKOWNER] = "OP_RELEASE_LOCKOWNER", + /* NFSv4.1 operations */ + [OP_EXCHANGE_ID] = "OP_EXCHANGE_ID", + [OP_BACKCHANNEL_CTL] = "OP_BACKCHANNEL_CTL", + [OP_BIND_CONN_TO_SESSION] = "OP_BIND_CONN_TO_SESSION", + [OP_CREATE_SESSION] = "OP_CREATE_SESSION", + [OP_DESTROY_SESSION] = "OP_DESTROY_SESSION", + [OP_SEQUENCE] = "OP_SEQUENCE", + [OP_DESTROY_CLIENTID] = "OP_DESTROY_CLIENTID", + [OP_RECLAIM_COMPLETE] = "OP_RECLAIM_COMPLETE", + [OP_SECINFO_NO_NAME] = "OP_SECINFO_NO_NAME", + [OP_TEST_STATEID] = "OP_TEST_STATEID", + [OP_FREE_STATEID] = "OP_FREE_STATEID", + [OP_GETDEVICEINFO] = "OP_GETDEVICEINFO", + [OP_LAYOUTGET] = "OP_LAYOUTGET", + [OP_LAYOUTCOMMIT] = "OP_LAYOUTCOMMIT", + [OP_LAYOUTRETURN] = "OP_LAYOUTRETURN", + /* NFSv4.2 operations */ + [OP_ALLOCATE] = "OP_ALLOCATE", + [OP_DEALLOCATE] = "OP_DEALLOCATE", + [OP_CLONE] = "OP_CLONE", + [OP_COPY] = "OP_COPY", + [OP_READ_PLUS] = "OP_READ_PLUS", + [OP_SEEK] = "OP_SEEK", + [OP_OFFLOAD_STATUS] = "OP_OFFLOAD_STATUS", + [OP_OFFLOAD_CANCEL] = "OP_OFFLOAD_CANCEL", + [OP_COPY_NOTIFY] = "OP_COPY_NOTIFY", + [OP_GETXATTR] = "OP_GETXATTR", + [OP_SETXATTR] = "OP_SETXATTR", + [OP_LISTXATTRS] = "OP_LISTXATTRS", + [OP_REMOVEXATTR] = "OP_REMOVEXATTR", +}; + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + + *ret = err->error; + return NL_SKIP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 0; + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + + *ret = 0; + return NL_STOP; +} + +static void parse_rpc_status_get(struct genlmsghdr *gnlh) +{ + 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_RPC_STATUS_XID: + case NFSD_A_RPC_STATUS_FLAGS: + printf(" 0x%08x", nla_get_u32(attr)); + break; + case NFSD_A_RPC_STATUS_PROC: + case NFSD_A_RPC_STATUS_PROG: + printf(" %d", nla_get_u32(attr)); + break; + case NFSD_A_RPC_STATUS_VERSION: + printf(" NFS%d", nla_get_u8(attr)); + break; + case NFSD_A_RPC_STATUS_SERVICE_TIME: + printf(" %ld", nla_get_u64(attr)); + break; + case NFSD_A_RPC_STATUS_DADDR4: + case NFSD_A_RPC_STATUS_SADDR4: { + struct in_addr addr = { + .s_addr = nla_get_u32(attr), + }; + + printf(" %s", inet_ntoa(addr)); + break; + } + case NFSD_A_RPC_STATUS_DPORT: + case NFSD_A_RPC_STATUS_SPORT: + printf(" %hu", nla_get_u16(attr)); + break; + case NFSD_A_RPC_STATUS_COMPOUND_OPS: { + unsigned int op = nla_get_u32(attr); + + if (op < NFSD4_OPS_MAX_LEN) + printf(" %s", nfsd4_ops[op]); + break; + } + default: + break; + } + } + printf("\n"); +} + +static void parse_version_get(struct genlmsghdr *gnlh) +{ + struct nlattr *attr; + int rem, idx = 0; + + /* clear the nfsd_versions array */ + memset(nfsd_versions, '\0', sizeof(*nfsd_versions) * MAX_NFS_VERSIONS); + + nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), rem) { + struct nlattr *a; + int i; + + nla_for_each_nested(a, attr, i) { + switch (nla_type(a)) { + case NFSD_A_VERSION_MAJOR: + nfsd_versions[idx].major = nla_get_u32(a); + break; + case NFSD_A_VERSION_MINOR: + nfsd_versions[idx].minor = nla_get_u32(a); + break; + case NFSD_A_VERSION_ENABLED: + nfsd_versions[idx].enabled = nla_get_flag(a); + break; + default: + break; + } + } + ++idx; + } +} + +static void parse_listener_get(struct genlmsghdr *gnlh) +{ + struct nlattr *attr; + int rem, idx = 0; + + /* clear the nfsd_sockets array */ + memset(nfsd_sockets, '\0', sizeof(*nfsd_sockets) * MAX_NFSD_SOCKETS); + + nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), rem) { + struct nlattr *a; + char *res; + int i; + + nla_for_each_nested(a, attr, i) { + switch (nla_type(a)) { + case NFSD_A_SOCK_TRANSPORT_NAME: + res = strncpy(nfsd_sockets[idx].name, nla_data(a), + MAX_CLASS_NAME_LEN); + res[MAX_CLASS_NAME_LEN - 1] = '\0'; // just to be sure + break; + case NFSD_A_SOCK_ADDR: + memcpy(&nfsd_sockets[idx].ss, nla_data(a), + sizeof(nfsd_sockets[idx].ss)); + break; + } + nfsd_sockets[idx].active = true; + } + ++idx; + } + nfsd_socket_count = idx; +} + +static void parse_threads_get(struct genlmsghdr *gnlh) +{ + struct nlattr *attr; + int rem, pools = 0, i = 0; + uint32_t *pool_threads = NULL; + + nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), rem) + if (nla_type(attr) == NFSD_A_SERVER_THREADS) + ++pools; + + pool_threads = alloca(pools * sizeof(*pool_threads)); + + nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), rem) { + struct nlattr *a; + + switch (nla_type(attr)) { + case NFSD_A_SERVER_GRACETIME: + printf("gracetime: %u\n", nla_get_u32(attr)); + break; + case NFSD_A_SERVER_LEASETIME: + printf("leasetime: %u\n", nla_get_u32(attr)); + break; + case NFSD_A_SERVER_SCOPE: + printf("scope: %s\n", nla_data(attr)); + break; + case NFSD_A_SERVER_THREADS: + pool_threads[i++] = nla_get_u32(attr); + break; + default: + break; + } + } + + printf("pool-threads:"); + for (i = 0; i < pools; ++i) + printf(" %d", pool_threads[i]); + putchar('\n'); +} + +static int recv_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + const struct nlattr *attr = genlmsg_attrdata(gnlh, 0); + + switch (gnlh->cmd) { + case NFSD_CMD_RPC_STATUS_GET: + parse_rpc_status_get(gnlh); + break; + case NFSD_CMD_THREADS_GET: + parse_threads_get(gnlh); + break; + case NFSD_CMD_VERSION_GET: + parse_version_get(gnlh); + break; + case NFSD_CMD_LISTENER_GET: + parse_listener_get(gnlh); + break; + default: + break; + } + + return NL_SKIP; +} + +#define BUFFER_SIZE 8192 +static struct nl_sock *netlink_sock_alloc(void) +{ + struct nl_sock *sock; + int ret; + + sock = nl_socket_alloc(); + if (!sock) + return NULL; + + if (genl_connect(sock)) { + fprintf(stderr, "Failed to connect to generic netlink\n"); + nl_socket_free(sock); + return NULL; + } + + nl_socket_set_buffer_size(sock, BUFFER_SIZE, BUFFER_SIZE); + setsockopt(nl_socket_get_fd(sock), SOL_NETLINK, NETLINK_EXT_ACK, + &ret, sizeof(ret)); + + return sock; +} + +static struct nl_msg *netlink_msg_alloc(struct nl_sock *sock) +{ + struct nl_msg *msg; + int id; + + id = genl_ctrl_resolve(sock, NFSD_FAMILY_NAME); + if (id < 0) { + fprintf(stderr, "%s not found\n", NFSD_FAMILY_NAME); + return NULL; + } + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return NULL; + } + + if (!genlmsg_put(msg, 0, 0, id, 0, 0, 0, 0)) { + fprintf(stderr, "failed to allocate netlink message\n"); + nlmsg_free(msg); + return NULL; + } + + return msg; +} + +static void status_usage(void) +{ + printf("Usage: %s status\n", taskname); + printf(" Display RPC jobs currently in flight on the server.\n"); +} + +static int status_func(struct nl_sock *sock, int argc, char ** argv) +{ + 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': + status_usage(); + return 0; + } + } + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + nlh->nlmsg_flags |= NLM_F_DUMP; + ghdr = nlmsg_data(nlh); + ghdr->cmd = NFSD_CMD_RPC_STATUS_GET; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", 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) +{ + struct genlmsghdr *ghdr; + struct nlmsghdr *nlh; + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + if (cmd == NFSD_CMD_THREADS_SET) { + int i; + + if (grace) + nla_put_u32(msg, NFSD_A_SERVER_GRACETIME, grace); + if (lease) + nla_put_u32(msg, NFSD_A_SERVER_LEASETIME, lease); + if (scope) + nla_put_string(msg, NFSD_A_SERVER_SCOPE, scope); + for (i = 0; i < pool_count; ++i) + nla_put_u32(msg, NFSD_A_SERVER_THREADS, pool_threads[i]); + } + ghdr = nlmsg_data(nlh); + ghdr->cmd = cmd; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + ret = 1; + goto out; + } + + ret = nl_send_auto(sock, msg); + if (ret < 0) { + fprintf(stderr, "send failed (%d)!\n", ret); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", strerror(-ret)); + ret = 1; + } +out_cb: + nl_cb_put(cb); +out: + nlmsg_free(msg); + return ret; +} + +static void threads_usage(void) +{ + printf("Usage: %s threads [ pool0_count ] [ pool1_count ] ...\n", taskname); + printf(" pool0_count: thread count for pool0, etc...\n"); + printf("Omit any arguments to show current thread counts.\n"); +} + +static int threads_func(struct nl_sock *sock, int argc, char **argv) +{ + uint8_t cmd = NFSD_CMD_THREADS_GET; + uint32_t *pool_threads = NULL; + int opt, pools = 0; + + optind = 1; + while ((opt = getopt_long(argc, argv, "h", help_only_options, NULL)) != -1) { + switch (opt) { + case 'h': + threads_usage(); + return 0; + } + } + + if (optind < argc) { + char **targv = &argv[optind]; + int i; + + pools = argc - optind, i; + pool_threads = alloca(pools * sizeof(*pool_threads)); + cmd = NFSD_CMD_THREADS_SET; + + for (i = 0; i < pools; ++i) { + char *endptr = NULL; + + /* empty string? */ + if (targv[i][0] == '\0') { + fprintf(stderr, "Invalid threads value %s.\n", targv[i]); + return 1; + } + + pool_threads[i] = strtol(targv[i], &endptr, 0); + if (!endptr || *endptr != '\0') { + fprintf(stderr, "Invalid threads value %s.\n", argv[1]); + return 1; + } + } + } + return threads_doit(sock, cmd, 0, 0, pools, pool_threads, NULL); +} + +/* + * Update the nfsd_versions array with the latest info from the kernel + */ +static int fetch_nfsd_versions(struct nl_sock *sock) +{ + struct genlmsghdr *ghdr; + struct nlmsghdr *nlh; + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + ghdr = nlmsg_data(nlh); + ghdr->cmd = NFSD_CMD_VERSION_GET; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + ret = 1; + goto out; + } + + ret = nl_send_auto(sock, msg); + if (ret < 0) { + fprintf(stderr, "send failed: %d\n", ret); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", strerror(-ret)); + ret = 1; + } +out_cb: + nl_cb_put(cb); +out: + nlmsg_free(msg); + return ret; +} + +static void print_versions_array(void) +{ + int i; + + for (i = 0; i < MAX_NFS_VERSIONS; ++i) { + /* A major of zero indicates the end of the array */ + if (nfsd_versions[i].major == 0) + break; + if (i != 0) + printf(" "); + printf("%c%hhd.%hhd", + nfsd_versions[i].enabled ? '+' : '-', + nfsd_versions[i].major, nfsd_versions[i].minor); + } + putchar('\n'); +} + +static int set_nfsd_versions(struct nl_sock *sock) +{ + struct genlmsghdr *ghdr; + struct nlmsghdr *nlh; + struct nl_msg *msg; + struct nl_cb *cb; + int i, ret; + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + + for (i = 0; i < MAX_NFS_VERSIONS; ++i) { + struct nlattr *a; + + if (nfsd_versions[i].major == 0) + break; + + a = nla_nest_start(msg, NLA_F_NESTED | NFSD_A_SERVER_PROTO_VERSION); + if (!a) { + fprintf(stderr, "Unable to allocate version nest!\n"); + ret = 1; + goto out; + } + + nla_put_u32(msg, NFSD_A_VERSION_MAJOR, nfsd_versions[i].major); + nla_put_u32(msg, NFSD_A_VERSION_MINOR, nfsd_versions[i].minor); + if (nfsd_versions[i].enabled) + nla_put_flag(msg, NFSD_A_VERSION_ENABLED); + nla_nest_end(msg, a); + } + + ghdr = nlmsg_data(nlh); + ghdr->cmd = NFSD_CMD_VERSION_SET; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "Failed to allocate netlink callbacks\n"); + ret = 1; + goto out; + } + + ret = nl_send_auto(sock, msg); + if (ret < 0) { + fprintf(stderr, "Send failed: %d\n", ret); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", strerror(-ret)); + ret = 1; + } +out_cb: + nl_cb_put(cb); +out: + nlmsg_free(msg); + return ret; +} + +static int update_nfsd_version(int major, int minor, bool enabled) +{ + int i; + + for (i = 0; i < MAX_NFS_VERSIONS; ++i) { + if (nfsd_versions[i].major == 0) + break; + if (nfsd_versions[i].major == major && nfsd_versions[i].minor == minor) { + nfsd_versions[i].enabled = enabled; + return 0; + } + } + /* the kernel doesn't support this version */ + if (!enabled) + return 0; + fprintf(stderr, "This kernel does not support NFS version %d.%d\n", major, minor); + return -EINVAL; +} + +static void version_usage(void) +{ + printf("Usage: %s version { {+,-}major.minor } ...\n", taskname); + printf(" + to enable a version, - to disable it\n"); + printf(" @major: major version number\n"); + printf(" @minor: minor version number\n"); + printf("Examples:\n"); + printf(" Display currently enabled and disabled versions:\n"); + printf(" version\n"); + printf(" Disable NFSv4.0:\n"); + printf(" version -v4.0\n"); + printf(" Enable v4.1, v4.2, disable v2, v3 and v4.0:\n"); + printf(" version -2 -3 -v4.0 +4.1 +v4.2\n"); +} + +static int version_func(struct nl_sock *sock, int argc, char ** argv) +{ + char *endptr = NULL; + int opt, ret, threads, i; + + /* help is only valid as first argument after command */ + if (argc > 1 && + (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { + version_usage(); + return 0; + } + + ret = fetch_nfsd_versions(sock); + if (ret) + return ret; + + if (argc > 1) { + for (i = 1; i < argc; ++i) { + int ret, major, minor = 0; + char sign = '\0', *str = argv[i]; + bool enabled; + + ret = sscanf(str, "%c%d.%d\n", &sign, &major, &minor); + if (ret < 2) { + fprintf(stderr, "Invalid version string (%d) %s\n", ret, str); + return -EINVAL; + } + + switch(sign) { + case '+': + enabled = true; + break; + case '-': + enabled = false; + break; + default: + fprintf(stderr, "Invalid version string %s\n", str); + return -EINVAL; + } + + ret = update_nfsd_version(major, minor, enabled); + if (ret) + return ret; + } + return set_nfsd_versions(sock); + } + + print_versions_array(); + return 0; +} + +static int fetch_current_listeners(struct nl_sock *sock) +{ + struct genlmsghdr *ghdr; + struct nlmsghdr *nlh; + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + ghdr = nlmsg_data(nlh); + ghdr->cmd = NFSD_CMD_LISTENER_GET; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + ret = 1; + goto out; + } + + ret = nl_send_auto(sock, msg); + if (ret < 0) { + fprintf(stderr, "send failed: %d\n", ret); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", strerror(-ret)); + ret = 1; + } +out_cb: + nl_cb_put(cb); +out: + nlmsg_free(msg); + return ret; +} + +static void print_listeners(void) +{ + int i; + const char *res; + + for (i = 0; i < MAX_NFSD_SOCKETS; ++i) { + struct server_socket *sock = &nfsd_sockets[i]; + char addr[INET6_ADDRSTRLEN + 1]; + in_port_t port = 0; + + if (*sock->name == '\0') + break; + + if (!sock->active) + continue; + + switch(sock->ss.ss_family) { + case AF_INET: + res = inet_ntop(AF_INET, &((struct sockaddr_in *)(&sock->ss))->sin_addr, + addr, INET6_ADDRSTRLEN); + port = ((struct sockaddr_in *)(&sock->ss))->sin_port; + if (res == NULL) + perror("inet_ntop"); + else + printf("%s:%s:%hu\n", sock->name, addr, ntohs(port)); + break; + case AF_INET6: + res = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)(&sock->ss))->sin6_addr, + addr, INET6_ADDRSTRLEN); + port = ((struct sockaddr_in6 *)(&sock->ss))->sin6_port; + if (res == NULL) + perror("inet_ntop"); + else + printf("%s:[%s]:%hu\n", sock->name, addr, ntohs(port)); + break; + default: + snprintf(addr, INET6_ADDRSTRLEN, "Unknown address family: %d\n", + sock->ss.ss_family); + addr[INET6_ADDRSTRLEN - 1] = '\0'; + } + } +} + +#define BUFLEN (INET6_ADDRSTRLEN + 16) + +/* + * Format is <+/->:
:port + * + * + or -: denotes whether we're adding or removing a socket + * netid: tcp, udp, rdma (something else in the future?( + * address: IPv4 or IPv6 address. IPv6 addr should be in square brackets + * port: decimal port value + */ +static int update_listeners(const char *str) +{ + char buf[INET6_ADDRSTRLEN + 16]; + char sign = *str; + char *netid, *addr, *port, *end; + struct addrinfo *res, *ai; + int i, ret; + struct addrinfo hints = { .ai_flags = AI_PASSIVE, + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP }; + + if (sign != '+' && sign != '-') + goto out_inval; + + strcpy(buf, str + 1); + + /* netid is start */ + netid = buf; + + /* find first ':' */ + addr = strchr(buf, ':'); + if (!addr) + goto out_inval; + + if (addr == buf) { + /* empty netid */ + goto out_inval; + } + *addr = '\0'; + ++addr; + + port = strrchr(addr, ':'); + if (!port) + goto out_inval; + if (port == addr) { + /* empty address, give gai a NULL ptr */ + addr = NULL; + } + *port = '\0'; + port++; + + if (*port == '\0') { + /* empty port */ + goto out_inval; + } + + /* IPv6 addrs must be in square brackets */ + if (addr && *addr == '[') { + hints.ai_family = AF_INET6; + ++addr; + end = strchr(addr, ']'); + if (!end) + goto out_inval; + if (end == addr) + addr = NULL; + *end = '\0'; + } + + /* + * If we're looking for wildcard address, look for both + * families. + */ + if (!addr) + hints.ai_family = AF_UNSPEC; + + /* + * Note that we hint for a stream/tcp socket just to limit the number of + * entries that come back. We're only interested in the sockaddrs. + */ + ret = getaddrinfo(addr, port, &hints, &res); + if (ret) { + fprintf(stderr, "getaddrinfo of \"%s\" failed: %s\n", + addr, gai_strerror(ret)); + return -EINVAL; + } + + for ( ; res; res = res->ai_next) { + struct sockaddr_in6 *r6 = (struct sockaddr_in6 *)res->ai_addr; + struct sockaddr_in *r4 = (struct sockaddr_in *)res->ai_addr; + bool found = false; + + for (i = 0; i < MAX_NFSD_SOCKETS; ++i) { + struct server_socket *sock = &nfsd_sockets[i]; + struct sockaddr_in6 *l6 = (struct sockaddr_in6 *)&sock->ss; + struct sockaddr_in *l4 = (struct sockaddr_in *)&sock->ss; + + if (sock->ss.ss_family == AF_UNSPEC) + break; + + if (sock->ss.ss_family != res->ai_addr->sa_family) + continue; + + if (strcmp(sock->name, netid)) + continue; + + switch(sock->ss.ss_family) { + case AF_INET: + if (r4->sin_port != l4->sin_port || + memcmp(&r4->sin_addr, &l4->sin_addr, sizeof(l4->sin_addr))) + continue; + case AF_INET6: + if (r6->sin6_port != l6->sin6_port || + memcmp(&r6->sin6_addr, &l6->sin6_addr, sizeof(l6->sin6_addr))) + continue; + default: + + } + sock->active = (sign == '+'); + found = true; + break; + } + if (!found && sign == '+') { + struct server_socket *sock = &nfsd_sockets[nfsd_socket_count]; + + memcpy(&sock->ss, res->ai_addr, res->ai_addrlen); + strncpy(sock->name, netid, MAX_CLASS_NAME_LEN); + sock->name[MAX_CLASS_NAME_LEN - 1] = '\0'; + sock->active = true; + ++nfsd_socket_count; + } + } + return 0; +out_inval: + fprintf(stderr, "Invalid listener update string: %s", str); + return -EINVAL; +} + +static int set_listeners(struct nl_sock *sock) +{ + struct genlmsghdr *ghdr; + struct nlmsghdr *nlh; + struct nl_msg *msg; + struct nl_cb *cb; + int i, ret; + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + + for (i = 0; i < MAX_NFSD_SOCKETS; ++i) { + struct server_socket *sock = &nfsd_sockets[i]; + struct nlattr *a; + + if (sock->ss.ss_family == 0) + break; + + if (!sock->active) + continue; + + a = nla_nest_start(msg, NLA_F_NESTED | NFSD_A_SERVER_SOCK_ADDR); + if (!a) { + fprintf(stderr, "Unable to allocate listener nest!\n"); + ret = 1; + goto out; + } + + nla_put(msg, NFSD_A_SOCK_ADDR, sizeof(sock->ss), &sock->ss); + nla_put_string(msg, NFSD_A_SOCK_TRANSPORT_NAME, sock->name); + nla_nest_end(msg, a); + } + + ghdr = nlmsg_data(nlh); + ghdr->cmd = NFSD_CMD_LISTENER_SET; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "Failed to allocate netlink callbacks\n"); + ret = 1; + goto out; + } + + ret = nl_send_auto(sock, msg); + if (ret < 0) { + fprintf(stderr, "Send failed: %d\n", ret); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", strerror(-ret)); + ret = 1; + } +out_cb: + nl_cb_put(cb); +out: + nlmsg_free(msg); + return ret; +} + +static void listener_usage(void) +{ + printf("Usage: %s listener { {+,-}proto:addr:port } ...\n", taskname); + printf(" + to add a listener, - to remove one\n"); + printf(" @proto: protocol (e.g. tcp, udp, rdma)\n"); + printf(" @addr: hostname or address to listen on (blank string == wildcard addresses)\n"); + printf(" @port: port number or service name to listen on\n\n"); + printf("Examples:\n"); + printf(" Display currently configured listeners:\n"); + printf(" listener\n"); + printf(" Add TCP listener on all addresses (both v4 and v6), port 2049:\n"); + printf(" listener +tcp::2049\n"); + printf(" Add RDMA listener on 1.2.3.4 port 20049:\n"); + printf(" listener +rdma:1.2.3.4:20049\n"); + printf(" Add same listener on IPv6 address f00::ba4 port 20050:\n"); + printf(" listener +rdma:[f00::ba4]:20050\n"); + printf(" Remove UDP listener from nfsserver.example.org, nfs port:\n"); + printf(" listener -udp:nfsserver.example.org:nfs\n\n"); +} + +static int listener_func(struct nl_sock *sock, int argc, char ** argv) +{ + char *endptr = NULL; + int ret, opt, threads, i; + int argidx; + + /* help is only valid as first argument after command */ + if (argc > 1 && + (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { + listener_usage(); + return 0; + } + + ret = fetch_current_listeners(sock); + if (ret) + return ret; + + if (argc > 1) { + for (i = 1; i < argc; ++i) + update_listeners(argv[i]); + return set_listeners(sock); + } + + print_listeners(); + return 0; +} + +#define MAX_LISTENER_LEN (64 * 2 + 16) + +static int +add_listener(const char *netid, const char *addr, const char *port) +{ + char buf[MAX_LISTENER_LEN]; + int ret; + + if (strchr(addr, ':')) + ret = snprintf(buf, MAX_LISTENER_LEN, "+%s:[%s]:%s", + netid, addr, port); + else + ret = snprintf(buf, MAX_LISTENER_LEN, "+%s:%s:%s", + netid, addr, port); + buf[MAX_LISTENER_LEN - 1] = '\0'; + update_listeners(buf); +} + +static void +read_nfsd_conf(void) +{ + conf_init_file(NFS_CONFFILE); + xlog_set_debug("nfsd"); +} + +static void configure_versions(void) +{ + bool v4 = conf_get_bool("nfsd", "vers4", true); + + update_nfsd_version(2, 0, conf_get_bool("nfsd", "vers2", false)); + update_nfsd_version(3, 0, conf_get_bool("nfsd", "vers3", true)); + update_nfsd_version(4, 0, v4 && conf_get_bool("nfsd", "vers4.0", true)); + update_nfsd_version(4, 1, v4 && conf_get_bool("nfsd", "vers4.1", true)); + update_nfsd_version(4, 2, v4 && conf_get_bool("nfsd", "vers4.2", true)); +} + +static void configure_listeners(void) +{ + char *port, *rdma_port; + bool rdma, udp, tcp; + struct conf_list *hosts; + + udp = conf_get_bool("nfsd", "udp", false); + tcp = conf_get_bool("nfsd", "tcp", true); + port = conf_get_str("nfsd", "port"); + if (!port) + port = "nfs"; + + rdma = conf_get_bool("nfsd", "rdma", false); + if (rdma) { + rdma_port = conf_get_str("nfsd", "rdma-port"); + if (!rdma_port) + rdma_port = "nfsrdma"; + } + + /* backward compatibility - nfs.conf used to set rdma port directly */ + if (!rdma_port) + rdma_port = conf_get_str("nfsd", "rdma"); + + hosts = conf_get_list("nfsd", "host"); + if (hosts && hosts->cnt) { + struct conf_list_node *n; + TAILQ_FOREACH(n, &(hosts->fields), link) { + if (udp) + add_listener("udp", n->field, port); + if (tcp) + add_listener("tcp", n->field, port); + if (rdma) + add_listener("rdma", n->field, rdma_port); + } + } else { + if (udp) + add_listener("udp", "", port); + if (tcp) + add_listener("tcp", "", port); + if (rdma) + add_listener("rdma", "", rdma_port); + } +} + +static void autostart_usage(void) +{ + printf("Usage: %s autostart\n", taskname); + printf(" Start the server with the settings in /etc/nfs.conf.\n"); +} + +static int autostart_func(struct nl_sock *sock, int argc, char ** argv) +{ + int *threads, grace, lease, idx, ret, opt; + struct conf_list *thread_str; + struct conf_list_node *n; + char *scope; + + optind = 1; + while ((opt = getopt_long(argc, argv, "h", help_only_options, NULL)) != -1) { + switch (opt) { + case 'h': + autostart_usage(); + return 0; + } + } + + read_nfsd_conf(); + + ret = fetch_nfsd_versions(sock); + if (ret) + return ret; + configure_versions(); + ret = set_nfsd_versions(sock); + if (ret) + return ret; + + configure_listeners(); + ret = set_listeners(sock); + if (ret) + return ret; + + grace = conf_get_num("nfsd", "grace-time", 0); + lease = conf_get_num("nfsd", "lease-time", 0); + scope = conf_get_str("nfsd", "scope"); + + thread_str = conf_get_list("nfsd", "threads"); + if (!thread_str || thread_str->cnt == 0) + return 0; + + threads = calloc(thread_str->cnt, sizeof(int)); + if (!threads) + return -ENOMEM; + + idx = 0; + TAILQ_FOREACH(n, &(thread_str->fields), link) { + char *endptr = NULL; + + threads[idx++] = strtol(n->field, &endptr, 0); + if (!endptr || *endptr != '\0') { + fprintf(stderr, "Invalid threads value %s.\n", n->field); + ret = -EINVAL; + goto out; + } + } + + ret = threads_doit(sock, NFSD_CMD_THREADS_SET, grace, lease, thread_str->cnt, + threads, scope); +out: + free(threads); + return ret; +} + +enum nfsdctl_commands { + NFSDCTL_STATUS, + NFSDCTL_THREADS, + NFSDCTL_VERSION, + NFSDCTL_LISTENER, + NFSDCTL_AUTOSTART, +}; + +static int parse_command(char *str) +{ + if (!strcmp(str, "status")) + return NFSDCTL_STATUS; + if (!strcmp(str, "threads")) + return NFSDCTL_THREADS; + if (!strcmp(str, "version")) + return NFSDCTL_VERSION; + if (!strcmp(str, "listener")) + return NFSDCTL_LISTENER; + if (!strcmp(str, "autostart")) + return NFSDCTL_AUTOSTART; + return -1; +} + +typedef int (*nfsdctl_func)(struct nl_sock *sock, int argc, char **argv); + +static nfsdctl_func func[] = { + [NFSDCTL_STATUS] = status_func, + [NFSDCTL_THREADS] = threads_func, + [NFSDCTL_VERSION] = version_func, + [NFSDCTL_LISTENER] = listener_func, + [NFSDCTL_AUTOSTART] = autostart_func, +}; + +static void usage(void) +{ + printf("Usage:\n"); + printf("%s [-hv] [COMMAND] [ARGS]\n", taskname); + printf(" options:\n"); + printf(" -h | --help usage info\n"); + printf(" -d | --debug=NUM enable debugging\n"); + printf(" -V | --version print version info\n"); + printf(" commands:\n"); + printf(" listener get/set listener info\n"); + printf(" version get/set supported NFS versions\n"); + printf(" threads get/set nfsd thread settings\n"); + printf(" status get current RPC processing info\n"); + printf(" autostart start server with settings from /etc/nfs.conf\n"); +} + +/* Options given before the command string */ +static const struct option pre_options[] = { + { "help", no_argument, NULL, 'h' }, + { "debug", required_argument, NULL, 'd' }, + { "version", no_argument, NULL, 'V' }, + { }, +}; + +static int run_one_command(struct nl_sock *sock, int argc, char **argv) +{ + int cmd = parse_command(argv[0]); + + if (cmd < 0) { + usage(); + return 1; + } + return func[cmd](sock, argc, argv); +} + +#define MAX_ARGUMENTS 256 + +static int tokenize_string(char *line, int *argc, char **argv) +{ + int idx = 0; + char *arg, *save; + + memset(argv, '\0', sizeof(*argv) * MAX_ARGUMENTS); + + arg = strtok_r(line, " \t", &save); + while(arg) { + argv[idx] = arg; + ++idx; + if (idx >= MAX_ARGUMENTS) + return -E2BIG; + arg = strtok_r(NULL, " \t", &save); + } + *argc = idx; + return 0; +} + +static int run_commandline(struct nl_sock *sock) +{ + char *argv[MAX_ARGUMENTS]; + char *line; + int ret, argc; + + for (;;) { + line = readline("nfsdctl> "); + if (!line || !strcmp(line, "quit")) + break; + if (*line == '\0') + continue; + add_history(line); + ret = tokenize_string(line, &argc, argv); + if (!ret) + ret = run_one_command(sock, argc, argv); + if (ret) + fprintf(stderr, "Error: %s\n", strerror(ret)); + free(line); + } + return 0; +} + +int main(int argc, char **argv) +{ + int opt, ret; + struct nl_sock *sock = netlink_sock_alloc(); + + if (!sock) { + fprintf(stderr, "Unable to allocate netlink socket!"); + return 1; + } + + taskname = argv[0]; + + /* Parse the preliminary options */ + while ((opt = getopt_long(argc, argv, "+hd:V", pre_options, NULL)) != -1) { + switch (opt) { + case 'h': + usage(); + return 0; + case 'd': + debug_level = atoi(optarg); + break; + case 'V': + // FIXME: print_version(); + return 0; + } + } + + if (optind > argc) { + usage(); + return 1; + } + + if (optind == argc) + ret = run_commandline(sock); + else + ret = run_one_command(sock, argc - optind, &argv[optind]); + + nl_socket_free(sock); + return ret; +} diff --git a/utils/nfsdctl/nfsdctl.h b/utils/nfsdctl/nfsdctl.h new file mode 100644 index 000000000000..f0aa3ab862c2 --- /dev/null +++ b/utils/nfsdctl/nfsdctl.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/nfsd.yaml */ +/* YNL-GEN uapi header */ + +#ifndef _UTILS_NFSDCTL_NFSDCTL_H +#define _UTILS_NFSDCTL_NFSDCTL_H + +enum nfs_opnum4 { + OP_ACCESS = 3, + OP_CLOSE = 4, + OP_COMMIT = 5, + OP_CREATE = 6, + OP_DELEGPURGE = 7, + OP_DELEGRETURN = 8, + OP_GETATTR = 9, + OP_GETFH = 10, + OP_LINK = 11, + OP_LOCK = 12, + OP_LOCKT = 13, + OP_LOCKU = 14, + OP_LOOKUP = 15, + OP_LOOKUPP = 16, + OP_NVERIFY = 17, + OP_OPEN = 18, + OP_OPENATTR = 19, + OP_OPEN_CONFIRM = 20, + OP_OPEN_DOWNGRADE = 21, + OP_PUTFH = 22, + OP_PUTPUBFH = 23, + OP_PUTROOTFH = 24, + OP_READ = 25, + OP_READDIR = 26, + OP_READLINK = 27, + OP_REMOVE = 28, + OP_RENAME = 29, + OP_RENEW = 30, + OP_RESTOREFH = 31, + OP_SAVEFH = 32, + OP_SECINFO = 33, + OP_SETATTR = 34, + OP_SETCLIENTID = 35, + OP_SETCLIENTID_CONFIRM = 36, + OP_VERIFY = 37, + OP_WRITE = 38, + OP_RELEASE_LOCKOWNER = 39, + + /* nfs41 */ + OP_BACKCHANNEL_CTL = 40, + OP_BIND_CONN_TO_SESSION = 41, + OP_EXCHANGE_ID = 42, + OP_CREATE_SESSION = 43, + OP_DESTROY_SESSION = 44, + OP_FREE_STATEID = 45, + OP_GET_DIR_DELEGATION = 46, + OP_GETDEVICEINFO = 47, + OP_GETDEVICELIST = 48, + OP_LAYOUTCOMMIT = 49, + OP_LAYOUTGET = 50, + OP_LAYOUTRETURN = 51, + OP_SECINFO_NO_NAME = 52, + OP_SEQUENCE = 53, + OP_SET_SSV = 54, + OP_TEST_STATEID = 55, + OP_WANT_DELEGATION = 56, + OP_DESTROY_CLIENTID = 57, + OP_RECLAIM_COMPLETE = 58, + + /* nfs42 */ + OP_ALLOCATE = 59, + OP_COPY = 60, + OP_COPY_NOTIFY = 61, + OP_DEALLOCATE = 62, + OP_IO_ADVISE = 63, + OP_LAYOUTERROR = 64, + OP_LAYOUTSTATS = 65, + OP_OFFLOAD_CANCEL = 66, + OP_OFFLOAD_STATUS = 67, + OP_READ_PLUS = 68, + OP_SEEK = 69, + OP_WRITE_SAME = 70, + OP_CLONE = 71, + + /* xattr support (RFC8726) */ + OP_GETXATTR = 72, + OP_SETXATTR = 73, + OP_LISTXATTRS = 74, + OP_REMOVEXATTR = 75, + + OP_ILLEGAL = 10044, +}; + +#endif /* _UTILS_NFSDCTL_NFSDCTL_H */ From patchwork Thu Jun 13 16:44:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 13697137 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BF896146A79 for ; Thu, 13 Jun 2024 16:44:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297068; cv=none; b=AkIR3glp3xvWcYltD7hRGrdRKod5A+kaiCxBN6zQOAgdlBq8iBK0j44/SmUOSQP7IykPHBdNt6ean7r6DJynTAHlJd+eLEDuokS6FDW4wwdoC0hsTnaEg1aLjNQDKYH4CUxRPNjfcS/noDr2++71rG8FZb3pCr0YgmtggTjYuu4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297068; c=relaxed/simple; bh=6qiwxDhQ8FRziLyT7fabl7gPHk/jpXioRkJvkXxPl/Q=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YnvLAbXbTKM2rjmunOORg6CSZeGL2c1ZGAwJuXSBd8mt2va9n5mMGggyoQu8uleMLRbc/v0zU/bCHQFGYwOHUmeKb1vpmY/1mO5eZzhFg+EcdDgUpr9l/YVM0AOD3f07nitjjEAFL+oVIO0W6bH8TFdEst+9ZdhALrJR0HdImkM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uyE41NcO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="uyE41NcO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 88A34C4AF50; Thu, 13 Jun 2024 16:44:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1718297068; bh=6qiwxDhQ8FRziLyT7fabl7gPHk/jpXioRkJvkXxPl/Q=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=uyE41NcOontWUiojSz3cSX2zhAqwPYQSvEqeebndnvPpjhdQ4gVxDBiIcw/Y4V747 ymz5YF6Q/p3F18VWA+aTs2cIymAR9OwzON7K+T8HNsSRl0ZOGS1jS4RnPwKvm+Q7+J rJR+j7PwlKyzUVSmiq+d68QxWgpj98kuRJpp7j+qEulIfVTwVs+9lfPwlkv64lSztH oTzDbqI3Ht6YaYsWtrGNwX4idQQXDEwY5BUd5vAMwkGHA6rT40YfefGJXsaGlF725j xlIXGzNCFDnSGy1czTXZjQfvRUAHXjk0xv+KL8TA7LrGBRJDRQs15NPTQa2tbtklJf VEnUDFHkqF65A== From: Jeff Layton Date: Thu, 13 Jun 2024 12:44:07 -0400 Subject: [PATCH nfs-utils v5 2/4] nfsdctl: asciidoc source for the manpage Precedence: bulk X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240613-nfsdctl-v5-2-0b7a39102bac@kernel.org> References: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> In-Reply-To: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> To: Steve Dickson Cc: linux-nfs@vger.kernel.org, Lorenzo Bianconi , Chuck Lever , Neil Brown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Jeff Layton X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=4597; i=jlayton@kernel.org; h=from:subject:message-id; bh=6qiwxDhQ8FRziLyT7fabl7gPHk/jpXioRkJvkXxPl/Q=; b=owEBbQKS/ZANAwAIAQAOaEEZVoIVAcsmYgBmayHow/PnLp8zSOMkP4jp9LaBRgKw20wOy/Pxn uASUfa1DFuJAjMEAAEIAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCZmsh6AAKCRAADmhBGVaC Fb1zD/9hgWVzXxtHjVdcjTzNSjRZzkTRIj+1nH4ElQiSeOLuLCLPXaDmNtCOh/WMmPJZ6K5qcGl umpPrY+ROdAaUo49YK4aBlBTH2KaumTIp8hV2anBDiBNqmT11LI/x6N7tcCaQXqCBUAIgkuTZFV 92++Y8N49odpkwHMUg9qZ3De68EdFB/3i4tWR/SpwFM+JaGiCHYE7pi5YgAyDnKuLL/ZNISEAnt 6bCS+v2Z2Y1vowD+OKcsRzaHeAhRt1XNikPXsHPu0fbkxuMFVrxrrEw4NRv4VVG15ogHzYlPZSJ bGGz2UJ9WWcSbRn8S4k5YqtWicPyIikBwLWgcydgmsw07sGLY068GWUCsf7+ghrlmnCooLh7qa+ ARAQS3FbRKGG4Xqk/0+Ed703FABZka1pEuTxgya/qA8ECmsAaCh2X4ial0EF5wcJ/VXSW1H151d bEeJ5dtw0swD4Ea6fBsAlhyQRrR1DneA0qj7Gywt92rk1+nJjkj38yUF8Aacz7emC+jyGDlQhuN 9AA6gaedgBdpgZXKl19OcyJdwPWHEg/aHQ+i8qI0Iw3FUAMHl+8PDP2ih7kjvx23jE5EyOjJpoC Y55j0vL/1iyVOAO9WnNbKj6dcDqsZIm3/Sc4mne6OSGTRaT/9E+ssSUmYqB9ptmXPgZNCxIyJH4 uAlMa3ysJgUWhlg== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Convert to manpage with: asciidoctor -b manpage nfsdctl.adoc Signed-off-by: Jeff Layton --- utils/nfsdctl/nfsdctl.adoc | 140 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/utils/nfsdctl/nfsdctl.adoc b/utils/nfsdctl/nfsdctl.adoc new file mode 100644 index 000000000000..b2f00c11829e --- /dev/null +++ b/utils/nfsdctl/nfsdctl.adoc @@ -0,0 +1,140 @@ += nfsdctl(8) +Jeff Layton +:doctype: manpage + +== NAME + +nfsdctl - control program for the Linux kernel NFS server + +== SYNOPSIS + +*nfsdctl* [ _OPTION_ ] COMMAND ... + +== DESCRIPTION + +nfsdctl is a control and query program for the in-kernel NFS server. There are several +subcommands (documented below) that allow the admin to configure or query different +aspects of the NFS server. + +To get information about different subcommand usage, pass the subcommand the +--help parameter. For example: + + nfsdctl listener --help + +== OPTIONS + +*-d, --debug*:: + Enable debug logging + +*-h, --help*:: + Print program help text + +*-V, --version*:: + Print program version + +== SUBCOMMANDS + +Each subcommand can also accept its own set of options and arguments. The +--help option is standard for all subcommands: + +*autostart*:: + Start the server using the settings in the [nfsd] section of /etc/nfs.conf. + This subcommand takes no arguments. + +*listener*:: + + Get/set the listening sockets for the server. Run this without arguments to + get a list of the sockets on which the server is currently listening. To add + or remove sockets, pass it whitespace-separated strings in the format: + + { +|- }{ protocol }:{ address }:{ port } + + The fields are: + + + to add a listener, - to remove one + protocol: protocol name (e.g. tcp, udp, rdma) + address: hostname or address + port: port number or service name + + All fields are required, except for the address. If address is an empty string, + then the listeners will be opened for INADDR_ANY and IN6ADDR_ANY_INIT for ipv6 + (if enabled). The address can be either a hostname or an IP address. IPv4 + addresses must be in dotted-quad form. IPv6 addresses should be in standard + colon separated form, and must be enclosed in square brackets. + +*status*:: + + Get information about RPCs currently executing in the server. This subcommand + takes no arguments. + +*threads*:: + + 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. + +*version*:: + + Get/set the enabled NFS versions for the server. Run without arguments to + get a list of supported versions and whether they are currently enabled or + disabled. To enable or disable a version, pass it a string in the format: + + { +|- }{ MAJOR }{.{ MINOR }} + + The fields are: + + + to enable a version, - to disable + MAJOR: the major version integer value + MINOR: the minor version integet value + + The minorversion field is optional. If not given, it will disable or enable + all minorversions for that major version. + +== EXAMPLES + +Start the server with the settings in nfs.conf: + + nfsdctl autostart + +Get a list of current listening sockets: + + nfsdctl listener + +Show the supported and enabled NFS versions: + + nfsdctl version + +Add TCP listener on all addresses (both v4 and v6), port 2049: + + nfsdctl listener +tcp::2049 + +Add RDMA listener on 1.2.3.4 port 20049: + + nfsdctl listener +rdma:1.2.3.4:20049 + +Add same listener on IPv6 address f00::ba4 port 20050: + + nfsdctl listener +rdma:[f00::ba4]:20050 + +Enable NFS version 3, disable v4.0: + + nfsdctl version +3 -4.0 + +Change the number of running threads in first pool to 256: + + nfsdctl threads 256 + +== NOTES + +nfsdctl is intended to supersede rpc.nfsd(8), which controls the nfs server +using the files under /proc/fs/nfsd. nfsdctl instead uses a netlink(7) +interface to achieve the same goals. + +Most of the commands that query the NFS server can be run as an unprivileged +user, but configuring the server typically requires an account with elevated +privileges. + +== SEE ALSO + +nfs.conf(5), rpc.nfsd(8), rpc.mountd(8), exports(5), exportfs(8), nfs.conf(5), rpc.rquotad(8), nfsstat(8), netconfig(5) From patchwork Thu Jun 13 16:44:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 13697138 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C6869149DEE for ; Thu, 13 Jun 2024 16:44:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297069; cv=none; b=bTdHBDIm1FW5GUcZ63QJIhfuAOTmK9/GqJqxKpTuK+nfP4P7ofmna0INqmwydBDBTY1WKk7zNsEyzHePOfa7bkJdYdYuJNdloOIXSOuF7bl9Um8XmZzGXbDNGeSvGbMLPNQhBXaz/w/0sHnGxcwXzdI3f+vR4Jcuso47ElIpgvE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297069; c=relaxed/simple; bh=qntMwaaweWl+Gg/eTntykRnBFONN9GK9xqHM7sT7/nI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=eF/yhoMWANK7S83luMA0/McxrN/b5c0EY58oIHCEYQEbkUU3iBVm5Qt4F33ftKgYuM4b7kFDyxtN6kzrptv1W5/5gviILHvzoTmUe1GzX2AXookqlPdU70aLbFz5FpJbJkoSgAkzSDSyTAwUNZADgnSy2eAgoVueD2VvDTSaVCs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HvLoCzGp; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HvLoCzGp" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8DE29C32786; Thu, 13 Jun 2024 16:44:28 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1718297069; bh=qntMwaaweWl+Gg/eTntykRnBFONN9GK9xqHM7sT7/nI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=HvLoCzGp8Ljv7ADHpBKrXtIQAgMWo8kgeb+Xu1hs5fOSKsSgNf3oF6WsXgjKZLGnd rk2ur9uJhSNdZIUSOPRgZOfDdZ3s+c1I4Lmfvwu6Hrrw9OShkywpChH1ISDkgKYBbp dsMImeAcAfNjsIQPDU7wEBiQFXKwL8iBO1+0Jiir2nTlMWCIghPWxmp8A5ecyKLi0A VKMa/o74HXK3R8GHwKsWoPVG0kfGKqh2NCqEJHpFcRnp/XQBjPQlQBfrBEwGRKTFhR kgXQxtlyJiFQ+1JfKSDL4lK8kWpy7w3b8V+UfcpMJy1Ql+v6Drdkv0ec+xSleMjy76 Ar8B4rFo50YbQ== From: Jeff Layton Date: Thu, 13 Jun 2024 12:44:08 -0400 Subject: [PATCH nfs-utils v5 3/4] systemd: use nfsdctl to start and stop the nfs server Precedence: bulk X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240613-nfsdctl-v5-3-0b7a39102bac@kernel.org> References: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> In-Reply-To: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> To: Steve Dickson Cc: linux-nfs@vger.kernel.org, Lorenzo Bianconi , Chuck Lever , Neil Brown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Jeff Layton X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=906; i=jlayton@kernel.org; h=from:subject:message-id; bh=qntMwaaweWl+Gg/eTntykRnBFONN9GK9xqHM7sT7/nI=; b=owEBbQKS/ZANAwAIAQAOaEEZVoIVAcsmYgBmayHopArce4G0UNl4JBkVCnEhGD6/5qHKY1CRT gyi1VA3X9aJAjMEAAEIAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCZmsh6AAKCRAADmhBGVaC FVoUEACEMsmVwstR4yAP9Mo1UKCot/yVugDyAZb4UYIOcvGOlexJaB9GHmeSnQ6cdERmNEqV4+5 1Si6IyYaz+sev5nN1aAH8hUwhDy0+HbPUxwPYe9riKzRslz2yXG9T0T7N8NkxBnSHTvS6mk+Pg4 LIaK4OtUA3P7+dXto3t1vHbhdMKGPdF3ZQ6e8/Ibb4D9RhtBFYuyYFPLFU3mOCMieTT76Wun/2y L2Cr7R/m8FW5PJgy0QQ3AE6AfTWd6REteWutoXJ+GZWe6reRFLILDOIQ9H95AxnIlVTtOgkk+LM Gf4ypvYYGEYnirolW+WLlNCftZeKXH38VxKsrZSCx1pk0VXAbQgkptaQRDLpyK6xRwo3evhdpAX mx01YHRKXZQ2ee8V8vXyjTzFxTATpFDWvEE0mo67fpSKkdL5Pjll9FU03c/iHys5sS1kNdkq6xA kBClvV9Z4+UPM/0O6bJlRCWwZvP68aY9CRmswXvNolmdBdiU9ChXq5r11JQTd8Ey4AiKJrHEQxr bq7s135aGjY9Z244romfl2qOdczA6jmAqN46Hm401qt15YMgJYgYeoRfTFT6VtxWlebbfqmA+p7 7ypFu7V6woit9ktrhhFGKUN5J5F94yDdDgFPZz9K2qj8mEVDHm4Eot1A/Zf89gdjQmNo9MRDiD/ 0iInLYisV/ZX7Ag== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Attempt to use nfsdctl to start and stop the nfs-server. If that fails for any reason, use rpc.nfsd to do it instead. Signed-off-by: Jeff Layton --- systemd/nfs-server.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service index ac17d5286d3b..8793856aa707 100644 --- a/systemd/nfs-server.service +++ b/systemd/nfs-server.service @@ -23,8 +23,8 @@ After=rpc-gssd.service gssproxy.service rpc-svcgssd.service Type=oneshot RemainAfterExit=yes ExecStartPre=-/usr/sbin/exportfs -r -ExecStart=/usr/sbin/rpc.nfsd -ExecStop=/usr/sbin/rpc.nfsd 0 +ExecStart=/bin/sh -c '/usr/sbin/nfsdctl autostart || /usr/sbin/rpc.nfsd' +ExecStop=/bin/sh -c '/usr/sbin/nfsdctl threads 0 || /usr/sbin/rpc.nfsd 0' ExecStopPost=/usr/sbin/exportfs -au ExecStopPost=/usr/sbin/exportfs -f From patchwork Thu Jun 13 16:44:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Layton X-Patchwork-Id: 13697139 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B9851149E13 for ; Thu, 13 Jun 2024 16:44:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297070; cv=none; b=hdmY2EYOARquCSkQJiuJO6Vy70igVMO87grgn+BQNh00OFrkWpoTkGEGp1dgifnFqsLlvHmRQWrvAXGVV0ie9xGO0CTn/imcyqtfEuhI5XzKANym4SVxXAnfUugPmUexgTv2zBv17orqpDX4gUrLdRESTlwQ+lm6nsXSPqIjrVE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1718297070; c=relaxed/simple; bh=pDjkJcDQSy7iagioZzAU5V/Nm4H31Sxq6T5r65wDnKA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Z/whQFC1D1pIjRlprKlKW1UTU66CcTA1MY6gyKfCe7l5lLZvI84wyNIgexzn9PQ2Su0wq4NeCXjFK+56u7i6oEEyorvi/L++UdrMdPHSYYw7ciqF9VQAHSFuzlfg5d/qgzbn03Jy0KeKrUgqtsOGYMsx1tvsg1ZYQuUeAKsdt84= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ocEDH6Q5; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ocEDH6Q5" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 93141C2BBFC; Thu, 13 Jun 2024 16:44:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1718297070; bh=pDjkJcDQSy7iagioZzAU5V/Nm4H31Sxq6T5r65wDnKA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ocEDH6Q5d2tSBtowUNi5RdCt2JhJoqlcNOlWRDTe/LsyurkzllEydlPEiPQ8kIgrD zCFLPhA1rEp6Taw80pp3rY7CnEbPIv5VHEraXiEIhNhDyaNOWuC+4Q+DlPgVPUynU9 w1BsiBNKXNyDX+Y7MQdMoUq2NDqR85qq0K9uNTkYXHjDccrYp3Bjw8BB92jtZPN7Vn GKVwS6RhItHQlmweatGf5sXY5/67mvM/Qc3GgyOcVlxfTqrHe5GPt2nBiDySbI8asn /froml79MLh75HTpNTJ0hVt19RHGo1GUIczEA72KeIvWU2J7HMUxJ3QifEi/R9FdZs Epa4SZZL1D5Xg== From: Jeff Layton Date: Thu, 13 Jun 2024 12:44:09 -0400 Subject: [PATCH nfs-utils v5 4/4] nfsdctl: add support for pool-mode Precedence: bulk X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240613-nfsdctl-v5-4-0b7a39102bac@kernel.org> References: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> In-Reply-To: <20240613-nfsdctl-v5-0-0b7a39102bac@kernel.org> To: Steve Dickson Cc: linux-nfs@vger.kernel.org, Lorenzo Bianconi , Chuck Lever , Neil Brown , Olga Kornievskaia , Dai Ngo , Tom Talpey , Jeff Layton X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10126; i=jlayton@kernel.org; h=from:subject:message-id; bh=pDjkJcDQSy7iagioZzAU5V/Nm4H31Sxq6T5r65wDnKA=; b=owEBbQKS/ZANAwAIAQAOaEEZVoIVAcsmYgBmayHp1gIMVU2RQF0opVNNrLCD2/i7CVuSpdF/b 1ShNEJCQQSJAjMEAAEIAB0WIQRLwNeyRHGyoYTq9dMADmhBGVaCFQUCZmsh6QAKCRAADmhBGVaC FdK3EACByhwsjMrCPqw8FVUbgrxFTHeJRYovlxmMRTJqAUdG81IyPdooPa7cRT88bUwLBcLVwnw +g5AzUaLBhn9eGGxg2ojsc76jKUkk1sdf8LApGfY7dOgQn4nyOhLS24I5Lk3WOAF7PJifiA79zC ER0OWhsxzw9hwBon8Qecr96uxgaIjkt1wFrbqurkRdwy92NBTtYaVtqXkrPtqCbL4oogzysQ8C0 lZyVk5emS79XRPAlkN+BAcdr5OTq1JOW2U2uYKGL2IknAx+IhBOV4K8JfVPJToWukt/GOSGH7R/ fHQSYjD4YrZVi0v1Zwcja1tYXzNqA2te8vIxbbQmnXj3+jWI5QdGfD5FmIkOs1MPn/5N1DhsaJW ZIkFwmaHE5B9Z3LW2NVEUtHNoqaOuzLCSPepwA90uNQ/+HnBfCZ0m6+nxwSwYvU+RraBVk/JwIY VDQMXcZhtPUCi3zRy/gzeU5lK58Kg9pjA1YuL9VvJ23K9Szm80GfMKtl9n5cEjeOwJ8okrGExJs e+AXoNV7iiGX66fAGWk0WUkcQXxqTEYOONMq1/X0q+8XZggA0cUHRdXRUGi7agSBBizTDuT8A6b rhrfXHMU9TRrharZ7g7f7Z1tF3Tl2eEpnrnkgZTMZ7L41A6xm4xT0jdpZCBRlhbJ/Gz81g9mAx7 AP+w5FiBa2VmGrQ== X-Developer-Key: i=jlayton@kernel.org; a=openpgp; fpr=4BC0D7B24471B2A184EAF5D3000E684119568215 Allow nfsdctl to use the new POOL_MODE_GET/SET operations to configure the pool-mode on the host. This setting mirrors the behavior of the sunrpc module parameter. The patch also adds support for a new pool-mode option in /etc/nfs.conf. With this change, you can now configure a multipool NFS server by just properly setting up /etc/nfs.conf. Signed-off-by: Jeff Layton --- configure.ac | 2 +- utils/nfsdctl/nfsd_netlink.h | 10 +++ utils/nfsdctl/nfsdctl.8 | 23 ++++++- utils/nfsdctl/nfsdctl.adoc | 12 ++++ utils/nfsdctl/nfsdctl.c | 154 ++++++++++++++++++++++++++++++++++++++----- 5 files changed, 183 insertions(+), 18 deletions(-) diff --git a/configure.ac b/configure.ac index 8cb42ebc5846..1aebc3a767a7 100644 --- a/configure.ac +++ b/configure.ac @@ -265,7 +265,7 @@ AC_ARG_ENABLE(nfsdctl, # ensure we have the pool-mode commands AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[int foo = NFSD_CMD_LISTENER_GET;]])], + [[int foo = NFSD_CMD_POOL_MODE_GET;]])], [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1, ["Use system's linux/nfsd_netlink.h"])]) fi diff --git a/utils/nfsdctl/nfsd_netlink.h b/utils/nfsdctl/nfsd_netlink.h index 24c86dbc7ed5..887cbd12b695 100644 --- a/utils/nfsdctl/nfsd_netlink.h +++ b/utils/nfsdctl/nfsd_netlink.h @@ -70,6 +70,14 @@ enum { NFSD_A_SERVER_SOCK_MAX = (__NFSD_A_SERVER_SOCK_MAX - 1) }; +enum { + NFSD_A_POOL_MODE_MODE = 1, + NFSD_A_POOL_MODE_NPOOLS, + + __NFSD_A_POOL_MODE_MAX, + NFSD_A_POOL_MODE_MAX = (__NFSD_A_POOL_MODE_MAX - 1) +}; + enum { NFSD_CMD_RPC_STATUS_GET = 1, NFSD_CMD_THREADS_SET, @@ -78,6 +86,8 @@ enum { NFSD_CMD_VERSION_GET, NFSD_CMD_LISTENER_SET, NFSD_CMD_LISTENER_GET, + NFSD_CMD_POOL_MODE_SET, + NFSD_CMD_POOL_MODE_GET, __NFSD_CMD_MAX, NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1) diff --git a/utils/nfsdctl/nfsdctl.8 b/utils/nfsdctl/nfsdctl.8 index 7f5d981042a5..d55862cc1fc1 100644 --- a/utils/nfsdctl/nfsdctl.8 +++ b/utils/nfsdctl/nfsdctl.8 @@ -2,12 +2,12 @@ .\" Title: nfsdctl .\" Author: Jeff Layton .\" Generator: Asciidoctor 2.0.20 -.\" Date: 2024-04-23 +.\" Date: 2024-06-13 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "NFSDCTL" "8" "2024-04-23" "\ \&" "\ \&" +.TH "NFSDCTL" "8" "2024-06-13" "\ \&" "\ \&" .ie \n(.g .ds Aq \(aq .el .ds Aq ' .ss \n[.ss] 0 @@ -176,6 +176,25 @@ all minorversions for that major version. .fi .if n .RE .RE +.sp +\fBpool\-mode\fP +.RS 4 +Get/set the host\(cqs pool mode. This will cause the server to start threads +that are pinned to either the CPU or the NUMA node. This can only be set +when there are no nfsd threads running. +.sp +.if n .RS 4 +.nf +.fam C +The available options are: + global: single large pool + percpu: pool per CPU + pernode: pool per NUMA node + auto: choose a mode based on host configuration +.fam +.fi +.if n .RE +.RE .SH "EXAMPLES" .sp Start the server with the settings in nfs.conf: diff --git a/utils/nfsdctl/nfsdctl.adoc b/utils/nfsdctl/nfsdctl.adoc index b2f00c11829e..03c5c281d92e 100644 --- a/utils/nfsdctl/nfsdctl.adoc +++ b/utils/nfsdctl/nfsdctl.adoc @@ -91,6 +91,18 @@ Each subcommand can also accept its own set of options and arguments. The The minorversion field is optional. If not given, it will disable or enable all minorversions for that major version. +*pool-mode*:: + + Get/set the host's pool mode. This will cause the server to start threads + that are pinned to either the CPU or the NUMA node. This can only be set + when there are no nfsd threads running. + + The available options are: + global: single large pool + percpu: pool per CPU + pernode: pool per NUMA node + auto: choose a mode based on host configuration + == EXAMPLES Start the server with the settings in nfs.conf: diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c index 986c937b620d..55bf5cd21783 100644 --- a/utils/nfsdctl/nfsdctl.c +++ b/utils/nfsdctl/nfsdctl.c @@ -320,20 +320,20 @@ static void parse_threads_get(struct genlmsghdr *gnlh) struct nlattr *a; switch (nla_type(attr)) { - case NFSD_A_SERVER_GRACETIME: - printf("gracetime: %u\n", nla_get_u32(attr)); - break; - case NFSD_A_SERVER_LEASETIME: - printf("leasetime: %u\n", nla_get_u32(attr)); - break; - case NFSD_A_SERVER_SCOPE: - printf("scope: %s\n", nla_data(attr)); - break; - case NFSD_A_SERVER_THREADS: - pool_threads[i++] = nla_get_u32(attr); - break; - default: - break; + case NFSD_A_SERVER_GRACETIME: + printf("gracetime: %u\n", nla_get_u32(attr)); + break; + case NFSD_A_SERVER_LEASETIME: + printf("leasetime: %u\n", nla_get_u32(attr)); + break; + case NFSD_A_SERVER_SCOPE: + printf("scope: %s\n", nla_data(attr)); + break; + case NFSD_A_SERVER_THREADS: + pool_threads[i++] = nla_get_u32(attr); + break; + default: + break; } } @@ -343,6 +343,26 @@ static void parse_threads_get(struct genlmsghdr *gnlh) putchar('\n'); } +static void parse_pool_mode_get(struct genlmsghdr *gnlh) +{ + 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_POOL_MODE_MODE: + printf("pool-mode: %s\n", nla_data(attr)); + break; + case NFSD_A_POOL_MODE_NPOOLS: + printf("npools: %u\n", nla_get_u32(attr)); + break; + default: + break; + } + } +} + static int recv_handler(struct nl_msg *msg, void *arg) { struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -361,6 +381,9 @@ static int recv_handler(struct nl_msg *msg, void *arg) case NFSD_CMD_LISTENER_GET: parse_listener_get(gnlh); break; + case NFSD_CMD_POOL_MODE_GET: + parse_pool_mode_get(gnlh); + break; default: break; } @@ -1158,6 +1181,95 @@ static int listener_func(struct nl_sock *sock, int argc, char ** argv) return 0; } +static int pool_mode_doit(struct nl_sock *sock, int cmd, const char *pool_mode) +{ + struct genlmsghdr *ghdr; + struct nlmsghdr *nlh; + struct nl_msg *msg; + struct nl_cb *cb; + int ret; + + msg = netlink_msg_alloc(sock); + if (!msg) + return 1; + + nlh = nlmsg_hdr(msg); + if (cmd == NFSD_CMD_POOL_MODE_SET) { + if (pool_mode) + nla_put_string(msg, NFSD_A_POOL_MODE_MODE, pool_mode); + } + ghdr = nlmsg_data(nlh); + ghdr->cmd = cmd; + + cb = nl_cb_alloc(NL_CB_CUSTOM); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + ret = 1; + goto out; + } + + ret = nl_send_auto(sock, msg); + if (ret < 0) { + fprintf(stderr, "send failed (%d)!\n", ret); + 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, NULL); + + while (ret > 0) + nl_recvmsgs(sock, cb); + if (ret < 0) { + fprintf(stderr, "Error: %s\n", strerror(-ret)); + ret = 1; + } +out_cb: + nl_cb_put(cb); +out: + nlmsg_free(msg); + return ret; +} + +static void pool_mode_usage(void) +{ + printf("Usage: %s pool-mode { global | auto | percpu | pernode } ...\n", taskname); +} + +static int pool_mode_func(struct nl_sock *sock, int argc, char **argv) +{ + uint8_t cmd = NFSD_CMD_POOL_MODE_GET; + const char *pool_mode = NULL; + int opt; + + optind = 1; + while ((opt = getopt_long(argc, argv, "h", help_only_options, NULL)) != -1) { + switch (opt) { + case 'h': + pool_mode_usage(); + return 0; + } + } + + if (optind < argc) { + char **targv = &argv[optind]; + int i; + + cmd = NFSD_CMD_POOL_MODE_SET; + + /* empty string? */ + if (targv[i][0] == '\0') { + fprintf(stderr, "Invalid threads value %s.\n", targv[i]); + return 1; + } + + pool_mode = targv[0]; + } + return pool_mode_doit(sock, cmd, pool_mode); +} + #define MAX_LISTENER_LEN (64 * 2 + 16) static int @@ -1249,7 +1361,7 @@ static int autostart_func(struct nl_sock *sock, int argc, char ** argv) int *threads, grace, lease, idx, ret, opt; struct conf_list *thread_str; struct conf_list_node *n; - char *scope; + char *scope, *pool_mode; optind = 1; while ((opt = getopt_long(argc, argv, "h", help_only_options, NULL)) != -1) { @@ -1262,6 +1374,13 @@ static int autostart_func(struct nl_sock *sock, int argc, char ** argv) read_nfsd_conf(); + pool_mode = conf_get_str("nfsd", "pool-mode"); + if (pool_mode) { + ret = pool_mode_doit(sock, NFSD_CMD_POOL_MODE_SET, pool_mode); + if (ret) + return ret; + } + ret = fetch_nfsd_versions(sock); if (ret) return ret; @@ -1312,6 +1431,7 @@ enum nfsdctl_commands { NFSDCTL_VERSION, NFSDCTL_LISTENER, NFSDCTL_AUTOSTART, + NFSDCTL_POOL_MODE, }; static int parse_command(char *str) @@ -1326,6 +1446,8 @@ static int parse_command(char *str) return NFSDCTL_LISTENER; if (!strcmp(str, "autostart")) return NFSDCTL_AUTOSTART; + if (!strcmp(str, "pool-mode")) + return NFSDCTL_POOL_MODE; return -1; } @@ -1337,6 +1459,7 @@ static nfsdctl_func func[] = { [NFSDCTL_VERSION] = version_func, [NFSDCTL_LISTENER] = listener_func, [NFSDCTL_AUTOSTART] = autostart_func, + [NFSDCTL_POOL_MODE] = pool_mode_func, }; static void usage(void) @@ -1348,6 +1471,7 @@ static void usage(void) printf(" -d | --debug=NUM enable debugging\n"); printf(" -V | --version print version info\n"); printf(" commands:\n"); + printf(" pool-mode get/set host pool mode setting\n"); printf(" listener get/set listener info\n"); printf(" version get/set supported NFS versions\n"); printf(" threads get/set nfsd thread settings\n");