Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package nfs-utils for openSUSE:Factory 
checked in at 2026-08-05 17:46:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/nfs-utils (Old)
 and      /work/SRC/openSUSE:Factory/.nfs-utils.new.16738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "nfs-utils"

Wed Aug  5 17:46:00 2026 rev:193 rq:1369307 version:unknown

Changes:
--------
--- /work/SRC/openSUSE:Factory/nfs-utils/nfs-utils.changes      2026-06-03 
20:22:32.813498112 +0200
+++ /work/SRC/openSUSE:Factory/.nfs-utils.new.16738/nfs-utils.changes   
2026-08-05 17:46:16.245354701 +0200
@@ -1,0 +2,57 @@
+Mon Aug  3 15:33:21 UTC 2026 - Dominique Leuenberger <[email protected]>
+
+- Require python3-PyYAML on openSUSE and SLE: the python-* symbol
+  is valid in both cases. On openSUSE, this is provided by the
+  module targetting the primary interpreter, which matches the
+  shebang of the python scripts lines being /usr/bin/python3.
+
+-------------------------------------------------------------------
+Mon Aug  3 09:57:15 UTC 2026 - Anthony Iliopoulos <[email protected]>
+
+- nfs-client: make the rpcctl util executable (bsc#1273197)
+
+  The specfile intentionally clears the execute bit for all python-based
+  utilities during the build, in order to prevent generating a package
+  dependency to the python interpreter. It subsequently restores the
+  execute bit explicitly for every single script. In the case of rpcctl
+  this was overlooked when the utility was introduced, and therefore it
+  was packaged without execute permissions. Fix this by restoring the
+  permissions.
+
+-------------------------------------------------------------------
+Mon Aug  3 02:36:59 UTC 2026 - Lidong Zhong <[email protected]>
+
+- Introduce new sub-package nfs-tools-extra, and move all python-based
+  tools into that (bsc#1268167).
+
+  The nfs-utils base packages previously suggested python-base as a soft
+  dependency, as some of the less used tools rely on that. Some of those
+  have further specific python dependencies (such as nfsdclnts which
+  needs pyyaml).
+
+  We do not want to pull in python as a strict requirement to any of the
+  base nfs-utils packages to keep the base as minimal as possible. Thus
+  we introduce a new package (nfs-tools-extra) that includes all those
+  optional python-based tools, and make that package require pyyaml and
+  python transitively.
+
+  Also, the base packages now suggest nfs-tools-extra, instead of
+  python.
+
+-------------------------------------------------------------------
+Fri Jul 31 20:59:19 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 2.9.2:
+  * nfsd: fix memory overflow for haddr
+  * gssd: fix memory leak in gssd_free_client
+  * Pass ignore_hosts to export_create() in export_read()
+  * mountd/exportd: disable netlink when falling back to /proc
+  * nfs.conf: add no-netlink option to exportd and mountd stanzas
+  * nfsstat: display NFSv4 callback operation statistics
+  * libnfsidmap: avoid malloc(0) for empty Local-Realms
+  * exportfs: drop unused is_export parameter from xtab_read() and xtab_write()
+  * support/backend_sqlite.c: fix getrandom() fallback
+  * nfs-iostat: add option to display throughput in MB/s
+  * exportfs: release NFSv4 state when last client is unexported
+
+-------------------------------------------------------------------

Old:
----
  nfs-utils-2.9.1.tar.sign
  nfs-utils-2.9.1.tar.xz

New:
----
  nfs-utils-2.9.2.tar.sign
  nfs-utils-2.9.2.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ nfs-utils.spec ++++++
--- /var/tmp/diff_new_pack.pxF0SY/_old  2026-08-05 17:46:17.105384823 +0200
+++ /var/tmp/diff_new_pack.pxF0SY/_new  2026-08-05 17:46:17.109384964 +0200
@@ -1,7 +1,6 @@
 #
 # spec file for package nfs-utils
 #
-# Copyright (c) 2026 SUSE LLC
 # Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
@@ -23,7 +22,7 @@
 %endif
 
 Name:           nfs-utils
-Version:        2.9.1
+Version:        2.9.2
 Release:        0
 Summary:        Support Utilities for Kernel nfsd
 License:        GPL-2.0-or-later
@@ -63,7 +62,7 @@
 BuildRequires:  pkgconfig(mount)
 BuildRequires:  pkgconfig(readline)
 BuildRequires:  pkgconfig(sqlite3)
-Suggests:       python-base
+Suggests:       nfs-tools-extra
 %{?systemd_ordering}
 
 %description
@@ -106,6 +105,15 @@
 USE_KERNEL_NFSD_NUMBER. For quota over NFS support, install the quota
 package.
 
+%package -n nfs-tools-extra
+Summary:        Extra support utils for NFS
+Group:          Productivity/Networking/NFS
+Requires:       python3-PyYAML
+
+%description -n nfs-tools-extra
+This package contains optional utilities for the client and kernel based
+server.
+
 %package -n libnfsidmap1
 Summary:        NFSv4 ID Mapping Library
 Group:          Productivity/Networking/NFS
@@ -259,15 +267,12 @@
 %{_sbindir}/mount.nfs4
 %{_sbindir}/umount.nfs
 %{_sbindir}/umount.nfs4
-%attr(0755,root,root) %{_sbindir}/mountstats
-%attr(0755,root,root) %{_sbindir}/nfsiostat
 %{_sbindir}/nfsdcld
 %{_sbindir}/nfsidmap
 %{_sbindir}/nfsstat
 %{_sbindir}/rpc.gssd
 %{_sbindir}/rpc.idmapd
 %{_sbindir}/rpc.statd
-%{_sbindir}/rpcctl
 %{_sbindir}/rpcdebug
 %{_sbindir}/showmount
 %{_sbindir}/sm-notify
@@ -308,20 +313,15 @@
 %{_mandir}/man8/gssd.8%{ext_man}
 %{_mandir}/man8/idmapd.8%{ext_man}
 %{_mandir}/man8/mount.nfs.8%{ext_man}
-%{_mandir}/man8/mountstats.8%{ext_man}
 %{_mandir}/man8/nfsconf.8%{ext_man}
 %{_mandir}/man8/nfsdcld.8%{ext_man}
-%{_mandir}/man8/nfsdclddb.8%{ext_man}
-%{_mandir}/man8/nfsdclnts.8%{ext_man}
 %{_mandir}/man8/nfsidmap.8%{ext_man}
-%{_mandir}/man8/nfsiostat.8%{ext_man}
 %{_mandir}/man8/nfsstat.8%{ext_man}
 %{_mandir}/man8/rpc.gssd.8%{ext_man}
 %{_mandir}/man8/rpc.idmapd.8%{ext_man}
 %{_mandir}/man8/rpc.sm-notify.8%{ext_man}
 %{_mandir}/man8/rpc.statd.8%{ext_man}
 %{_mandir}/man8/rpc.svcgssd.8%{ext_man}
-%{_mandir}/man8/rpcctl.8%{ext_man}
 %{_mandir}/man8/rpcdebug.8%{ext_man}
 %{_mandir}/man8/showmount.8%{ext_man}
 %{_mandir}/man8/sm-notify.8%{ext_man}
@@ -354,8 +354,6 @@
 %{_sbindir}/nfsdcltrack
 %{_sbindir}/nfsdctl
 %{_sbindir}/nfsref
-%attr(0755,root,root) %{_sbindir}/nfsdclddb
-%attr(0755,root,root) %{_sbindir}/nfsdclnts
 %{_mandir}/man5/exports.5%{ext_man}
 %{_mandir}/man7/nfsd.7%{ext_man}
 %{_mandir}/man8/exportfs.8%{ext_man}
@@ -367,6 +365,19 @@
 %{_mandir}/man8/nfsdctl.8%{ext_man}
 %{_mandir}/man8/nfsdcltrack.8%{ext_man}
 
+%files -n nfs-tools-extra
+%attr(0755,root,root) %{_sbindir}/mountstats
+%attr(0755,root,root) %{_sbindir}/nfsdclddb
+%attr(0755,root,root) %{_sbindir}/nfsdclnts
+%attr(0755,root,root) %{_sbindir}/nfsiostat
+%attr(0755,root,root) %{_sbindir}/rpcctl
+%{_sbindir}/rpcctl
+%{_mandir}/man8/mountstats.8%{ext_man}
+%{_mandir}/man8/nfsdclddb.8%{ext_man}
+%{_mandir}/man8/nfsdclnts.8%{ext_man}
+%{_mandir}/man8/nfsiostat.8%{ext_man}
+%{_mandir}/man8/rpcctl.8%{ext_man}
+
 %files -n libnfsidmap1
 %{_libdir}/libnfsidmap-1.0.0/
 %{_libdir}/libnfsidmap.so.1*

++++++ nfs-utils-2.9.1.tar.xz -> nfs-utils-2.9.2.tar.xz ++++++
++++ 1948 lines of diff (skipped)
++++    retrying with extended exclude list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/configure.ac new/nfs-utils-2.9.2/configure.ac
--- old/nfs-utils-2.9.1/configure.ac    2026-04-04 11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/configure.ac    2026-07-23 17:47:22.000000000 +0200
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl
-AC_INIT([linux nfs-utils],[2.9.1],[[email protected]],[nfs-utils])
+AC_INIT([linux nfs-utils],[2.9.2],[[email protected]],[nfs-utils])
 AC_CANONICAL_BUILD([])
 AC_CANONICAL_HOST([])
 AC_CONFIG_MACRO_DIR(aclocal)
@@ -256,10 +256,12 @@
 PKG_CHECK_MODULES(LIBNLGENL3, libnl-genl-3.0 >= 3.1)
 
 AC_CHECK_HEADERS(linux/nfsd_netlink.h)
+AC_DEFINE([HAVE_NFSD_NETLINK], 1,
+         [Define to 1 if nfsd generic netlink support is available])
 
-# ensure we have the expkey attributes
+# ensure the system netlink headers have the latest features
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
-                                          [[int foo = NFSD_A_EXPKEY_SEQNO;]])],
+                                          [[int foo = 
NFSD_CMD_SERVER_STATS_GET;]])],
                           [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1,
                                      ["Use system's linux/nfsd_netlink.h"])])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/lockd_netlink.h>]],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/nfs.conf new/nfs-utils-2.9.2/nfs.conf
--- old/nfs-utils-2.9.1/nfs.conf        2026-04-04 11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/nfs.conf        2026-07-23 17:47:22.000000000 +0200
@@ -43,6 +43,8 @@
 # threads=1
 # cache-use-ipaddr=n
 # ttl=1800
+# no-netlink=0
+#
 [mountd]
 # debug="all|auth|call|general|parse"
 # apply-root-cred=n
@@ -55,6 +57,7 @@
 # ha-callout=
 # cache-use-ipaddr=n
 # ttl=1800
+# no-netlink=0
 #
 [nfsdcld]
 # debug=0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/export/cache.c 
new/nfs-utils-2.9.2/support/export/cache.c
--- old/nfs-utils-2.9.1/support/export/cache.c  2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/support/export/cache.c  2026-07-23 17:47:22.000000000 
+0200
@@ -40,7 +40,7 @@
 #include <netlink/genl/ctrl.h>
 #include <netlink/msg.h>
 #include <netlink/attr.h>
-#include <linux/netlink.h>
+#include "compat.h"
 
 #ifdef USE_SYSTEM_NFSD_NETLINK_H
 #include <linux/nfsd_netlink.h>
@@ -3072,6 +3072,8 @@
                         * were queued before we opened the socket.
                         */
                        auth_reload();
+                       if (no_netlink)
+                               goto fallback;
                        cache_nl_process_export();
                        cache_nl_process_expkey();
                        cache_nl_process_ip_map();
@@ -3079,11 +3081,16 @@
                                cache_nl_process_unix_gid();
                        return;
                }
+fallback:
                xlog(L_NOTICE, "sunrpc netlink family unavailable, falling back 
to /proc");
                nl_socket_free(nfsd_nl_notify_sock);
                nfsd_nl_notify_sock = NULL;
                nl_socket_free(nfsd_nl_cmd_sock);
                nfsd_nl_cmd_sock = NULL;
+               nl_socket_free(sunrpc_nl_notify_sock);
+               sunrpc_nl_notify_sock = NULL;
+               nl_socket_free(sunrpc_nl_cmd_sock);
+               sunrpc_nl_cmd_sock = NULL;
        }
 
        for (i=0; cachelist[i].cache_name; i++ ) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/export/cache_flush.c 
new/nfs-utils-2.9.2/support/export/cache_flush.c
--- old/nfs-utils-2.9.1/support/export/cache_flush.c    2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/support/export/cache_flush.c    2026-07-23 
17:47:22.000000000 +0200
@@ -38,6 +38,8 @@
 #include "sunrpc_netlink.h"
 #endif
 
+#include "compat.h"
+
 static int nl_send_flush(struct nl_sock *sock, int family, int cmd)
 {
        struct nl_msg *msg;
@@ -162,5 +164,6 @@
                return;
        }
        /* Fallback: /proc path */
+       no_netlink = 1;
        cache_proc_flush();
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/export/export.c 
new/nfs-utils-2.9.2/support/export/export.c
--- old/nfs-utils-2.9.1/support/export/export.c 2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/support/export/export.c 2026-07-23 17:47:22.000000000 
+0200
@@ -122,7 +122,7 @@
        while ((eep = getexportent(0)) != NULL) {
                exp = export_lookup(eep->e_hostname, eep->e_path, ignore_hosts);
                if (!exp) {
-                       if (export_create(eep, 0))
+                       if (export_create(eep, ignore_hosts))
                                /* possible complaints already logged */
                                volumes++;
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/export/xtab.c 
new/nfs-utils-2.9.2/support/export/xtab.c
--- old/nfs-utils-2.9.1/support/export/xtab.c   2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/support/export/xtab.c   2026-07-23 17:47:22.000000000 
+0200
@@ -33,11 +33,8 @@
 static void cond_rename(char *newfile, char *oldfile);
 
 static int
-xtab_read(char *xtab, char *lockfn, int is_export)
+xtab_read(char *xtab, char *lockfn)
 {
-    /* is_export == 0  => reading /proc/fs/nfs/exports - we know these things 
are exported to kernel
-     * is_export == 1  => reading /var/lib/nfs/etab - these things are allowed 
to be exported
-     */
        struct exportent        *xp;
        nfs_export              *exp;
        int                     lockid;
@@ -45,11 +42,10 @@
        if ((lockid = xflock(lockfn, "r")) < 0)
                return 0;
        setexportent(xtab, "r");
-       if (is_export == 1)
-               v4root_needed = 1;
-       while ((xp = getexportent(is_export==0)) != NULL) {
-               if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export 
!= 1)) &&
-                   !(exp = export_create(xp, is_export!=1))) {
+       v4root_needed = 1;
+       while ((xp = getexportent(0)) != NULL) {
+               if (!(exp = export_lookup(xp->e_hostname, xp->e_path, 0)) &&
+                   !(exp = export_create(xp, 0))) {
                         if(xp->e_hostname) {
                             free(xp->e_hostname);
                             xp->e_hostname=NULL;
@@ -60,17 +56,10 @@
                         }
                        continue;
                }
-               switch (is_export) {
-               case 0:
-                       exp->m_exported = 1;
-                       break;
-               case 1:
-                       exp->m_xtabent = 1;
-                       exp->m_mayexport = 1;
-                       if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
-                               v4root_needed = 0;
-                       break;
-               }  
+               exp->m_xtabent = 1;
+               exp->m_mayexport = 1;
+               if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
+                       v4root_needed = 0;
                 if(xp->e_hostname) {
                     free(xp->e_hostname);
                     xp->e_hostname=NULL;
@@ -90,7 +79,7 @@
 int
 xtab_export_read(void)
 {
-       return xtab_read(etab.statefn, etab.lockfn, 1);
+       return xtab_read(etab.statefn, etab.lockfn);
 }
 
 /*
@@ -100,7 +89,7 @@
  * fix the auth_reload logic as well...
  */
 static int
-xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
+xtab_write(char *xtab, char *xtabtmp, char *lockfn)
 {
        struct exportent        xe;
        nfs_export              *exp;
@@ -114,9 +103,7 @@
 
        for (i = 0; i < MCL_MAXTYPES; i++) {
                for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-                       if (is_export && !exp->m_xtabent)
-                               continue;
-                       if (!is_export && ! exp->m_exported)
+                       if (!exp->m_xtabent)
                                continue;
 
                        /* write out the export entry using the FQDN */
@@ -137,7 +124,7 @@
 int
 xtab_export_write(void)
 {
-       return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1);
+       return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn);
 }
 
 /*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/include/compat.h 
new/nfs-utils-2.9.2/support/include/compat.h
--- old/nfs-utils-2.9.1/support/include/compat.h        1970-01-01 
01:00:00.000000000 +0100
+++ new/nfs-utils-2.9.2/support/include/compat.h        2026-07-23 
17:47:22.000000000 +0200
@@ -0,0 +1,10 @@
+#ifndef COMPAT_H
+#define COMPAT_H
+
+#include <linux/netlink.h>
+
+#ifndef NETLINK_EXT_ACK
+#define NETLINK_EXT_ACK 11
+#endif
+
+#endif /* COMPAT_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/include/config.h.in 
new/nfs-utils-2.9.2/support/include/config.h.in
--- old/nfs-utils-2.9.1/support/include/config.h.in     2026-04-04 
11:28:41.000000000 +0200
+++ new/nfs-utils-2.9.2/support/include/config.h.in     2026-07-28 
16:06:23.000000000 +0200
@@ -255,6 +255,9 @@
 /* Bundled lib always has the `nfs4_set_debug' function. */
 #undef HAVE_NFS4_SET_DEBUG
 
+/* Define to 1 if nfsd generic netlink support is available */
+#undef HAVE_NFSD_NETLINK
+
 /* Define this if you want NFSv4 server only support compiled in */
 #undef HAVE_NFSV4SERVER_SUPPORT
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/include/nfsd_netlink.h 
new/nfs-utils-2.9.2/support/include/nfsd_netlink.h
--- old/nfs-utils-2.9.1/support/include/nfsd_netlink.h  2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/support/include/nfsd_netlink.h  2026-07-23 
17:47:22.000000000 +0200
@@ -152,15 +152,6 @@
 };
 
 enum {
-       NFSD_A_SVC_EXPORT_REQ_SEQNO = 1,
-       NFSD_A_SVC_EXPORT_REQ_CLIENT,
-       NFSD_A_SVC_EXPORT_REQ_PATH,
-
-       __NFSD_A_SVC_EXPORT_REQ_MAX,
-       NFSD_A_SVC_EXPORT_REQ_MAX = (__NFSD_A_SVC_EXPORT_REQ_MAX - 1)
-};
-
-enum {
        NFSD_A_SVC_EXPORT_SEQNO = 1,
        NFSD_A_SVC_EXPORT_CLIENT,
        NFSD_A_SVC_EXPORT_PATH,
@@ -214,6 +205,62 @@
 };
 
 enum {
+       NFSD_A_UNLOCK_IP_ADDRESS = 1,
+
+       __NFSD_A_UNLOCK_IP_MAX,
+       NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1)
+};
+
+enum {
+       NFSD_A_UNLOCK_FILESYSTEM_PATH = 1,
+
+       __NFSD_A_UNLOCK_FILESYSTEM_MAX,
+       NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1)
+};
+
+enum {
+       NFSD_A_UNLOCK_EXPORT_PATH = 1,
+
+       __NFSD_A_UNLOCK_EXPORT_MAX,
+       NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
+};
+
+enum {
+       NFSD_A_SERVER_PROC_ENTRY_OP = 1,
+       NFSD_A_SERVER_PROC_ENTRY_COUNT,
+       NFSD_A_SERVER_PROC_ENTRY_PAD,
+
+       __NFSD_A_SERVER_PROC_ENTRY_MAX,
+       NFSD_A_SERVER_PROC_ENTRY_MAX = (__NFSD_A_SERVER_PROC_ENTRY_MAX - 1)
+};
+
+enum {
+       NFSD_A_SERVER_STATS_RC_HITS = 1,
+       NFSD_A_SERVER_STATS_RC_MISSES,
+       NFSD_A_SERVER_STATS_RC_NOCACHE,
+       NFSD_A_SERVER_STATS_PAD,
+       NFSD_A_SERVER_STATS_FH_STALE,
+       NFSD_A_SERVER_STATS_IO_READ,
+       NFSD_A_SERVER_STATS_IO_WRITE,
+       NFSD_A_SERVER_STATS_NETCNT,
+       NFSD_A_SERVER_STATS_NETUDPCNT,
+       NFSD_A_SERVER_STATS_NETTCPCNT,
+       NFSD_A_SERVER_STATS_NETTCPCONN,
+       NFSD_A_SERVER_STATS_RPCCNT,
+       NFSD_A_SERVER_STATS_RPCBADFMT,
+       NFSD_A_SERVER_STATS_RPCBADAUTH,
+       NFSD_A_SERVER_STATS_RPCBADCLNT,
+       NFSD_A_SERVER_STATS_PROC2_OPS,
+       NFSD_A_SERVER_STATS_PROC3_OPS,
+       NFSD_A_SERVER_STATS_PROC4_OPS,
+       NFSD_A_SERVER_STATS_PROC4OPS_OPS,
+       NFSD_A_SERVER_STATS_PROC4CB_OPS,
+
+       __NFSD_A_SERVER_STATS_MAX,
+       NFSD_A_SERVER_STATS_MAX = (__NFSD_A_SERVER_STATS_MAX - 1)
+};
+
+enum {
        NFSD_CMD_RPC_STATUS_GET = 1,
        NFSD_CMD_THREADS_SET,
        NFSD_CMD_THREADS_GET,
@@ -229,6 +276,10 @@
        NFSD_CMD_EXPKEY_GET_REQS,
        NFSD_CMD_EXPKEY_SET_REQS,
        NFSD_CMD_CACHE_FLUSH,
+       NFSD_CMD_UNLOCK_IP,
+       NFSD_CMD_UNLOCK_FILESYSTEM,
+       NFSD_CMD_UNLOCK_EXPORT,
+       NFSD_CMD_SERVER_STATS_GET,
 
        __NFSD_CMD_MAX,
        NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/include/nfsdnl.h 
new/nfs-utils-2.9.2/support/include/nfsdnl.h
--- old/nfs-utils-2.9.1/support/include/nfsdnl.h        1970-01-01 
01:00:00.000000000 +0100
+++ new/nfs-utils-2.9.2/support/include/nfsdnl.h        2026-07-23 
17:47:22.000000000 +0200
@@ -0,0 +1,34 @@
+/*
+ * Helper for sending nfsd generic netlink commands.
+ *
+ * Used by both nfsdctl and exportfs.
+ */
+
+#ifndef NFS_UTILS_NFSDNL_H
+#define NFS_UTILS_NFSDNL_H
+
+#ifdef HAVE_NFSD_NETLINK
+
+/**
+ * nfsd_nl_cmd_str - send an nfsd netlink command carrying a string attribute
+ * @cmd:   NFSD_CMD_* command number
+ * @attr:  NFSD_A_* attribute number
+ * @value: NUL-terminated string value for the attribute
+ *
+ * Opens a genetlink connection, resolves the "nfsd" family, sends a
+ * single "do" command with one string attribute, waits for the ACK,
+ * and cleans up.
+ *
+ * Returns 0 on success or a negative errno on failure.
+ */
+int nfsd_nl_cmd_str(int cmd, int attr, const char *value);
+
+#else
+
+static inline int nfsd_nl_cmd_str(int cmd, int attr, const char *value)
+{
+       return -ENOSYS;
+}
+
+#endif /* HAVE_NFSD_NETLINK */
+#endif /* NFS_UTILS_NFSDNL_H */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/nfs/Makefile.am 
new/nfs-utils-2.9.2/support/nfs/Makefile.am
--- old/nfs-utils-2.9.1/support/nfs/Makefile.am 2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/support/nfs/Makefile.am 2026-07-23 17:47:22.000000000 
+0200
@@ -11,6 +11,13 @@
 libnfs_la_LIBADD = libnfsconf.la -luuid
 libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) 
-I$(top_srcdir)/support/reexport
 
+if CONFIG_NFSDCTL
+libnfs_la_SOURCES += nfsdnl.c
+libnfs_la_CPPFLAGS += $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS) \
+                     -I$(top_srcdir)/utils/nfsdctl
+libnfs_la_LIBADD += $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS)
+endif
+
 libnfsconf_la_SOURCES = conffile.c xlog.c
 
 MAINTAINERCLEANFILES = Makefile.in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/nfs/fh_key_file.c 
new/nfs-utils-2.9.2/support/nfs/fh_key_file.c
--- old/nfs-utils-2.9.1/support/nfs/fh_key_file.c       2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/support/nfs/fh_key_file.c       2026-07-23 
17:47:22.000000000 +0200
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <errno.h>
+#include <string.h>
 #include <uuid/uuid.h>
 
 #include "nfslib.h"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/nfs/getport.c 
new/nfs-utils-2.9.2/support/nfs/getport.c
--- old/nfs-utils-2.9.1/support/nfs/getport.c   2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/support/nfs/getport.c   2026-07-23 17:47:22.000000000 
+0200
@@ -452,11 +452,12 @@
        uint16_t port;
        size_t count;
        char *result;
-       int len;
+       int len = sizeof(struct sockaddr);
 
        switch (sap->sa_family) {
        case AF_LOCAL:
-               return strndup(sun->sun_path, sizeof(sun->sun_path));
+               size_t path_len = len - offsetof(struct sockaddr_un, sun_path);
+               return strndup(sun->sun_path, path_len);
        case AF_INET:
                if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr,
                                        buf, (socklen_t)sizeof(buf)) == NULL)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/nfs/nfsdnl.c 
new/nfs-utils-2.9.2/support/nfs/nfsdnl.c
--- old/nfs-utils-2.9.1/support/nfs/nfsdnl.c    1970-01-01 01:00:00.000000000 
+0100
+++ new/nfs-utils-2.9.2/support/nfs/nfsdnl.c    2026-07-23 17:47:22.000000000 
+0200
@@ -0,0 +1,125 @@
+/*
+ * nfsdnl.c -- send nfsd generic netlink commands
+ *
+ * Helper shared by nfsdctl and exportfs.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "nfslib.h"
+#include "xlog.h"
+#include "nfsdnl.h"
+
+#ifdef USE_SYSTEM_NFSD_NETLINK_H
+#include <linux/nfsd_netlink.h>
+#else
+#include "nfsd_netlink.h"
+#endif
+
+#define NFSDNL_BUFSIZE (4096)
+
+static int error_handler(struct sockaddr_nl *UNUSED(nla), struct nlmsgerr *err,
+                        void *arg)
+{
+       int *ret = arg;
+       *ret = err->error;
+       return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *UNUSED(msg), void *arg)
+{
+       int *ret = arg;
+       *ret = 0;
+       return NL_SKIP;
+}
+
+static int ack_handler(struct nl_msg *UNUSED(msg),
+                      void *arg)
+{
+       int *ret = arg;
+       *ret = 0;
+       return NL_STOP;
+}
+
+/**
+ * nfsd_nl_cmd_str - send an nfsd netlink command carrying a string attribute
+ * @cmd:   NFSD_CMD_* command number
+ * @attr:  NFSD_A_* attribute number
+ * @value: NUL-terminated string value for the attribute
+ *
+ * Returns 0 on success or a negative errno on failure.
+ */
+int nfsd_nl_cmd_str(int cmd, int attr, const char *value)
+{
+       struct genlmsghdr *ghdr;
+       struct nl_sock *sock;
+       struct nl_msg *msg;
+       struct nl_cb *cb;
+       int family;
+       int ret;
+
+       sock = nl_socket_alloc();
+       if (!sock)
+               return -ENOMEM;
+       if (genl_connect(sock)) {
+               ret = -ECONNREFUSED;
+               goto out_sock;
+       }
+       nl_socket_set_buffer_size(sock, NFSDNL_BUFSIZE, NFSDNL_BUFSIZE);
+
+       family = genl_ctrl_resolve(sock, NFSD_FAMILY_NAME);
+       if (family < 0) {
+               ret = family;
+               goto out_sock;
+       }
+
+       msg = nlmsg_alloc();
+       if (!msg) {
+               ret = -ENOMEM;
+               goto out_sock;
+       }
+       if (!genlmsg_put(msg, 0, 0, family, 0, 0, 0, 0)) {
+               ret = -ENOMEM;
+               goto out_msg;
+       }
+
+       ghdr = nlmsg_data(nlmsg_hdr(msg));
+       ghdr->cmd = (__u8)cmd;
+       nla_put_string(msg, attr, value);
+
+       cb = nl_cb_alloc(NL_CB_CUSTOM);
+       if (!cb) {
+               ret = -ENOMEM;
+               goto out_msg;
+       }
+
+       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);
+
+       while (ret > 0)
+               nl_recvmsgs(sock, cb);
+
+out_cb:
+       nl_cb_put(cb);
+out_msg:
+       nlmsg_free(msg);
+out_sock:
+       nl_socket_free(sock);
+       return ret;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/nfsidmap/libnfsidmap.c 
new/nfs-utils-2.9.2/support/nfsidmap/libnfsidmap.c
--- old/nfs-utils-2.9.1/support/nfsidmap/libnfsidmap.c  2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/support/nfsidmap/libnfsidmap.c  2026-07-23 
17:47:22.000000000 +0200
@@ -409,15 +409,14 @@
        if (idmap_verbosity >= 1) {
                struct conf_list_node *r;
                char *buf = NULL;
-               int siz=0;
+               size_t siz = 1;
 
                if (local_realms) {
                        TAILQ_FOREACH(r, &local_realms->fields, link) {
                                siz += (strlen(r->field)+4);
                        }
-                       buf = malloc(siz);
+                       buf = calloc(1, siz);
                        if (buf) {
-                               *buf = 0;
                                TAILQ_FOREACH(r, &local_realms->fields, link) {
                                        sprintf(buf+strlen(buf), "'%s' ", 
r->field);
                                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/support/reexport/backend_sqlite.c 
new/nfs-utils-2.9.2/support/reexport/backend_sqlite.c
--- old/nfs-utils-2.9.1/support/reexport/backend_sqlite.c       2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/support/reexport/backend_sqlite.c       2026-07-23 
17:47:22.000000000 +0200
@@ -9,6 +9,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <sys/syscall.h>
+
 #ifdef HAVE_GETRANDOM
 # include <sys/random.h>
 # if !defined(SYS_getrandom) && defined(__NR_getrandom)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/tools/nfs-iostat/nfs-iostat.py 
new/nfs-utils-2.9.2/tools/nfs-iostat/nfs-iostat.py
--- old/nfs-utils-2.9.1/tools/nfs-iostat/nfs-iostat.py  2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/tools/nfs-iostat/nfs-iostat.py  2026-07-23 
17:47:22.000000000 +0200
@@ -327,7 +327,7 @@
             print()
             print('%d congestion waits' % congestionwaits)
 
-    def __print_rpc_op_stats(self, op, sample_time):
+    def __print_rpc_op_stats(self, op, sample_time, use_mb=False):
         """Print generic stats for one RPC op
         """
         if op not in self.__rpc_data:
@@ -343,9 +343,19 @@
         if len(rpc_stats) >= 9:
             errs = float(rpc_stats[8])
 
+        # scale to MB if requested
+        if use_mb:
+            throughput = kilobytes / 1024.0
+            throughput_label = 'MB/s'
+            per_op_label = 'MB/op'
+        else:
+            throughput = kilobytes
+            throughput_label = 'kB/s'
+            per_op_label = 'kB/op'
+
         # prevent floating point exceptions
         if ops != 0:
-            kb_per_op = kilobytes / ops
+            unit_per_op = throughput / ops
             retrans_percent = (retrans * 100) / ops
             rtt_per_op = rtt / ops
             exe_per_op = exe / ops
@@ -353,7 +363,7 @@
             if len(rpc_stats) >= 9:
                 errs_percent = (errs * 100) / ops
         else:
-            kb_per_op = 0.0
+            unit_per_op = 0.0
             retrans_percent = 0.0
             rtt_per_op = 0.0
             exe_per_op = 0.0
@@ -364,8 +374,8 @@
         op += ':'
         print(format(op.lower(), '<16s'), end='')
         print(format('ops/s', '>8s'), end='')
-        print(format('kB/s', '>16s'), end='')
-        print(format('kB/op', '>16s'), end='')
+        print(format(throughput_label, '>16s'), end='')
+        print(format(per_op_label, '>16s'), end='')
         print(format('retrans', '>16s'), end='')
         print(format('avg RTT (ms)', '>16s'), end='')
         print(format('avg exe (ms)', '>16s'), end='')
@@ -375,8 +385,8 @@
         print()
 
         print(format((ops / sample_time), '>24.3f'), end='')
-        print(format((kilobytes / sample_time), '>16.3f'), end='')
-        print(format(kb_per_op, '>16.3f'), end='')
+        print(format((throughput / sample_time), '>16.3f'), end='')
+        print(format(unit_per_op, '>16.3f'), end='')
         retransmits = '{0:>10.0f} ({1:>3.1f}%)'.format(retrans, 
retrans_percent).strip()
         print(format(retransmits, '>16'), end='')
         print(format(rtt_per_op, '>16.3f'), end='')
@@ -395,9 +405,11 @@
             sample_time = 1;
         return (sends / sample_time)
 
-    def display_iostats(self, sample_time, which):
+    def display_iostats(self, sample_time, options):
         """Display NFS and RPC stats in an iostat-like way
         """
+        which = options.which
+        use_mb = options.megabytes
         sends = float(self.__rpc_data['rpcsends'])
         if sample_time == 0:
             sample_time = float(self.__nfs_data['age'])
@@ -423,21 +435,21 @@
         print()
 
         if which == 0:
-            self.__print_rpc_op_stats('READ', sample_time)
-            self.__print_rpc_op_stats('WRITE', sample_time)
+            self.__print_rpc_op_stats('READ', sample_time, use_mb)
+            self.__print_rpc_op_stats('WRITE', sample_time, use_mb)
         elif which == 1:
-            self.__print_rpc_op_stats('GETATTR', sample_time)
-            self.__print_rpc_op_stats('ACCESS', sample_time)
+            self.__print_rpc_op_stats('GETATTR', sample_time, use_mb)
+            self.__print_rpc_op_stats('ACCESS', sample_time, use_mb)
             self.__print_attr_cache_stats(sample_time)
         elif which == 2:
-            self.__print_rpc_op_stats('LOOKUP', sample_time)
-            self.__print_rpc_op_stats('READDIR', sample_time)
+            self.__print_rpc_op_stats('LOOKUP', sample_time, use_mb)
+            self.__print_rpc_op_stats('READDIR', sample_time, use_mb)
             if 'READDIRPLUS' in self.__rpc_data:
-                self.__print_rpc_op_stats('READDIRPLUS', sample_time)
+                self.__print_rpc_op_stats('READDIRPLUS', sample_time, use_mb)
             self.__print_dir_cache_stats(sample_time)
         elif which == 3:
-            self.__print_rpc_op_stats('READ', sample_time)
-            self.__print_rpc_op_stats('WRITE', sample_time)
+            self.__print_rpc_op_stats('READ', sample_time, use_mb)
+            self.__print_rpc_op_stats('WRITE', sample_time, use_mb)
             self.__print_page_stats(sample_time)
 
         sys.stdout.flush()
@@ -500,7 +512,7 @@
 
     count = 1
     for device in devices:
-        display_stats[device].display_iostats(time, options.which)
+        display_stats[device].display_iostats(time, options)
 
         count += 1
         if (count > options.list):
@@ -585,6 +597,11 @@
                             type="int",
                             dest="list",
                             help="only print stats for first LIST mount 
points")
+    displaygroup.add_option('-m', '--megabytes',
+                            action="store_true",
+                            dest="megabytes",
+                            default=False,
+                            help="display throughput in megabytes per second 
(MB/s) instead of kilobytes per second (kB/s)")
     parser.add_option_group(displaygroup)
 
     (options, args) = parser.parse_args(sys.argv)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/tools/nfs-iostat/nfsiostat.man 
new/nfs-utils-2.9.2/tools/nfs-iostat/nfsiostat.man
--- old/nfs-utils-2.9.1/tools/nfs-iostat/nfsiostat.man  2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/tools/nfs-iostat/nfsiostat.man  2026-07-23 
17:47:22.000000000 +0200
@@ -56,16 +56,16 @@
 .RE
 .RE
 .RS 8
-- \fBkB/s\fR
+- \fBkB/s (MB/s)\fR
 .RS
-This is the number of kB written/read per second.
+This is the number of kB (or MB) written/read per second.
 .RE
 .RE
 .RE
 .RS 8
-- \fBkB/op\fR
+- \fBkB/op (MB/op)\fR
 .RS
-This is the number of kB written/read per each operation.
+This is the number of kB (or MB) written/read per each operation.
 .RE
 .RE
 .RE
@@ -122,6 +122,9 @@
 .B \-l LIST or " \-\-list=LIST 
 only print stats for first LIST mount points
 .TP
+.B \-m or " \-\-megabytes
+display throughput in megabytes per second
+.TP
 .B \-p " or " \-\-page
 displays statistics related to the page cache
 .TP
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/exportfs/exportfs.c 
new/nfs-utils-2.9.2/utils/exportfs/exportfs.c
--- old/nfs-utils-2.9.1/utils/exportfs/exportfs.c       2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/utils/exportfs/exportfs.c       2026-07-23 
17:47:22.000000000 +0200
@@ -39,6 +39,15 @@
 #include "xlog.h"
 #include "conffile.h"
 #include "reexport.h"
+#include "nfsdnl.h"
+
+#ifdef HAVE_NFSD_NETLINK
+#ifdef USE_SYSTEM_NFSD_NETLINK_H
+#include <linux/nfsd_netlink.h>
+#else
+#include "nfsd_netlink.h"
+#endif
+#endif
 
 #include <netlink/genl/genl.h>
 #include <netlink/genl/ctrl.h>
@@ -63,6 +72,7 @@
 
 static const char *lockfile = EXP_LOCKFILE;
 static int _lockfd = -1;
+static int f_unexport_all;
 
 /*
  * If we aren't careful, changes made by exportfs can be lost
@@ -246,7 +256,8 @@
         * don't care about what should be exported, as that
         * may require DNS lookups..
         */
-       if (! ( !f_export && f_all)) {
+       f_unexport_all = !f_export && f_all;
+       if (!f_unexport_all) {
                /* note: xtab_*_read does not update entries if they already 
exist,
                 * so this will not lose new options
                 */
@@ -380,6 +391,26 @@
                xlog(L_ERROR, "Invalid export syntax: %s", arg);
 }
 
+/*
+ * Check whether any active export remains for the given path across
+ * all client types.  Returns true if at least one export still has
+ * m_xtabent set.
+ */
+static int
+path_still_exported(const char *path, size_t nlen)
+{
+       nfs_export *exp;
+       int i;
+
+       for (i = 0; i < MCL_MAXTYPES; i++)
+               for (exp = exportlist[i].p_head; exp; exp = exp->m_next)
+                       if (exp->m_xtabent &&
+                           strlen(exp->m_export.e_path) == nlen &&
+                           strncmp(path, exp->m_export.e_path, nlen) == 0)
+                               return 1;
+       return 0;
+}
+
 static void
 unexportfs_parsed(char *hname, char *path, int verbose)
 {
@@ -434,9 +465,39 @@
                exp->m_mayexport = 0;
                success = 1;
        }
-       if (!success)
+       if (!success) {
                xlog(L_ERROR, "Could not find '%s:%s' to unexport.", hname, 
path);
+               goto out;
+       }
 
+       /*
+        * If no exports remain for this path, ask the kernel to
+        * revoke any NFSv4 state and close cached file handles
+        * associated with exports of this path.  This enables the
+        * underlying filesystem to be unmounted.
+        *
+        * Skip this during "exportfs -ua" -- that is a shutdown
+        * operation.  Clients should wait for nfsd to restart and
+        * reclaim state through the grace period rather than
+        * receiving NFS4ERR_ADMIN_REVOKED.
+        */
+#ifdef HAVE_NFSD_NETLINK
+       if (!f_unexport_all && !path_still_exported(path, nlen)) {
+               char pathbuf[NFS_MAXPATHLEN + 1];
+               int ret;
+
+               memcpy(pathbuf, path, nlen);
+               pathbuf[nlen] = '\0';
+               ret = nfsd_nl_cmd_str(NFSD_CMD_UNLOCK_EXPORT,
+                                     NFSD_A_UNLOCK_EXPORT_PATH,
+                                     pathbuf);
+               if (ret && ret != -ENOSYS)
+                       xlog(L_WARNING,
+                            "Failed to release state for %s: %s",
+                            pathbuf, strerror(-ret));
+       }
+#endif
+out:
        nfs_freeaddrinfo(ai);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/exportfs/exportfs.man 
new/nfs-utils-2.9.2/utils/exportfs/exportfs.man
--- old/nfs-utils-2.9.1/utils/exportfs/exportfs.man     2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/utils/exportfs/exportfs.man     2026-07-23 
17:47:22.000000000 +0200
@@ -256,6 +256,23 @@
 .I /var/lib/nfs/etab
 and removes the corresponding kernel entry (if any).
 .PP
+When the last client for a given export path is unexported,
+.B exportfs
+signals the kernel to revoke NFSv4 state (opens, locks, and
+delegations) and release cached state for that path.
+Without this revocation, retained state would prevent the
+underlying filesystem from being unmounted.
+Affected clients receive
+.B NFS4ERR_ADMIN_REVOKED
+errors for operations that use revoked state.
+.PP
+.B "exportfs \-ua"
+does not revoke NFSv4 state, however.
+If
+.B nfsd
+is then restarted, clients may reclaim state during the
+grace period.
+.PP
 .SS Dumping the Export Table
 Invoking
 .B exportfs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/gssd/gssd.c new/nfs-utils-2.9.2/utils/gssd/gssd.c
--- old/nfs-utils-2.9.1/utils/gssd/gssd.c       2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/utils/gssd/gssd.c       2026-07-23 17:47:22.000000000 
+0200
@@ -360,6 +360,10 @@
        clp->prog = program;
        clp->vers = version;
        clp->protocol = protoname;
+       clp->upcall_address = NULL;
+       clp->upcall_port = NULL;
+       clp->upcall_protoname = NULL;
+       clp->upcall_service = NULL;
 
        goto out;
 
@@ -414,16 +418,16 @@
        free(clp->servicename);
        free(clp->servername);
        free(clp->protocol);
-       if (!clp->servername) {
-               if (clp->upcall_address)
-                       free(clp->upcall_address);
-               if (clp->upcall_port)
-                       free(clp->upcall_port);
-               if (clp->upcall_protoname)
-                       free(clp->upcall_protoname);
-               if (clp->upcall_service)
-                       free(clp->upcall_service);
-       }
+
+       if (clp->upcall_address)
+               free(clp->upcall_address);
+       if (clp->upcall_port)
+               free(clp->upcall_port);
+       if (clp->upcall_protoname)
+               free(clp->upcall_protoname);
+       if (clp->upcall_service)
+               free(clp->upcall_service);
+
        free(clp);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/gssd/gssd_proc.c 
new/nfs-utils-2.9.2/utils/gssd/gssd_proc.c
--- old/nfs-utils-2.9.1/utils/gssd/gssd_proc.c  2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/utils/gssd/gssd_proc.c  2026-07-23 17:47:22.000000000 
+0200
@@ -897,6 +897,9 @@
        if (info->srchost)
                free(info->srchost);
 out_info:
+       pthread_mutex_lock(&clp_lock);
+       clp->refcount--;
+       pthread_mutex_unlock(&clp_lock);
        free(info);
        info = NULL;
        goto out;
@@ -981,6 +984,7 @@
        tinfo->timeout.tv_sec += upcall_timeout;
        TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list);
        pthread_mutex_unlock(&active_thread_list_lock);
+       pthread_attr_destroy(&attr);
 
        return ret;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/nfsd/nfsd.c new/nfs-utils-2.9.2/utils/nfsd/nfsd.c
--- old/nfs-utils-2.9.1/utils/nfsd/nfsd.c       2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/utils/nfsd/nfsd.c       2026-07-23 17:47:22.000000000 
+0200
@@ -185,7 +185,7 @@
                                hcounter = 0;
                        }
                        if (hcounter) {
-                               haddr = realloc(haddr, sizeof(char*) * 
hcounter+1);
+                               haddr = realloc(haddr, sizeof(char*) * 
(hcounter+1));
                                if(!haddr) {
                                        fprintf(stderr, "%s: unable to allocate 
"
                                                        "memory.\n", progname);
@@ -311,12 +311,16 @@
                                argv[0], count);
                        count = 1;
                } else if (count == 0) {
-                       /*
-                        * don't bother setting anything else if the threads
-                        * are coming down anyway.
-                        */
-                       socket_up = 1;
-                       goto set_threads;
+                       if (nfssvc_inuse()) {
+                               /*
+                                * don't bother setting anything else if the 
threads
+                                * are coming down anyway.
+                                */
+                               socket_up = 1;
+                               goto set_threads;
+                       } else {
+                               goto out;
+                       }
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/nfsdctl/nfsdctl.c 
new/nfs-utils-2.9.2/utils/nfsdctl/nfsdctl.c
--- old/nfs-utils-2.9.1/utils/nfsdctl/nfsdctl.c 2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/utils/nfsdctl/nfsdctl.c 2026-07-23 17:47:22.000000000 
+0200
@@ -26,6 +26,7 @@
 #include <netlink/msg.h>
 #include <netlink/attr.h>
 #include <linux/netlink.h>
+#include "compat.h"
 
 #include <readline/readline.h>
 #include <readline/history.h>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/nfsstat/Makefile.am 
new/nfs-utils-2.9.2/utils/nfsstat/Makefile.am
--- old/nfs-utils-2.9.1/utils/nfsstat/Makefile.am       2026-04-04 
11:27:07.000000000 +0200
+++ new/nfs-utils-2.9.2/utils/nfsstat/Makefile.am       2026-07-23 
17:47:22.000000000 +0200
@@ -5,8 +5,10 @@
 
 sbin_PROGRAMS  = nfsstat
 nfsstat_SOURCES = nfsstat.c
+nfsstat_CFLAGS = $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS)
 nfsstat_LDADD = ../../support/export/libexport.a \
                ../../support/nfs/libnfs.la \
-               ../../support/misc/libmisc.a
+               ../../support/misc/libmisc.a \
+               $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS)
 
 MAINTAINERCLEANFILES = Makefile.in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude 
config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 
--exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh 
old/nfs-utils-2.9.1/utils/nfsstat/nfsstat.c 
new/nfs-utils-2.9.2/utils/nfsstat/nfsstat.c
--- old/nfs-utils-2.9.1/utils/nfsstat/nfsstat.c 2026-04-04 11:27:07.000000000 
+0200
+++ new/nfs-utils-2.9.2/utils/nfsstat/nfsstat.c 2026-07-23 17:47:22.000000000 
+0200
@@ -23,6 +23,17 @@
 #include <signal.h>
 #include <time.h>
 
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#ifdef USE_SYSTEM_NFSD_NETLINK_H
+#include <linux/nfsd_netlink.h>
+#else
+#include "nfsd_netlink.h"
+#endif
+
 #define MAXNRVALS      32
 
 enum {
@@ -33,6 +44,8 @@
        SRVPROC4_SZ = 2,
        CLTPROC4_SZ = 59,
        SRVPROC4OPS_SZ = 71,
+       SRVPROC4CB_SZ = 16,     /* indexed by callback opcode; OP_CB_OFFLOAD == 
15 */
+       SRVPROC4CB_FIRST = 3,   /* first assigned callback opcode, 
OP_CB_GETATTR */
 };
 
 static unsigned int    srvproc2info[SRVPROC2_SZ+2],
@@ -49,6 +62,8 @@
                        cltproc4info_old[CLTPROC4_SZ+2];        /* NFSv4 call 
counts ([0] == 49) */
 static unsigned int    srvproc4opsinfo[SRVPROC4OPS_SZ+2],
                        srvproc4opsinfo_old[SRVPROC4OPS_SZ+2];  /* NFSv4 call 
counts ([0] == 59) */
+static unsigned int    srvproc4cbinfo[SRVPROC4CB_SZ+2],
+                       srvproc4cbinfo_old[SRVPROC4CB_SZ+2];    /* NFSv4 
callback op counts (netlink only, [0] == 16) */
 static unsigned int    srvnetinfo[5], srvnetinfo_old[5];       /* 0  # of 
received packets
                                                                 * 1  UDP 
packets
                                                                 * 2  TCP 
packets
@@ -196,6 +211,19 @@
        "write_same",
 };
 
+/*
+ * NFSv4 callback (backchannel) operations, indexed directly by RFC 8881
+ * callback opcode.  Opcodes 0-2 are not assigned, so the first three slots
+ * are placeholders to keep the array index aligned with the opcode value;
+ * display starts at SRVPROC4CB_FIRST and never prints them.
+ */
+static const char *    nfssrvproc4cbname[SRVPROC4CB_SZ] = {
+       "op0-unused",   "op1-unused",   "op2-unused",   "cb_getattr",
+       "cb_recall",    "cb_layoutrecall", "cb_notify", "cb_push_deleg",
+       "cb_recall_any","cb_recall_obj","cb_recall_slot", "cb_sequence",
+       "cb_wants_cancel", "cb_notify_lock", "cb_notify_devid", "cb_offload",
+};
+
 #define LABEL_srvnet           "Server packet stats:\n"
 #define LABEL_srvrpc           "Server rpc stats:\n"
 #define LABEL_srvrc            "Server reply cache:\n"
@@ -206,6 +234,7 @@
 #define LABEL_srvproc3         "Server nfs v3:\n"
 #define LABEL_srvproc4         "Server nfs v4:\n"
 #define LABEL_srvproc4ops      "Server nfs v4 operations:\n"
+#define LABEL_srvproc4cb       "Server nfs v4 callback operations:\n"
 #define LABEL_cltnet           "Client packet stats:\n"
 #define LABEL_cltrpc           "Client rpc stats:\n"
 #define LABEL_cltproc2         "Client nfs v2:\n"
@@ -239,6 +268,7 @@
                                        SRV(proc3,s),\
                                        SRV(proc4,s), \
                                        SRV(proc4ops,s),\
+                                       SRV(proc4cb,s),\
                                        { NULL, NULL, 0, NULL }\
                                }
 #define DECLARE_CLT(n, s...)   static statinfo n##s[] = { \
@@ -271,6 +301,7 @@
 
 static int             mounts(const char *);
 
+static int             get_stats_netlink(struct statinfo *);
 static void            get_stats(const char *, struct statinfo *, int *, int,
                                        int);
 static int             has_stats(const unsigned int *, int);
@@ -671,14 +702,24 @@
                                        nfssrvproc4name, srvproc4info + 1, 
                                        sizeof(nfssrvproc4name)/sizeof(char *));
                                print_callstats(LABEL_srvproc4ops,
-                                       nfssrvproc4opname, srvproc4opsinfo + 1, 
+                                       nfssrvproc4opname, srvproc4opsinfo + 1,
                                        sizeof(nfssrvproc4opname)/sizeof(char 
*));
+                               /*
+                                * Callback op counts are only available via
+                                * netlink; srvproc4cbinfo[0] is left zero when
+                                * stats come from /proc.
+                                */
+                               if (srvproc4cbinfo[0])
+                                       print_callstats(LABEL_srvproc4cb,
+                                               nfssrvproc4cbname + 
SRVPROC4CB_FIRST,
+                                               srvproc4cbinfo + 1 + 
SRVPROC4CB_FIRST,
+                                               SRVPROC4CB_SZ - 
SRVPROC4CB_FIRST);
                        }
                }
        }
 }
 static void
-print_client_stats(int opt_prt) 
+print_client_stats(int opt_prt)
 {
        if (opt_prt & PRNT_NET) {
                if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
@@ -794,8 +835,13 @@
                                        nfssrvproc4name, srvproc4info + 1, 
                                        sizeof(nfssrvproc4name)/sizeof(char *));
                                print_callstats_list("nfs v4 servop",
-                                       nfssrvproc4opname, srvproc4opsinfo + 1, 
+                                       nfssrvproc4opname, srvproc4opsinfo + 1,
                                        sizeof(nfssrvproc4opname)/sizeof(char 
*));
+                               if (srvproc4cbinfo[0])
+                                       print_callstats_list("nfs v4 cback",
+                                               nfssrvproc4cbname + 
SRVPROC4CB_FIRST,
+                                               srvproc4cbinfo + 1 + 
SRVPROC4CB_FIRST,
+                                               SRVPROC4CB_SZ - 
SRVPROC4CB_FIRST);
                        }
                }
        }
@@ -1051,6 +1097,278 @@
        return 1;
 }
 
+/*
+ * Netlink helpers for fetching server stats via Generic Netlink.
+ */
+static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+                           void *arg)
+{
+       int *ret = arg;
+
+       *ret = err->error;
+       return NL_SKIP;
+}
+
+static int nl_finish_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+
+       *ret = 0;
+       return NL_SKIP;
+}
+
+static int nl_ack_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+
+       *ret = 0;
+       return NL_STOP;
+}
+
+static void parse_one_proc_entry(struct nlattr *nest, unsigned int *info,
+                                unsigned int max_ops)
+{
+       struct nlattr *tb[NFSD_A_SERVER_PROC_ENTRY_MAX + 1];
+       unsigned int op, count;
+
+       nla_parse_nested(tb, NFSD_A_SERVER_PROC_ENTRY_MAX, nest, NULL);
+       if (!tb[NFSD_A_SERVER_PROC_ENTRY_OP] ||
+           !tb[NFSD_A_SERVER_PROC_ENTRY_COUNT])
+               return;
+
+       op = nla_get_u32(tb[NFSD_A_SERVER_PROC_ENTRY_OP]);
+       count = (unsigned int)nla_get_u64(tb[NFSD_A_SERVER_PROC_ENTRY_COUNT]);
+       if (op < max_ops) {
+               info[0] = max_ops;
+               info[op + 1] = count;
+       }
+}
+
+static int stats_nl_handler(struct nl_msg *msg, void *arg)
+{
+       struct statinfo *info = arg;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *attr;
+       statinfo *si;
+       int rem;
+
+       nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0),
+                         genlmsg_attrlen(gnlh, 0), rem) {
+               int type = nla_type(attr);
+
+               switch (type) {
+               /* Reply cache */
+               case NFSD_A_SERVER_STATS_RC_HITS:
+                       si = get_stat_info("rc", info);
+                       if (si)
+                               si->valptr[0] = nla_get_u64(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_RC_MISSES:
+                       si = get_stat_info("rc", info);
+                       if (si)
+                               si->valptr[1] = nla_get_u64(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_RC_NOCACHE:
+                       si = get_stat_info("rc", info);
+                       if (si)
+                               si->valptr[2] = nla_get_u64(attr);
+                       break;
+
+               /* Filehandle */
+               case NFSD_A_SERVER_STATS_FH_STALE:
+                       si = get_stat_info("fh", info);
+                       if (si)
+                               si->valptr[0] = nla_get_u64(attr);
+                       break;
+
+               /* IO */
+               case NFSD_A_SERVER_STATS_IO_READ:
+                       si = get_stat_info("io", info);
+                       if (si)
+                               si->valptr[0] = nla_get_u64(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_IO_WRITE:
+                       si = get_stat_info("io", info);
+                       if (si)
+                               si->valptr[1] = nla_get_u64(attr);
+                       break;
+
+               /* Network */
+               case NFSD_A_SERVER_STATS_NETCNT:
+                       si = get_stat_info("net", info);
+                       if (si)
+                               si->valptr[0] = nla_get_u32(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_NETUDPCNT:
+                       si = get_stat_info("net", info);
+                       if (si)
+                               si->valptr[1] = nla_get_u32(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_NETTCPCNT:
+                       si = get_stat_info("net", info);
+                       if (si)
+                               si->valptr[2] = nla_get_u32(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_NETTCPCONN:
+                       si = get_stat_info("net", info);
+                       if (si)
+                               si->valptr[3] = nla_get_u32(attr);
+                       break;
+
+               /* RPC */
+               case NFSD_A_SERVER_STATS_RPCCNT:
+                       si = get_stat_info("rpc", info);
+                       if (si)
+                               si->valptr[0] = nla_get_u32(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_RPCBADFMT:
+                       si = get_stat_info("rpc", info);
+                       if (si)
+                               si->valptr[2] = nla_get_u32(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_RPCBADAUTH:
+                       si = get_stat_info("rpc", info);
+                       if (si)
+                               si->valptr[3] = nla_get_u32(attr);
+                       break;
+               case NFSD_A_SERVER_STATS_RPCBADCLNT:
+                       si = get_stat_info("rpc", info);
+                       if (si)
+                               si->valptr[4] = nla_get_u32(attr);
+                       break;
+
+               /* Per-version procedure counts (multi-attr) */
+               case NFSD_A_SERVER_STATS_PROC2_OPS:
+                       si = get_stat_info("proc2", info);
+                       if (si)
+                               parse_one_proc_entry(attr, si->valptr,
+                                                    SRVPROC2_SZ);
+                       break;
+               case NFSD_A_SERVER_STATS_PROC3_OPS:
+                       si = get_stat_info("proc3", info);
+                       if (si)
+                               parse_one_proc_entry(attr, si->valptr,
+                                                    SRVPROC3_SZ);
+                       break;
+               case NFSD_A_SERVER_STATS_PROC4_OPS:
+                       si = get_stat_info("proc4", info);
+                       if (si)
+                               parse_one_proc_entry(attr, si->valptr,
+                                                    SRVPROC4_SZ);
+                       break;
+               case NFSD_A_SERVER_STATS_PROC4OPS_OPS:
+                       si = get_stat_info("proc4ops", info);
+                       if (si)
+                               parse_one_proc_entry(attr, si->valptr,
+                                                    SRVPROC4OPS_SZ);
+                       break;
+               case NFSD_A_SERVER_STATS_PROC4CB_OPS:
+                       si = get_stat_info("proc4cb", info);
+                       if (si)
+                               parse_one_proc_entry(attr, si->valptr,
+                                                    SRVPROC4CB_SZ);
+                       break;
+               }
+       }
+
+       return NL_OK;
+}
+
+/*
+ * Fetch server stats via Generic Netlink.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+get_stats_netlink(struct statinfo *info)
+{
+       struct nl_sock *sock;
+       struct nl_msg *msg;
+       struct nl_cb *cb;
+       int family, ret;
+       statinfo *si;
+
+       sock = nl_socket_alloc();
+       if (!sock)
+               return -1;
+
+       if (genl_connect(sock)) {
+               nl_socket_free(sock);
+               return -1;
+       }
+
+       family = genl_ctrl_resolve(sock, NFSD_FAMILY_NAME);
+       if (family < 0) {
+               nl_socket_free(sock);
+               return -1;
+       }
+
+       msg = nlmsg_alloc();
+       if (!msg) {
+               nl_socket_free(sock);
+               return -1;
+       }
+
+       if (!genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0,
+                        NLM_F_DUMP, NFSD_CMD_SERVER_STATS_GET, 0)) {
+               nlmsg_free(msg);
+               nl_socket_free(sock);
+               return -1;
+       }
+
+       cb = nl_cb_alloc(NL_CB_CUSTOM);
+       if (!cb) {
+               nlmsg_free(msg);
+               nl_socket_free(sock);
+               return -1;
+       }
+
+       ret = nl_send_auto(sock, msg);
+       if (ret < 0) {
+               nl_cb_put(cb);
+               nlmsg_free(msg);
+               nl_socket_free(sock);
+               return -1;
+       }
+
+       ret = 1;
+       nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &ret);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &ret);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &ret);
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, stats_nl_handler, info);
+
+       while (ret > 0)
+               nl_recvmsgs(sock, cb);
+
+       nl_cb_put(cb);
+       nlmsg_free(msg);
+       nl_socket_free(sock);
+
+       if (ret < 0)
+               return -1;
+
+       /*
+        * Compute derived fields. The proc file emits "rpc rpccnt
+        * badcalls badfmt badauth badclnt" where badcalls is the sum
+        * of badfmt+badauth+badclnt. The netlink interface sends the
+        * components individually, so recompute the sum here.
+        */
+       si = get_stat_info("rpc", info);
+       if (si)
+               si->valptr[1] = si->valptr[2] + si->valptr[3] + si->valptr[4];
+
+       /* Compute totals for each stat category */
+       for (si = info; si->tag; si++) {
+               unsigned int total = 0;
+               int i;
+
+               for (i = 0; i < si->nrvals - 1; i++)
+                       total += si->valptr[i];
+               si->valptr[si->nrvals - 1] = total;
+       }
+
+       return 0;
+}
+
 static void
 get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
                int is_srv)
@@ -1060,6 +1378,10 @@
        int err = 1;
        char *label = is_srv ? "Server" : "Client";
 
+       /* Try netlink first for server stats */
+       if (is_srv && get_stats_netlink(info) == 0)
+               return;
+
        /* try to guess what type of stat file we're dealing with */
        if ((fp = fopen(file, "r")) == NULL)
                goto out;

Reply via email to