From 44c2df5f8b4b065dc8675fa4deaec6e103e366e3 Mon Sep 17 00:00:00 2001 From: Scott Mayhew Date: Thu, 8 May 2025 13:07:48 -0400 Subject: [PATCH] nfsiostat fixes 26f8410d mountstats/nfsiostat: merge and rework the infinite and counted loops 21238e5c mountstats/nfsiostat: Move the checks for empty mountpoint list into the print function 1bd30a9d nfsiostat: make comment explain mount/unmount more broadly 2b9251ed nfsiostat: fix crash when filtering mountstats after unmount 9836adbc nfsiostat: mirror how mountstats iostat prints the stats 936a19cc mountstats/nfsiostat: add a function to return the fstype 155c5343 nfsiostat: skip argv[0] when parsing command-line 39f57998 nfsiostat/mountstats: handle KeyError in compare_iostats() c4c14011 nfsiostat: replace 'list' reserved word Resolves: RHEL-90242 Signed-off-by: Scott Mayhew --- nfs-utils-2.3.3-nfsiostat-fixes.patch | 242 ++++++++++++++++++++++++++ nfs-utils.spec | 6 +- 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 nfs-utils-2.3.3-nfsiostat-fixes.patch diff --git a/nfs-utils-2.3.3-nfsiostat-fixes.patch b/nfs-utils-2.3.3-nfsiostat-fixes.patch new file mode 100644 index 0000000..afbebf8 --- /dev/null +++ b/nfs-utils-2.3.3-nfsiostat-fixes.patch @@ -0,0 +1,242 @@ +26f8410d mountstats/nfsiostat: merge and rework the infinite and counted loops +21238e5c mountstats/nfsiostat: Move the checks for empty mountpoint list into the print function +1bd30a9d nfsiostat: make comment explain mount/unmount more broadly +2b9251ed nfsiostat: fix crash when filtering mountstats after unmount +9836adbc nfsiostat: mirror how mountstats iostat prints the stats +936a19cc mountstats/nfsiostat: add a function to return the fstype +155c5343 nfsiostat: skip argv[0] when parsing command-line +39f57998 nfsiostat/mountstats: handle KeyError in compare_iostats() +c4c14011 nfsiostat: replace 'list' reserved word + +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index 7cbe543c..dec0e861 100644 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -493,20 +493,20 @@ def list_nfs_mounts(givenlist, mountstats): + return a full list if the given list is empty - + may return an empty list if none found + """ +- list = [] ++ devicelist = [] + if len(givenlist) > 0: + for device in givenlist: + stats = DeviceData() + stats.parse_stats(mountstats[device]) + if stats.is_nfs_mountpoint(): +- list += [device] ++ devicelist += [device] + else: + for device, descr in mountstats.items(): + stats = DeviceData() + stats.parse_stats(descr) + if stats.is_nfs_mountpoint(): +- list += [device] +- return list ++ devicelist += [device] ++ return devicelist + + def iostat_command(name): + """iostat-like command for NFS mount points +diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 014f38a3..1054f698 100755 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -560,7 +560,10 @@ class DeviceData: + # the reference to them. so we build new lists here + # for the result object. + for op in result.__rpc_data['ops']: +- result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ try: ++ result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ except KeyError: ++ continue + + # update the remaining keys + if protocol == 'udp': +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index b7e98a2a..5556f692 100755 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -213,8 +213,11 @@ class DeviceData: + # the reference to them. so we build new lists here + # for the result object. + for op in result.__rpc_data['ops']: +- result.__rpc_data[op] = list(map( +- difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ try: ++ result.__rpc_data[op] = list(map( ++ difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ except KeyError: ++ continue + + # update the remaining keys we care about + result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends'] +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index 85294fb9..e46b1a83 100755 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -187,6 +187,11 @@ class DeviceData: + found = True + self.__parse_rpc_line(words) + ++ def fstype(self): ++ """Return the fstype for the mountpoint ++ """ ++ return self.__nfs_data['fstype'] ++ + def is_nfs_mountpoint(self): + """Return True if this is an NFS or NFSv4 mountpoint, + otherwise return False +@@ -471,41 +476,31 @@ def parse_stats_file(filename): + return ms_dict + + def print_iostat_summary(old, new, devices, time, options): +- stats = {} +- diff_stats = {} +- devicelist = [] +- if old: +- # Trim device list to only include intersection of old and new data, +- # this addresses umounts due to autofs mountpoints +- for device in devices: +- if "fstype autofs" not in str(old[device]): +- devicelist.append(device) +- else: +- devicelist = devices ++ display_stats = {} ++ ++ if len(devices) == 0: ++ print('No NFS mount points were found') ++ return + +- for device in devicelist: +- stats[device] = DeviceData() +- stats[device].parse_stats(new[device]) +- if old: ++ for device in devices: ++ stats = DeviceData() ++ stats.parse_stats(new[device]) ++ if old and device in old: + old_stats = DeviceData() + old_stats.parse_stats(old[device]) +- diff_stats[device] = stats[device].compare_iostats(old_stats) ++ if stats.fstype() == old_stats.fstype(): ++ display_stats[device] = stats.compare_iostats(old_stats) ++ else: # device is in old, but fstypes are different ++ display_stats[device] = stats ++ else: # device is only in new ++ display_stats[device] = stats + + if options.sort: +- if old: +- # We now have compared data and can print a comparison +- # ordered by mountpoint ops per second +- devicelist.sort(key=lambda x: diff_stats[x].ops(time), reverse=True) +- else: +- # First iteration, just sort by newly parsed ops/s +- devicelist.sort(key=lambda x: stats[x].ops(time), reverse=True) ++ devices.sort(key=lambda x: display_stats[x].ops(time), reverse=True) + + count = 1 +- for device in devicelist: +- if old: +- diff_stats[device].display_iostats(time, options.which) +- else: +- stats[device].display_iostats(time, options.which) ++ for device in devices: ++ display_stats[device].display_iostats(time, options.which) + + count += 1 + if (count > options.list): +@@ -520,10 +515,11 @@ def list_nfs_mounts(givenlist, mountstats): + devicelist = [] + if len(givenlist) > 0: + for device in givenlist: +- stats = DeviceData() +- stats.parse_stats(mountstats[device]) +- if stats.is_nfs_mountpoint(): +- devicelist += [device] ++ if device in mountstats: ++ stats = DeviceData() ++ stats.parse_stats(mountstats[device]) ++ if stats.is_nfs_mountpoint(): ++ devicelist += [device] + else: + for device, descr in mountstats.items(): + stats = DeviceData() +@@ -592,11 +588,7 @@ client are listed. + parser.add_option_group(displaygroup) + + (options, args) = parser.parse_args(sys.argv) +- for arg in args: +- +- if arg == sys.argv[0]: +- continue +- ++ for arg in args[1:]: + if arg in mountstats: + origdevices += [arg] + elif not interval_seen: +@@ -622,47 +614,29 @@ client are listed. + print('Illegal value %s' % arg) + return + +- # make certain devices contains only NFS mount points +- devices = list_nfs_mounts(origdevices, mountstats) +- if len(devices) == 0: +- print('No NFS mount points were found') +- return +- +- + old_mountstats = None + sample_time = 0.0 + ++ # make certain devices contains only NFS mount points ++ devices = list_nfs_mounts(origdevices, mountstats) ++ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) ++ + if not interval_seen: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) + return + +- if count_seen: +- while count != 0: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) +- old_mountstats = mountstats +- time.sleep(interval) +- sample_time = interval +- mountstats = parse_stats_file('/proc/self/mountstats') +- # automount mountpoints add and drop, if automount is involved +- # we need to recheck the devices list when reparsing +- devices = list_nfs_mounts(origdevices,mountstats) +- if len(devices) == 0: +- print('No NFS mount points were found') +- return ++ while True: ++ if count_seen: + count -= 1 +- else: +- while True: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) +- old_mountstats = mountstats +- time.sleep(interval) +- sample_time = interval +- mountstats = parse_stats_file('/proc/self/mountstats') +- # automount mountpoints add and drop, if automount is involved +- # we need to recheck the devices list when reparsing +- devices = list_nfs_mounts(origdevices,mountstats) +- if len(devices) == 0: +- print('No NFS mount points were found') +- return ++ if count == 0: ++ break ++ time.sleep(interval) ++ old_mountstats = mountstats ++ sample_time = interval ++ mountstats = parse_stats_file('/proc/self/mountstats') ++ # nfs mountpoints may appear or disappear, so we need to ++ # recheck the devices list each time we parse mountstats ++ devices = list_nfs_mounts(origdevices, mountstats) ++ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) + + # + # Main diff --git a/nfs-utils.spec b/nfs-utils.spec index 1c9ed60..db7e69c 100644 --- a/nfs-utils.spec +++ b/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: 61%{?dist} +Release: 62%{?dist} Epoch: 1 # group all 32bit related archs @@ -115,6 +115,7 @@ Patch057: nfs-utils-2.3.3-covscan-return-value.patch Patch058: nfs-utils-2.3.3-gssd-man-document-use-gss-proxy.patch Patch059: nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch Patch060: nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch +Patch061: nfs-utils-2.3.3-nfsiostat-fixes.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -393,6 +394,9 @@ fi %{_libdir}/libnfsidmap.so %changelog +* Thu May 8 2025 Scott Mayhew 2.3.3-62 +- nfsiostat fixes (RHEL-90242) + * Mon Apr 28 2025 Scott Mayhew 2.3.3-61 - gssd: unconditionally use krb5_get_init_creds_opt_alloc (RHEL-62422) - gssd: do not use krb5_cc_initialize (RHEL-62422)