Re: [PATCH nfs-utils 17/17] mountd/exportd/exportfs: add --no-netlink option to disable netlink

Jeff Layton
2026-03-16T15:26:23Z

On Mon, 2026-03-16 at 11:16 -0400, Jeff Layton wrote:
> Add a --no-netlink command-line option (short: -L) and no-netlink=
> config file setting that disables netlink support, forcing the use of
> /proc interfaces instead.
>
> For mountd and exportd, the setting lives in their respective [mountd]
> and [exportd] sections of nfs.conf. For exportfs, it's in the
> [exportfs] section.

Correction: exportfs doesn't have a section. It will look for the
setting in the mountd and exportd sections. I'll fix up the commit log
in my tree.

>
> This is useful for debugging, testing the /proc fallback path, or
> working around kernel netlink issues.
> ---
> support/export/cache.c | 3 ++-
> support/export/cache_flush.c | 4 ++-
> utils/exportd/exportd.c | 10 ++++++--
> utils/exportd/exportd.man | 12 +++++++--
> utils/exportfs/exportfs.c | 13 +++++++++-
> utils/exportfs/exportfs.man | 58 ++++++++++++++++++++++++++++++++++++++------
> utils/mountd/mountd.c | 9 ++++++-
> utils/mountd/mountd.man | 9 +++++++
> 8 files changed, 102 insertions(+), 16 deletions(-)
>
> diff --git a/support/export/cache.c b/support/export/cache.c
> index 5a2c5760cb5410845971ba831a9ae779d17a6d87..2f128d7db7bd63d86530f0c4003af58327db2c70 100644
> --- a/support/export/cache.c
> +++ b/support/export/cache.c
> @@ -111,6 +111,7 @@ static bool path_lookup_error(int err)
>
> extern int use_ipaddr;
> extern int manage_gids;
> +extern int no_netlink;
>
> static void auth_unix_ip(int f)
> {
> @@ -3064,7 +3065,7 @@ void cache_open(void)
> {
> int i;
>
> - if (cache_nfsd_nl_open() == 0) {
> + if (!no_netlink && cache_nfsd_nl_open() == 0) {
> if (cache_sunrpc_nl_open() == 0) {
> /*
> * Check for pending requests, in case any
> diff --git a/support/export/cache_flush.c b/support/export/cache_flush.c
> index 7d7f12b212967e5b3d1a2357de07bc3ba5f0b674..ed7b964f9d5372f4accba21254ee9c5f40ffd44d 100644
> --- a/support/export/cache_flush.c
> +++ b/support/export/cache_flush.c
> @@ -20,6 +20,8 @@
> #include "nfslib.h"
> #include "xlog.h"
>
> +extern int no_netlink;
> +
> #include <netlink/genl/genl.h>
> #include <netlink/genl/ctrl.h>
> #include <netlink/msg.h>
> @@ -155,7 +157,7 @@ static void cache_proc_flush(void)
> void
> cache_flush(void)
> {
> - if (cache_nl_flush() == 0) {
> + if (!no_netlink && cache_nl_flush() == 0) {
> xlog(D_NETLINK, "cache flush via netlink succeeded");
> return;
> }
> diff --git a/utils/exportd/exportd.c b/utils/exportd/exportd.c
> index a2e370ac506f56d0feab306bd252c32ef58ba009..a08aaaccbc2f2ec8504c53bbf07daf1ac2be0c32 100644
> --- a/utils/exportd/exportd.c
> +++ b/utils/exportd/exportd.c
> @@ -32,6 +32,7 @@ static int num_threads = 1;
> #define MAX_THREADS 64
>
> int manage_gids;
> +int no_netlink;
> int use_ipaddr = -1;
>
> static struct option longopts[] =
> @@ -40,13 +41,14 @@ static struct option longopts[] =
> { "debug", 1, 0, 'd' },
> { "help", 0, 0, 'h' },
> { "manage-gids", 0, 0, 'g' },
> + { "no-netlink", 0, 0, 'L' },
> { "num-threads", 1, 0, 't' },
> { "log-auth", 0, 0, 'l' },
> { "cache-use-ipaddr", 0, 0, 'i' },
> { "ttl", 0, 0, 'T' },
> { NULL, 0, 0, 0 }
> };
> -static char shortopts[] = "d:fghs:t:liT:";
> +static char shortopts[] = "d:fghs:t:liLT:";
>
> /*
> * Signal handlers.
> @@ -109,7 +111,7 @@ usage(const char *prog, int n)
> "Usage: %s [-f|--foreground] [-h|--help] [-d kind|--debug kind]\n"
> " [-g|--manage-gids] [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n"
> " [-s|--state-directory-path path]\n"
> -" [-t num|--num-threads=num]\n", prog);
> +" [-t num|--num-threads=num] [-L|--no-netlink]\n", prog);
> exit(n);
> }
>
> @@ -124,6 +126,7 @@ read_exportd_conf(char *progname, char **argv)
> xlog_set_debug(progname);
>
> manage_gids = conf_get_bool("exportd", "manage-gids", manage_gids);
> + no_netlink = conf_get_bool("exportd", "no-netlink", no_netlink);
> num_threads = conf_get_num("exportd", "threads", num_threads);
> if (conf_get_bool("mountd", "cache-use-ipaddr", 0))
> use_ipaddr = 2;
> @@ -171,6 +174,9 @@ main(int argc, char **argv)
> case 'g':
> manage_gids = 1;
> break;
> + case 'L':
> + no_netlink = 1;
> + break;
> case 'h':
> usage(progname, 0);
> break;
> diff --git a/utils/exportd/exportd.man b/utils/exportd/exportd.man
> index fae434b5f03bfb5a252f1e5c6d7fc8fc2a3f5567..d024868c6471c60f6804f427317a2627cbddb0af 100644
> --- a/utils/exportd/exportd.man
> +++ b/utils/exportd/exportd.man
> @@ -106,6 +106,13 @@ the server. Note that the 'primary' group id is not affected so a
> .B newgroup
> command on the client will still be effective. This function requires
> a Linux Kernel with version at least 2.6.21.
> +.TP
> +.B \-L " or " \-\-no-netlink
> +Disable the use of netlink for kernel communication and force the use
> +of the legacy
> +.I /proc/net/rpc
> +interfaces instead. This can be useful for debugging or working around
> +kernel netlink issues.
> .SH CONFIGURATION FILE
> Many of the options that can be set on the command line can also be
> controlled through values set in the
> @@ -120,8 +127,9 @@ Values recognized in the
> section include
> .B cache\-use\-ipaddr ,
> .BR ttl ,
> -.BR manage-gids ", and"
> -.B debug
> +.BR manage-gids ,
> +.BR no\-netlink ", and"
> +.B debug
> which each have the same effect as the option with the same name.
> .SH FILES
> .TP 2.5i
> diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
> index 04753fa169f97c6b07893613197739ff36e0d09b..9eedb750e316a2ef42bb541d4df4925deeee4874 100644
> --- a/utils/exportfs/exportfs.c
> +++ b/utils/exportfs/exportfs.c
> @@ -49,6 +49,8 @@
> #include "sunrpc_netlink.h"
> #endif
>
> +int no_netlink;
> +
> static void export_all(int verbose);
> static void exportfs(char *arg, char *options, int verbose);
> static void unexportfs(char *arg, int verbose);
> @@ -109,8 +111,11 @@ read_exportfs_conf(char **argv)
> xlog_set_debug("exportfs");
>
> /* NOTE: following uses "mountd" section of nfs.conf !!!! */
> + no_netlink = conf_get_bool("mountd", "no-netlink", no_netlink);
> s = conf_get_str("mountd", "state-directory-path");
> /* Also look in the exportd section */
> + if (!no_netlink)
> + no_netlink = conf_get_bool("exportd", "no-netlink", no_netlink);
> if (s == NULL)
> s = conf_get_str("exportd", "state-directory-path");
> if (s && !state_setup_basedir(argv[0], s))
> @@ -145,7 +150,7 @@ main(int argc, char **argv)
>
> nfsd_path_init();
>
> - while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) {
> + while ((c = getopt(argc, argv, "ad:fhiLo:ruvs")) != EOF) {
> switch(c) {
> case 'a':
> f_all = 1;
> @@ -162,6 +167,9 @@ main(int argc, char **argv)
> case 'i':
> f_ignore = 1;
> break;
> + case 'L':
> + no_netlink = 1;
> + break;
> case 'o':
> options = optarg;
> break;
> @@ -500,6 +508,8 @@ static int can_test(void)
> size_t bufsiz = sizeof(buf);
>
> /* Try netlink first: resolve sunrpc genl family */
> + if (no_netlink)
> + goto try_proc;
> sock = nl_socket_alloc();
> if (sock) {
> if (genl_connect(sock) == 0) {
> @@ -513,6 +523,7 @@ static int can_test(void)
> }
>
> /* Fallback: /proc probe */
> +try_proc:
> fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
> if (fd < 0)
> return 0;
> diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
> index af0e5571cef83d4f3de6915608b4871690a8853a..3737ee81ab275aa65e942ec1602f33a7abbfc80e 100644
> --- a/utils/exportfs/exportfs.man
> +++ b/utils/exportfs/exportfs.man
> @@ -53,14 +53,41 @@ by using the
> command.
> .PP
> .B exportfs
> -does not give any information to the kernel directly, but provides it
> -only to
> -.B rpc.mountd
> -through the
> +does not communicate with the kernel directly.
> +It writes export information to
> .I /var/lib/nfs/etab
> -file.
> +and relies on its partner programs
> +.B rpc.mountd
> +and
> +.B nfsv4.exportd
> +to manage kernel communication.
> +These daemons work in one of two modes: a netlink mode and a
> +.I /proc
> +mode.
> +.PP
> +In the netlink mode, available on sufficiently recent kernels,
> .B rpc.mountd
> -then manages kernel requests for information about exports, as needed.
> +(or
> +.BR nfsv4.exportd )
> +communicates with the kernel via generic netlink sockets.
> +The kernel sends multicast notifications when cache entries need to be
> +resolved, and the daemon responds with the appropriate export
> +information.
> +Cache flushing (via
> +.BR "exportfs \-f" )
> +is also performed over netlink.
> +This mode can be disabled with the
> +.B \-L
> +option.
> +.PP
> +In the
> +.I /proc
> +mode, used when netlink is unavailable,
> +.B rpc.mountd
> +manages kernel requests for information about exports
> +via the
> +.I /proc/net/rpc
> +channel files.
> .SH OPTIONS
> .TP
> .B \-d kind " or " \-\-debug kind
> @@ -123,6 +150,12 @@ options.
> .TP
> .B -s
> Display the current export list suitable for /etc/exports.
> +.TP
> +.B -L
> +Disable the use of netlink for kernel communication and force the use
> +of the legacy
> +.I /proc
> +interfaces for cache flushing and export validation.
>
> .SH CONFIGURATION FILE
> The
> @@ -142,11 +175,20 @@ When a list is given, the members should be comma-separated.
> .B exportfs
> will also recognize the
> .B state-directory-path
> -value from both the
> +and
> +.B no\-netlink
> +values from both the
> .B [mountd]
> section and the
> .B [exportd]
> -section
> +section.
> +When
> +.B no\-netlink
> +is set,
> +.B exportfs
> +will skip the netlink probe and use the legacy
> +.I /proc
> +interfaces for cache flushing and export validation
>
> .SH DISCUSSION
> .SS Exporting Directories
> diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
> index 6e6777cd1daa0227f3ff81f826c1cbc8627b4a8a..92d8c4690efc48fcfa12d9618cac9172c4752f4f 100644
> --- a/utils/mountd/mountd.c
> +++ b/utils/mountd/mountd.c
> @@ -41,6 +41,7 @@ static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **,
>
> int reverse_resolve = 0;
> int manage_gids;
> +int no_netlink;
> int apply_root_cred;
> int use_ipaddr = -1;
>
> @@ -72,6 +73,7 @@ static struct option longopts[] =
> { "num-threads", 1, 0, 't' },
> { "reverse-lookup", 0, 0, 'r' },
> { "manage-gids", 0, 0, 'g' },
> + { "no-netlink", 0, 0, 'L' },
> { "no-udp", 0, 0, 'u' },
> { "log-auth", 0, 0, 'l'},
> { "cache-use-ipaddr", 0, 0, 'i'},
> @@ -667,6 +669,7 @@ read_mountd_conf(char **argv)
>
> xlog_set_debug("mountd");
> manage_gids = conf_get_bool("mountd", "manage-gids", manage_gids);
> + no_netlink = conf_get_bool("mountd", "no-netlink", no_netlink);
> descriptors = conf_get_num("mountd", "descriptors", descriptors);
> port = conf_get_num("mountd", "port", port);
> num_threads = conf_get_num("mountd", "threads", num_threads);
> @@ -734,6 +737,9 @@ main(int argc, char **argv)
> case 'g':
> manage_gids = 1;
> break;
> + case 'L':
> + no_netlink = 1;
> + break;
> case 'o':
> descriptors = atoi(optarg);
> if (descriptors <= 0) {
> @@ -951,6 +957,7 @@ usage(const char *prog, int n)
> " [-N version|--no-nfs-version version] [-n|--no-tcp]\n"
> " [-H prog |--ha-callout prog] [-r |--reverse-lookup]\n"
> " [-s|--state-directory-path path] [-g|--manage-gids]\n"
> -" [-t num|--num-threads=num] [-u|--no-udp]\n", prog);
> +" [-t num|--num-threads=num] [-u|--no-udp]\n"
> +" [-L|--no-netlink]\n", prog);
> exit(n);
> }
> diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
> index 2fa396c3288f37b1afa247b54a6166ca4f1b5e06..8bec38db131d9f70d1e04a000133023cca955fe1 100644
> --- a/utils/mountd/mountd.man
> +++ b/utils/mountd/mountd.man
> @@ -284,6 +284,14 @@ the server. Note that the 'primary' group id is not affected so a
> command on the client will still be effective. This function requires
> a Linux Kernel with version at least 2.6.21.
>
> +.TP
> +.B \-L " or " \-\-no-netlink
> +Disable the use of netlink for kernel communication and force the use
> +of the legacy
> +.I /proc/net/rpc
> +interfaces instead. This can be useful for debugging or working around
> +kernel netlink issues.
> +
> .SH CONFIGURATION FILE
> Many of the options that can be set on the command line can also be
> controlled through values set in the
> @@ -297,6 +305,7 @@ Values recognized in the
> .B [mountd]
> section include
> .BR manage-gids ,
> +.BR no\-netlink ,
> .BR cache\-use\-ipaddr ,
> .BR descriptors ,
> .BR port ,

--
Jeff Layton <jlayton@kernel.org>