diff --git a/SOURCES/nfs-utils-2.3.3-nfsman-softreval.patch b/SOURCES/nfs-utils-2.3.3-nfsman-softreval.patch
new file mode 100644
index 0000000..2db2db0
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-nfsman-softreval.patch
@@ -0,0 +1,53 @@
+commit b5381c96298d75ba66625a007e2390e2b501850d
+Author: Trond Myklebust <trond.myklebust@hammerspace.com>
+Date:   Wed Jan 29 10:45:39 2020 -0500
+
+    manpage: Add a description of the 'softreval' / 'nosoftreval' mount option
+    
+    Add a description of the 'softreval' / 'nosoftreval' mount options on
+    the 'nfs' generic manpage.
+    
+    Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
+    Signed-off-by: Steve Dickson <steved@redhat.com>
+
+diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
+index 84462cd7..6f79c63a 100644
+--- a/utils/mount/nfs.man
++++ b/utils/mount/nfs.man
+@@ -121,6 +121,36 @@ option may mitigate some of the risks of using the
+ .B soft
+ option.
+ .TP 1.5i
++.BR softreval " / " nosoftreval
++In cases where the NFS server is down, it may be useful to
++allow the NFS client to continue to serve up paths and
++attributes from cache after
++.B retrans
++attempts to revalidate that cache have timed out.
++This may, for instance, be helpful when trying to unmount a
++filesystem tree from a server that is permanently down.
++.IP
++It is possible to combine
++.BR softreval
++with the
++.B soft
++mount option, in which case operations that cannot be served up
++from cache will time out and return an error after
++.B retrans
++attempts. The combination with the default
++.B hard
++mount option implies those uncached operations will continue to
++retry until a response is received from the server.
++.IP
++Note: the default mount option is
++.BR nosoftreval
++which disallows fallback to cache when revalidation fails, and
++instead follows the behavior dictated by the
++.B hard
++or
++.B soft
++mount option.
++.TP 1.5i
+ .BR intr " / " nointr
+ This option is provided for backward compatibility.
+ It is ignored after kernel 2.6.25.
diff --git a/SOURCES/nfs-utils-2.3.3-nfsrahead.patch b/SOURCES/nfs-utils-2.3.3-nfsrahead.patch
new file mode 100644
index 0000000..2863a6f
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-nfsrahead.patch
@@ -0,0 +1,399 @@
+diff --git a/.gitignore b/.gitignore
+index e97b31f5..e504d492 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -60,6 +60,8 @@ utils/statd/statd
+ tools/locktest/testlk
+ tools/getiversion/getiversion
+ tools/nfsconf/nfsconf
++tools/nfsrahead/nfsrahead
++tools/nfsrahead/99-nfs_bdi.rules
+ support/export/mount.h
+ support/export/mount_clnt.c
+ support/export/mount_xdr.c
+diff --git a/configure.ac b/configure.ac
+index 6d464ac5..f462a645 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -639,6 +639,7 @@ AC_CONFIG_FILES([
+ 	tools/rpcgen/Makefile
+ 	tools/mountstats/Makefile
+ 	tools/nfs-iostat/Makefile
++	tools/nfsrahead/Makefile
+ 	tools/rpcctl/Makefile
+ 	tools/nfsdclnts/Makefile
+ 	tools/nfsconf/Makefile
+diff --git a/nfs.conf b/nfs.conf
+index 86ed7d53..30f9e109 100644
+--- a/nfs.conf
++++ b/nfs.conf
+@@ -5,6 +5,10 @@
+ [general]
+ # pipefs-directory=/var/lib/nfs/rpc_pipefs
+ #
++[nfsrahead]
++# nfs=15000
++# nfs4=16000
++#
+ [exportfs]
+ # debug=0
+ #
+diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
+index f32c690b..ebbf28d0 100644
+--- a/systemd/nfs.conf.man
++++ b/systemd/nfs.conf.man
+@@ -245,6 +245,17 @@ Only
+ .B debug=
+ is recognized.
+ 
++.TP
++.B nfsrahead
++Recognized values:
++.BR nfs ,
++.BR nfsv4 ,
++.BR default .
++
++See
++.BR nfsrahead (5)
++for deatils.
++
+ .SH FILES
+ .I /etc/nfs.conf
+ .SH SEE ALSO
+diff --git a/tools/Makefile.am b/tools/Makefile.am
+index c3feabbe..40c17c37 100644
+--- a/tools/Makefile.am
++++ b/tools/Makefile.am
+@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
+ OPTDIRS += nfsdclddb
+ endif
+ 
+-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts nfsrahead $(OPTDIRS)
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/tools/nfsrahead/99-nfs.rules b/tools/nfsrahead/99-nfs.rules
+new file mode 100644
+index 00000000..c74914b2
+--- /dev/null
++++ b/tools/nfsrahead/99-nfs.rules
+@@ -0,0 +1 @@
++SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="/usr/libexec/nfsrahead %k", ATTR{read_ahead_kb}="%c"
+diff --git a/tools/nfsrahead/99-nfs.rules.in b/tools/nfsrahead/99-nfs.rules.in
+new file mode 100644
+index 00000000..648813c5
+--- /dev/null
++++ b/tools/nfsrahead/99-nfs.rules.in
+@@ -0,0 +1 @@
++SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="_libexecdir_/nfsrahead %k", ATTR{read_ahead_kb}="%c"
+diff --git a/tools/nfsrahead/Makefile.am b/tools/nfsrahead/Makefile.am
+new file mode 100644
+index 00000000..845ea0d5
+--- /dev/null
++++ b/tools/nfsrahead/Makefile.am
+@@ -0,0 +1,16 @@
++libexec_PROGRAMS = nfsrahead
++nfsrahead_SOURCES = main.c
++nfsrahead_LDFLAGS= -lmount
++nfsrahead_LDADD = ../../support/nfs/libnfsconf.la
++
++man5_MANS = nfsrahead.man
++EXTRA_DIST = $(man5_MANS)
++
++udev_rulesdir = /usr/lib/udev/rules.d/
++udev_rules_DATA = 99-nfs.rules
++
++99-nfs.rules: 99-nfs.rules.in $(builddefs)
++	$(SED) "s|_libexecdir_|@libexecdir@|g" 99-nfs.rules.in > $@
++
++clean-local:
++	$(RM) 99-nfs.rules
+diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c
+new file mode 100644
+index 00000000..c83c6f71
+--- /dev/null
++++ b/tools/nfsrahead/main.c
+@@ -0,0 +1,192 @@
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <unistd.h>
++
++#include <libmount/libmount.h>
++#include <sys/sysmacros.h>
++
++#include "xlog.h"
++#include "conffile.h"
++
++#ifndef MOUNTINFO_PATH
++#define MOUNTINFO_PATH "/proc/self/mountinfo"
++#endif
++
++#define CONF_NAME "nfsrahead"
++#define NFS_DEFAULT_READAHEAD 128
++
++/* Device information from the system */
++struct device_info {
++	char *device_number;
++	dev_t dev;
++	char *mountpoint;
++	char *fstype;
++};
++
++/* Convert a string in the format n:m to a device number */
++static int fill_device_number(struct device_info *info)
++{
++	char *s = strdup(info->device_number), *p;
++	char *maj_s, *min_s;
++	unsigned int maj, min;
++	int err = -EINVAL;
++
++	maj_s = p = s;
++	for ( ; *p != ':' && *p != '\0'; p++)
++		;
++
++	if (*p == '\0')
++		goto out_free;
++
++	err = 0;
++	*p = '\0';
++	min_s = p + 1;
++
++	maj = strtol(maj_s, NULL, 10);
++	min = strtol(min_s, NULL, 10);
++
++	info->dev = makedev(maj, min);
++out_free:
++	free(s);
++	return err;
++}
++
++#define sfree(ptr) if (ptr) free(ptr)
++
++/* device_info maintenance */
++static void init_device_info(struct device_info *di, const char *device_number)
++{
++	di->device_number = strdup(device_number);
++	di->dev = 0;
++	di->mountpoint = NULL;
++	di->fstype = NULL;
++}
++
++
++static void free_device_info(struct device_info *di)
++{
++	sfree(di->mountpoint);
++	sfree(di->fstype);
++	sfree(di->device_number);
++}
++
++static int get_mountinfo(const char *device_number, struct device_info *device_info, const char *mountinfo_path)
++{
++	int ret = 0;
++	struct libmnt_table *mnttbl;
++	struct libmnt_fs *fs;
++	char *target;
++
++	init_device_info(device_info, device_number);
++	if ((ret = fill_device_number(device_info)) < 0)
++		goto out_free_device_info;
++
++	mnttbl = mnt_new_table();
++
++	if ((ret = mnt_table_parse_file(mnttbl, mountinfo_path)) < 0) {
++		xlog(D_GENERAL, "Failed to parse %s\n", mountinfo_path);
++		goto out_free_tbl;
++	}
++
++	if ((fs = mnt_table_find_devno(mnttbl, device_info->dev, MNT_ITER_FORWARD)) == NULL) {
++		ret = ENOENT;
++		goto out_free_tbl;
++	}
++
++	if ((target = (char *)mnt_fs_get_target(fs)) == NULL) {
++		ret = ENOENT;
++		goto out_free_fs;
++	}
++
++	device_info->mountpoint = strdup(target);
++	target = (char *)mnt_fs_get_fstype(fs);
++	if (target)
++		device_info->fstype = strdup(target);
++
++out_free_fs:
++	mnt_free_fs(fs);
++out_free_tbl:
++	mnt_free_table(mnttbl);
++out_free_device_info:
++	free(device_info->device_number);
++	device_info->device_number = NULL;
++	return ret;
++}
++
++static int get_device_info(const char *device_number, struct device_info *device_info)
++{
++	int ret = ENOENT;
++	for (int retry_count = 0; retry_count < 10 && ret != 0; retry_count++)
++		ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH);
++
++	return ret;
++}
++
++static int conf_get_readahead(const char *kind) {
++	int readahead = 0;
++
++	if((readahead = conf_get_num(CONF_NAME, kind, -1)) == -1)
++		readahead = conf_get_num(CONF_NAME, "default", NFS_DEFAULT_READAHEAD);
++	
++	return readahead;
++}
++
++int main(int argc, char **argv)
++{
++	int ret = 0, retry, opt;
++	struct device_info device;
++	unsigned int readahead = 128, log_level, log_stderr = 0;
++
++
++	log_level = D_ALL & ~D_GENERAL;
++	while((opt = getopt(argc, argv, "dF")) != -1) {
++		switch (opt) {
++		case 'd':
++			log_level = D_ALL;
++			break;
++		case 'F':
++			log_stderr = 1;
++			break;
++		}
++	}
++
++	conf_init_file(NFS_CONFFILE);
++
++	xlog_stderr(log_stderr);
++	xlog_syslog(~log_stderr);
++	xlog_config(log_level, 1);
++	xlog_open(CONF_NAME);
++
++	// xlog_err causes the system to exit
++	if ((argc - optind) != 1)
++		xlog_err("expected the device number of a BDI; is udev ok?");
++
++	for (retry = 0; retry <= 10; retry++ )
++		if ((ret = get_device_info(argv[optind], &device)) == 0)
++			break;
++
++	if (ret != 0) {
++		xlog(D_GENERAL, "unable to find device %s\n", argv[optind]);
++		goto out;
++	}
++
++	if (strncmp("nfs", device.fstype, 3) != 0) {
++		xlog(D_GENERAL,
++			"not setting readahead for non supported fstype %s on device %s\n",
++			device.fstype, argv[optind]);
++		ret = -EINVAL;
++		goto out;
++	}
++
++	readahead = conf_get_readahead(device.fstype);
++
++	xlog(D_FAC7, "setting %s readahead to %d\n", device.mountpoint, readahead);
++
++	printf("%d\n", readahead);
++
++out:
++	free_device_info(&device);
++	return ret;
++}
+diff --git a/tools/nfsrahead/nfsrahead.man b/tools/nfsrahead/nfsrahead.man
+new file mode 100644
+index 00000000..5488f633
+--- /dev/null
++++ b/tools/nfsrahead/nfsrahead.man
+@@ -0,0 +1,72 @@
++.\" Manpage for nfsrahead.
++.nh
++.ad l
++.TH man 5 "08 Mar 2022" "1.0" "nfsrahead man page"
++.SH NAME
++
++nfsrahead \- Configure the readahead for NFS mounts
++
++.SH SYNOPSIS
++
++nfsrahead [-F] [-d] <device>
++
++.SH DESCRIPTION
++
++\fInfsrahead\fR is a tool intended to be used with udev to set the \fIread_ahead_kb\fR parameter of NFS mounts, according to the configuration file (see \fICONFIGURATION\fR). \fIdevice\fR is the device number for the NFS backing device as provided by the kernel.
++
++.SH OPTIONS
++.TP
++.B -F
++Send messages to 
++.I stderr 
++instead of
++.I syslog
++
++.TP
++.B -d
++Increase the debugging level.
++
++.SH CONFIGURATION
++.I nfsrahead
++is configured in
++.IR /etc/nfs.conf ,
++in the section titled
++.IR nfsrahead .
++It accepts the following configurations.
++
++.TP
++.B nfs=<value>
++The readahead value applied to NFSv3 mounts.
++
++.TP
++.B nfs4=<value>
++The readahead value applied to NFSv4 mounts.
++
++.TP
++.B default=<value>
++The default configuration when none of the configurations above is set.
++
++.SH EXAMPLE CONFIGURATION
++[nfsrahead]
++.br
++nfs=15000              # readahead of 15000 for NFSv3 mounts
++.br
++nfs4=16000             # readahead of 16000 for NFSv4 mounts
++.br
++default=128            # default is 128
++
++.SH SEE ALSO
++
++.BR mount.nfs (8),
++.BR nfs (5),
++.BR nfs.conf (5),
++.BR udev (7),
++.BR bcc-readahead (8)
++
++.SH BUGS
++
++No known bugs.
++
++.SH AUTHOR
++
++Thiago Rafael Becker <trbecker@gmail.com>
+diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man
+index 2af16f31..1911c41b 100644
+--- a/utils/nfsidmap/nfsidmap.man
++++ b/utils/nfsidmap/nfsidmap.man
+@@ -2,7 +2,7 @@
+ .\"@(#)nfsidmap(8) - The NFS idmapper upcall program
+ .\"
+ .\" Copyright (C) 2010 Bryan Schumaker <bjschuma@netapp.com>
+-.TH nfsidmap 5 "1 October 2010"
++.TH nfsidmap 8 "1 October 2010"
+ .SH NAME
+ nfsidmap \- The NFS idmapper upcall program
+ .SH SYNOPSIS
diff --git a/SOURCES/nfs-utils-2.3.3-rpcctl-posixpath.patch b/SOURCES/nfs-utils-2.3.3-rpcctl-posixpath.patch
new file mode 100644
index 0000000..ea1e5b8
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-rpcctl-posixpath.patch
@@ -0,0 +1,12 @@
+diff -up nfs-utils-2.3.3/tools/rpcctl/rpcctl.py.orig nfs-utils-2.3.3/tools/rpcctl/rpcctl.py
+--- nfs-utils-2.3.3/tools/rpcctl/rpcctl.py.orig	2022-06-27 13:22:19.844747880 -0400
++++ nfs-utils-2.3.3/tools/rpcctl/rpcctl.py	2022-06-27 13:23:02.168004219 -0400
+@@ -213,7 +213,7 @@ class RpcClient:
+     def __init__(self, path):
+         self.path = path
+         self.name = path.stem
+-        self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",")
++        self.switch = XprtSwitch(path / os.readlink(path / "switch"), sep=",")
+ 
+     def __lt__(self, rhs):
+         return self.name < rhs.name
diff --git a/SOURCES/nfs-utils-2.3.3-rpcctl-subparser.patch b/SOURCES/nfs-utils-2.3.3-rpcctl-subparser.patch
new file mode 100644
index 0000000..6bdc813
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-rpcctl-subparser.patch
@@ -0,0 +1,34 @@
+From 2fdd10bebf395b51e931a10adbdc85f3a3f8a285 Mon Sep 17 00:00:00 2001
+From: Alice Mitchell <ajmitchell@redhat.com>
+Date: Thu, 23 Jun 2022 16:04:45 +0100
+Subject: [PATCH] Remove subparser required option as that was added in py3.7
+
+---
+ tools/rpcctl/rpcctl.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py
+index d2110ad6..2ac6ede9 100755
+--- a/tools/rpcctl/rpcctl.py
++++ b/tools/rpcctl/rpcctl.py
+@@ -120,7 +120,7 @@ class Xprt:
+         set = subparser.add_parser("set", help="Change an xprt property")
+         set.add_argument("xprt", metavar="XPRT", nargs=1,
+                          help="Name of a specific xprt to modify")
+-        subparser = set.add_subparsers(required=True)
++        subparser = set.add_subparsers()
+         online = subparser.add_parser("online", help="Set an xprt online")
+         online.set_defaults(func=Xprt.set_property, property="online")
+         offline = subparser.add_parser("offline", help="Set an xprt offline")
+@@ -185,7 +185,7 @@ class XprtSwitch:
+         set = subparser.add_parser("set", help="Change an xprt switch property")
+         set.add_argument("switch", metavar="SWITCH", nargs=1,
+                          help="Name of a specific xprt switch to modify")
+-        subparser = set.add_subparsers(required=True)
++        subparser = set.add_subparsers()
+         dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr")
+         dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
+                              help="The new address for the xprt switch")
+-- 
+2.36.1
+
diff --git a/SOURCES/nfs-utils-2.3.3-rpcctl.patch b/SOURCES/nfs-utils-2.3.3-rpcctl.patch
new file mode 100644
index 0000000..cec8437
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-rpcctl.patch
@@ -0,0 +1,384 @@
+diff --git a/configure.ac b/configure.ac
+index f2f2303b..6d464ac5 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -639,6 +639,7 @@ AC_CONFIG_FILES([
+ 	tools/rpcgen/Makefile
+ 	tools/mountstats/Makefile
+ 	tools/nfs-iostat/Makefile
++	tools/rpcctl/Makefile
+ 	tools/nfsdclnts/Makefile
+ 	tools/nfsconf/Makefile
+ 	tools/nfsdclddb/Makefile
+diff --git a/tools/Makefile.am b/tools/Makefile.am
+index 9b4b0803..c3feabbe 100644
+--- a/tools/Makefile.am
++++ b/tools/Makefile.am
+@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
+ OPTDIRS += nfsdclddb
+ endif
+ 
+-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS)
++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/tools/rpcctl/Makefile.am b/tools/rpcctl/Makefile.am
+new file mode 100644
+index 00000000..33fb431f
+--- /dev/null
++++ b/tools/rpcctl/Makefile.am
+@@ -0,0 +1,13 @@
++## Process this file with automake to produce Makefile.in
++PYTHON_FILES =  rpcctl.py
++
++man8_MANS = rpcctl.man
++
++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES)
++
++all-local: $(PYTHON_FILES)
++
++install-data-hook:
++	$(INSTALL) -m 755 rpcctl.py $(DESTDIR)$(sbindir)/rpcctl
++
++MAINTAINERCLEANFILES=Makefile.in
+diff --git a/tools/rpcctl/rpcctl.man b/tools/rpcctl/rpcctl.man
+new file mode 100644
+index 00000000..b87ba0df
+--- /dev/null
++++ b/tools/rpcctl/rpcctl.man
+@@ -0,0 +1,67 @@
++.\"
++.\" rpcctl(8)
++.\"
++.TH rpcctl 8 "15 Feb 2022"
++.SH NAME
++rpcctl \- Displays SunRPC connection information
++.SH SYNOPSIS
++.nf
++.BR rpcctl " [ \fB\-h \fR| \fB\-\-help \fR] { \fBclient \fR| \fBswitch \fR| \fBxprt \fR}"
++.P
++.BR "rpcctl client" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBshow \fR}"
++.BR "rpcctl client show " "\fR[ \fB\-h \f| \fB\-\-help \fR] [ \fIXPRT \fR]"
++.P
++.BR "rpcctl switch" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBset \fR| \fBshow \fR}"
++.BR "rpcctl switch set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fISWITCH \fBdstaddr \fINEWADDR"
++.BR "rpcctl switch show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fISWITCH \fR]"
++.P
++.BR "rpcctl xprt" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBremove \fR| \fBset \fR| \fBshow \fR}"
++.BR "rpcctl xprt remove" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT"
++.BR "rpcctl xprt set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT \fR{ \fBdstaddr \fINEWADDR \fR| \fBoffline \fR| \fBonline \fR}"
++.BR "rpcctl xprt show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fIXPRT \fR]"
++.fi
++.SH DESCRIPTION
++.RB "The " rpcctl " command displays information collected in the SunRPC sysfs files about the system's SunRPC objects.
++.P
++.SS rpcctl client \fR- \fBCommands operating on RPC clients
++.IP "\fBshow \fR[ \fICLIENT \fR] \fB(default)"
++Show detailed information about the RPC clients on this system.
++If \fICLIENT \fRwas provided, then only show information about a single RPC client.
++.P
++.SS rpcctl switch \fR- \fBCommands operating on groups of transports
++.IP "\fBset \fISWITCH \fBdstaddr \fINEWADDR"
++Change the destination address of all transports in the \fISWITCH \fRto \fINEWADDR\fR.
++\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3).
++.IP "\fBshow \fR[ \fISWITCH \fR] \fB(default)"
++Show detailed information about groups of transports on this system.
++If \fISWITCH \fRwas provided, then only show information about a single transport group.
++.P
++.SS rpcctl xprt \fR- \fBCommands operating on individual transports
++.IP "\fBremove \fIXPRT"
++Removes the specified \fIXPRT \fRfrom the system.
++Note that "main" transports cannot be removed.
++.P
++.IP "\fBset \fIXPRT \fBdstaddr \fINEWADDR"
++Change the destination address of the specified \fIXPRT \fR to \fINEWADDR\fR.
++\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3).
++.P
++.IP "\fBset \fIXPRT \fBoffline"
++Sets the specified \fIXPRT\fR's state to offline.
++.P
++.IP "\fBset \fIXPRT \fBonline"
++Sets the specified \fIXPRT\fR's state to online.
++.IP "\fBshow \fR[ \fIXPRT \fR] \fB(default)"
++Show detailed information about this system's transports.
++If \fIXPRT \fRwas provided, then only show information about a single transport.
++.SH EXAMPLES
++.IP "\fBrpcctl switch show switch-2"
++Show details about the RPC switch named "switch-2".
++.IP "\fBrpcctl xprt remove xprt-4"
++Remove the xprt named "xprt-4" from the system.
++.IP "\fBrpcctl xprt set xprt-3 dstaddr https://linux-nfs.org
++Change the dstaddr of the xprt named "xprt-3" to point to linux-nfs.org
++.SH DIRECTORY
++.TP
++.B /sys/kernel/sunrpc/
++.SH AUTHOR
++Anna Schumaker <Anna.Schumaker@Netapp.com>
+diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py
+new file mode 100755
+index 00000000..d2110ad6
+--- /dev/null
++++ b/tools/rpcctl/rpcctl.py
+@@ -0,0 +1,262 @@
++#!/usr/bin/python3
++import argparse
++import collections
++import errno
++import os
++import pathlib
++import socket
++import sys
++
++with open("/proc/mounts", 'r') as f:
++    mount = [ line.split()[1] for line in f if "sysfs" in line ]
++    if len(mount) == 0:
++        print("ERROR: sysfs is not mounted")
++        sys.exit(1)
++
++sunrpc = pathlib.Path(mount[0]) / "kernel" / "sunrpc"
++if not sunrpc.is_dir():
++    print("ERROR: sysfs does not have sunrpc directory")
++    sys.exit(1)
++
++def read_addr_file(path):
++    try:
++        with open(path, 'r') as f:
++            return f.readline().strip()
++    except:
++        return "(enoent)"
++
++def write_addr_file(path, newaddr):
++    with open(path, 'w') as f:
++        f.write(newaddr)
++    return read_addr_file(path)
++
++def read_info_file(path):
++    res = collections.defaultdict(int)
++    try:
++        with open(path) as info:
++            lines = [ l.split("=", 1) for l in info if "=" in l ]
++            res.update({ key:int(val.strip()) for (key, val) in lines })
++    finally:
++        return res
++
++
++class Xprt:
++    def __init__(self, path):
++        self.path = path
++        self.name = path.stem.rsplit("-", 1)[0]
++        self.type = path.stem.split("-")[2]
++        self.info = read_info_file(path / "xprt_info")
++        self.dstaddr = read_addr_file(path / "dstaddr")
++        self.srcaddr = read_addr_file(path / "srcaddr")
++        self.read_state()
++
++    def __lt__(self, rhs):
++        return self.name < rhs.name
++
++    def _xprt(self):
++        main = ", main" if self.info.get("main_xprt") else ""
++        return f"{self.name}: {self.type}, {self.dstaddr}, " \
++               f"port {self.info['dst_port']}, state <{self.state}>{main}"
++
++    def _src_reqs(self):
++        return f"	Source: {self.srcaddr}, port {self.info['src_port']}, " \
++               f"Requests: {self.info['num_reqs']}"
++
++    def _cong_slots(self):
++        return f"	Congestion: cur {self.info['cur_cong']}, win {self.info['cong_win']}, " \
++               f"Slots: min {self.info['min_num_slots']}, max {self.info['max_num_slots']}"
++
++    def _queues(self):
++        return f"	Queues: binding {self.info['binding_q_len']}, " \
++               f"sending {self.info['sending_q_len']}, pending {self.info['pending_q_len']}, " \
++               f"backlog {self.info['backlog_q_len']}, tasks {self.info['tasks_queuelen']}"
++
++    def __str__(self):
++        if not self.path.exists():
++            return f"{self.name}: has been removed"
++        return "\n".join([self._xprt(), self._src_reqs(),
++                          self._cong_slots(), self._queues() ])
++
++    def read_state(self):
++        if self.path.exists():
++            with open(self.path / "xprt_state") as f:
++                self.state = ','.join(f.readline().split()[1:])
++
++    def small_str(self):
++        main = " [main]" if self.info.get("main_xprt") else ""
++        return f"{self.name}: {self.type}, {self.dstaddr}{main}"
++
++    def set_dstaddr(self, newaddr):
++        self.dstaddr = write_addr_file(self.path / "dstaddr", newaddr)
++
++    def set_state(self, state):
++        if self.info.get("main_xprt"):
++            raise Exception(f"Main xprts cannot be set {state}")
++        with open(self.path / "xprt_state", 'w') as f:
++            f.write(state)
++        self.read_state()
++
++    def remove(self):
++        if self.info.get("main_xprt"):
++            raise Exception("Main xprts cannot be removed")
++        self.set_state("offline")
++        self.set_state("remove")
++
++    def add_command(subparser):
++        parser = subparser.add_parser("xprt", help="Commands for individual xprts")
++        parser.set_defaults(func=Xprt.show, xprt=None)
++        subparser = parser.add_subparsers()
++
++        remove = subparser.add_parser("remove", help="Remove an xprt")
++        remove.add_argument("xprt", metavar="XPRT", nargs=1,
++                            help="Name of the xprt to remove")
++        remove.set_defaults(func=Xprt.set_property, property="remove")
++
++        show = subparser.add_parser("show", help="Show xprts")
++        show.add_argument("xprt", metavar="XPRT", nargs='?',
++                          help="Name of a specific xprt to show")
++        show.set_defaults(func=Xprt.show)
++
++        set = subparser.add_parser("set", help="Change an xprt property")
++        set.add_argument("xprt", metavar="XPRT", nargs=1,
++                         help="Name of a specific xprt to modify")
++        subparser = set.add_subparsers(required=True)
++        online = subparser.add_parser("online", help="Set an xprt online")
++        online.set_defaults(func=Xprt.set_property, property="online")
++        offline = subparser.add_parser("offline", help="Set an xprt offline")
++        offline.set_defaults(func=Xprt.set_property, property="offline")
++        dstaddr = subparser.add_parser("dstaddr", help="Change an xprt's dstaddr")
++        dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
++                             help="The new address for the xprt")
++        dstaddr.set_defaults(func=Xprt.set_property, property="dstaddr")
++
++    def get_by_name(name):
++        glob = f"**/{name}-*" if name else "**/xprt-*"
++        res = [ Xprt(x) for x in (sunrpc / "xprt-switches").glob(glob) ]
++        if name and len(res) == 0:
++            raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
++                                    f"{sunrpc / 'xprt-switches' / glob}")
++        return sorted(res)
++
++    def show(args):
++        for xprt in Xprt.get_by_name(args.xprt):
++            print(xprt)
++
++    def set_property(args):
++        for xprt in Xprt.get_by_name(args.xprt[0]):
++            if args.property == "dstaddr":
++                xprt.set_dstaddr(socket.gethostbyname(args.newaddr[0]))
++            elif args.property == "remove":
++                xprt.remove()
++            else:
++                xprt.set_state(args.property)
++        print(xprt)
++
++
++class XprtSwitch:
++    def __init__(self, path, sep=":"):
++        self.path = path
++        self.name = path.stem
++        self.info = read_info_file(path / "xprt_switch_info")
++        self.xprts = sorted([ Xprt(p) for p in self.path.iterdir() if p.is_dir() ])
++        self.sep = sep
++
++    def __lt__(self, rhs):
++        return self.name < rhs.name
++
++    def __str__(self):
++        switch =  f"{self.name}{self.sep} " \
++                  f"xprts {self.info['num_xprts']}, " \
++                  f"active {self.info['num_active']}, " \
++                  f"queue {self.info['queue_len']}"
++        xprts = [ f"	{x.small_str()}" for x in self.xprts ]
++        return "\n".join([ switch ] + xprts)
++
++    def add_command(subparser):
++        parser = subparser.add_parser("switch", help="Commands for xprt switches")
++        parser.set_defaults(func=XprtSwitch.show, switch=None)
++        subparser = parser.add_subparsers()
++
++        show = subparser.add_parser("show", help="Show xprt switches")
++        show.add_argument("switch", metavar="SWITCH", nargs='?',
++                          help="Name of a specific switch to show")
++        show.set_defaults(func=XprtSwitch.show)
++
++        set = subparser.add_parser("set", help="Change an xprt switch property")
++        set.add_argument("switch", metavar="SWITCH", nargs=1,
++                         help="Name of a specific xprt switch to modify")
++        subparser = set.add_subparsers(required=True)
++        dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr")
++        dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
++                             help="The new address for the xprt switch")
++        dstaddr.set_defaults(func=XprtSwitch.set_property, property="dstaddr")
++
++    def get_by_name(name):
++        xprt_switches = sunrpc / "xprt-switches"
++        if name:
++            return [ XprtSwitch(xprt_switches / name) ]
++        return [ XprtSwitch(f) for f in sorted(xprt_switches.iterdir()) ]
++
++    def show(args):
++        for switch in XprtSwitch.get_by_name(args.switch):
++            print(switch)
++
++    def set_property(args):
++        for switch in XprtSwitch.get_by_name(args.switch[0]):
++            resolved = socket.gethostbyname(args.newaddr[0])
++            for xprt in switch.xprts:
++                xprt.set_dstaddr(resolved)
++        print(switch)
++
++
++class RpcClient:
++    def __init__(self, path):
++        self.path = path
++        self.name = path.stem
++        self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",")
++
++    def __lt__(self, rhs):
++        return self.name < rhs.name
++
++    def __str__(self):
++        return f"{self.name}: {self.switch}"
++
++    def add_command(subparser):
++        parser = subparser.add_parser("client", help="Commands for rpc clients")
++        parser.set_defaults(func=RpcClient.show, client=None)
++        subparser = parser.add_subparsers()
++
++        show = subparser.add_parser("show", help="Show rpc clients")
++        show.add_argument("client", metavar="CLIENT", nargs='?',
++                          help="Name of a specific rpc client to show")
++        parser.set_defaults(func=RpcClient.show)
++
++    def get_by_name(name):
++        rpc_clients = sunrpc / "rpc-clients"
++        if name:
++            return [ RpcClient(rpc_clients / name) ]
++        return [ RpcClient(f) for f in sorted(rpc_clients.iterdir()) ]
++
++    def show(args):
++        for client in RpcClient.get_by_name(args.client):
++            print(client)
++
++
++parser = argparse.ArgumentParser()
++
++def show_small_help(args):
++    parser.print_usage()
++    print("sunrpc dir:", sunrpc)
++parser.set_defaults(func=show_small_help)
++
++subparser = parser.add_subparsers(title="commands")
++RpcClient.add_command(subparser)
++XprtSwitch.add_command(subparser)
++Xprt.add_command(subparser)
++
++args = parser.parse_args()
++try:
++    args.func(args)
++except Exception as e:
++    print(str(e))
++    sys.exit(1)
diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec
index 0a9e740..452baa6 100644
--- a/SPECS/nfs-utils.spec
+++ b/SPECS/nfs-utils.spec
@@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
 Name: nfs-utils
 URL: http://linux-nfs.org/
 Version: 2.3.3
-Release: 51%{?dist}
+Release: 56%{?dist}
 Epoch: 1
 
 # group all 32bit related archs
@@ -87,11 +87,21 @@ Patch045: nfs-utils-2.3.3-gssd-mutex-refcnt.patch
 
 #
 # RHEL 8.6
+#
 Patch046: nfs-utils-2.3.3-mountd-v4-logging.patch
 Patch047: nfs-utils-2.3.3-gssd-printerr.patch
 Patch048: nfs-utils-2.3.3-mount-ebusy.patch
 Patch049: nfs-utils-2.3.3-nfsidmap-debug.patch
 
+#
+# RHEL 8.7
+#
+Patch050: nfs-utils-2.3.3-nfsman-softreval.patch
+Patch051: nfs-utils-2.3.3-rpcctl.patch
+Patch052: nfs-utils-2.3.3-nfsrahead.patch
+Patch053: nfs-utils-2.3.3-rpcctl-subparser.patch
+Patch054: nfs-utils-2.3.3-rpcctl-posixpath.patch
+
 Patch100: nfs-utils-1.2.1-statdpath-man.patch
 Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
 Patch102: nfs-utils-2.3.3-idmap-errmsg.patch
@@ -343,6 +353,9 @@ fi
 %{_sbindir}/nfsdclddb
 %{_sbindir}/nfsdcld
 %{_sbindir}/nfsdclnts
+%{_sbindir}/rpcctl
+%{_libexecdir}/nfsrahead
+%{_udevrulesdir}/99-nfs.rules
 %{_mandir}/*/*
 %{_pkgdir}/*/*
 
@@ -366,6 +379,21 @@ fi
 %{_libdir}/libnfsidmap.so
 
 %changelog
+* Thu Jun 30 2022 Steve Dickson <steved@redhat.com> 2.3.3-56
+- rpcctl: 'PosixPath' object has no attribute 'readlink' (bz 2087187)
+
+* Mon Jun 27 2022 Steve Dickson <steved@redhat.com> 2.3.3-55
+- rpcctl: Remove subparser required option as that was added in py3.7 (bz 2087187)
+
+* Tue Jun 14 2022 Steve Dickson <steved@redhat.com> 2.3.3-54
+- Create the nfsrahead command (bz 1946283)
+
+* Tue May 31 2022 Steve Dickson <steved@redhat.com> 2.3.3-53
+- rpcctl: Add a rpcctl.py tool (bz 2087187)
+
+* Tue May 24 2022 Steve Dickson <steved@redhat.com> 2.3.3-52
+- manpage: Add a description of the softreval/nosoftreval (bz 2073476)
+
 * Mon Mar  7 2022 Steve Dickson <steved@redhat.com> 2.3.3-51
 - libnfsidmap: Turn off default verbosity (bz 2057612)