- Added -D_FILE_OFFSET_BITS=64 to CFLAGS

- make nfsstat read and print stats as unsigned integers
- Added (but not installed) the mountstats and nfs-iostat python scripts.
This commit is contained in:
Steve Dickson 2008-06-23 18:03:20 +00:00
parent 6e8eb7f040
commit 4f0634d672
6 changed files with 1326 additions and 3 deletions

View File

@ -0,0 +1,42 @@
commit 589a913e42476a965b686c9f2656b786eaae399e
Author: Tom Talpey <tmt@netapp.com>
Date: Mon Jun 23 12:54:08 2008 -0400
Add RDMA as a supported transport for reporting the
mountstats statistics
Signed-off-by: Tom Talpey <tmt@netapp.com>
Acked-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
index 5f20db6..f55595e 100644
--- a/tools/mountstats/mountstats.py
+++ b/tools/mountstats/mountstats.py
@@ -116,6 +116,26 @@ class DeviceData:
self.__rpc_data['badxids'] = int(words[9])
self.__rpc_data['inflightsends'] = long(words[10])
self.__rpc_data['backlogutil'] = int(words[11])
+ elif words[1] == 'rdma':
+ self.__rpc_data['port'] = words[2]
+ self.__rpc_data['bind_count'] = int(words[3])
+ self.__rpc_data['connect_count'] = int(words[4])
+ self.__rpc_data['connect_time'] = int(words[5])
+ self.__rpc_data['idle_time'] = int(words[6])
+ self.__rpc_data['rpcsends'] = int(words[7])
+ self.__rpc_data['rpcreceives'] = int(words[8])
+ self.__rpc_data['badxids'] = int(words[9])
+ self.__rpc_data['backlogutil'] = int(words[10])
+ self.__rpc_data['read_chunks'] = int(words[11])
+ self.__rpc_data['write_chunks'] = int(words[12])
+ self.__rpc_data['reply_chunks'] = int(words[13])
+ self.__rpc_data['total_rdma_req'] = int(words[14])
+ self.__rpc_data['total_rdma_rep'] = int(words[15])
+ self.__rpc_data['pullup'] = int(words[16])
+ self.__rpc_data['fixup'] = int(words[17])
+ self.__rpc_data['hardway'] = int(words[18])
+ self.__rpc_data['failed_marshal'] = int(words[19])
+ self.__rpc_data['bad_reply'] = int(words[20])
elif words[0] == 'per-op':
self.__rpc_data['per-op'] = words
else:

View File

@ -0,0 +1,605 @@
commit c761709ad3abb9c36a68c269f78118bf49d79639
Author: Chuck Lever <chuck.lever@oracle.com>
Date: Mon Jun 23 12:52:33 2008 -0400
The "mountstats" utility is a Python program that extracts and displays NFS
client performance information from /proc/self/mountstats.
Note that if mountstats is named 'ms-nfsstat' or 'ms-iostat' it offers
slightly different functionality. It needs two man pages and the install
script should provide both commands by installing the script and providing the
other command via a symlink.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
new file mode 100644
index 0000000..5f20db6
--- /dev/null
+++ b/tools/mountstats/mountstats.py
@@ -0,0 +1,584 @@
+#!/usr/bin/env python
+# -*- python-mode -*-
+"""Parse /proc/self/mountstats and display it in human readable form
+"""
+
+__copyright__ = """
+Copyright (C) 2005, Chuck Lever <cel@netapp.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys, os, time
+
+Mountstats_version = '0.2'
+
+def difference(x, y):
+ """Used for a map() function
+ """
+ return x - y
+
+class DeviceData:
+ """DeviceData objects provide methods for parsing and displaying
+ data for a single mount grabbed from /proc/self/mountstats
+ """
+ def __init__(self):
+ self.__nfs_data = dict()
+ self.__rpc_data = dict()
+ self.__rpc_data['ops'] = []
+
+ def __parse_nfs_line(self, words):
+ if words[0] == 'device':
+ self.__nfs_data['export'] = words[1]
+ self.__nfs_data['mountpoint'] = words[4]
+ self.__nfs_data['fstype'] = words[7]
+ if words[7].find('nfs') != -1:
+ self.__nfs_data['statvers'] = words[8]
+ elif words[0] == 'age:':
+ self.__nfs_data['age'] = long(words[1])
+ elif words[0] == 'opts:':
+ self.__nfs_data['mountoptions'] = ''.join(words[1:]).split(',')
+ elif words[0] == 'caps:':
+ self.__nfs_data['servercapabilities'] = ''.join(words[1:]).split(',')
+ elif words[0] == 'nfsv4:':
+ self.__nfs_data['nfsv4flags'] = ''.join(words[1:]).split(',')
+ elif words[0] == 'sec:':
+ keys = ''.join(words[1:]).split(',')
+ self.__nfs_data['flavor'] = int(keys[0].split('=')[1])
+ self.__nfs_data['pseudoflavor'] = 0
+ if self.__nfs_data['flavor'] == 6:
+ self.__nfs_data['pseudoflavor'] = int(keys[1].split('=')[1])
+ elif words[0] == 'events:':
+ self.__nfs_data['inoderevalidates'] = int(words[1])
+ self.__nfs_data['dentryrevalidates'] = int(words[2])
+ self.__nfs_data['datainvalidates'] = int(words[3])
+ self.__nfs_data['attrinvalidates'] = int(words[4])
+ self.__nfs_data['syncinodes'] = int(words[5])
+ self.__nfs_data['vfsopen'] = int(words[6])
+ self.__nfs_data['vfslookup'] = int(words[7])
+ self.__nfs_data['vfspermission'] = int(words[8])
+ self.__nfs_data['vfsreadpage'] = int(words[9])
+ self.__nfs_data['vfsreadpages'] = int(words[10])
+ self.__nfs_data['vfswritepage'] = int(words[11])
+ self.__nfs_data['vfswritepages'] = int(words[12])
+ self.__nfs_data['vfsreaddir'] = int(words[13])
+ self.__nfs_data['vfsflush'] = int(words[14])
+ self.__nfs_data['vfsfsync'] = int(words[15])
+ self.__nfs_data['vfslock'] = int(words[16])
+ self.__nfs_data['vfsrelease'] = int(words[17])
+ self.__nfs_data['setattrtrunc'] = int(words[18])
+ self.__nfs_data['extendwrite'] = int(words[19])
+ self.__nfs_data['sillyrenames'] = int(words[20])
+ self.__nfs_data['shortreads'] = int(words[21])
+ self.__nfs_data['shortwrites'] = int(words[22])
+ self.__nfs_data['delay'] = int(words[23])
+ elif words[0] == 'bytes:':
+ self.__nfs_data['normalreadbytes'] = long(words[1])
+ self.__nfs_data['normalwritebytes'] = long(words[2])
+ self.__nfs_data['directreadbytes'] = long(words[3])
+ self.__nfs_data['directwritebytes'] = long(words[4])
+ self.__nfs_data['serverreadbytes'] = long(words[5])
+ self.__nfs_data['serverwritebytes'] = long(words[6])
+
+ def __parse_rpc_line(self, words):
+ if words[0] == 'RPC':
+ self.__rpc_data['statsvers'] = float(words[3])
+ self.__rpc_data['programversion'] = words[5]
+ elif words[0] == 'xprt:':
+ self.__rpc_data['protocol'] = words[1]
+ if words[1] == 'udp':
+ self.__rpc_data['port'] = int(words[2])
+ self.__rpc_data['bind_count'] = int(words[3])
+ self.__rpc_data['rpcsends'] = int(words[4])
+ self.__rpc_data['rpcreceives'] = int(words[5])
+ self.__rpc_data['badxids'] = int(words[6])
+ self.__rpc_data['inflightsends'] = long(words[7])
+ self.__rpc_data['backlogutil'] = long(words[8])
+ elif words[1] == 'tcp':
+ self.__rpc_data['port'] = words[2]
+ self.__rpc_data['bind_count'] = int(words[3])
+ self.__rpc_data['connect_count'] = int(words[4])
+ self.__rpc_data['connect_time'] = int(words[5])
+ self.__rpc_data['idle_time'] = int(words[6])
+ self.__rpc_data['rpcsends'] = int(words[7])
+ self.__rpc_data['rpcreceives'] = int(words[8])
+ self.__rpc_data['badxids'] = int(words[9])
+ self.__rpc_data['inflightsends'] = long(words[10])
+ self.__rpc_data['backlogutil'] = int(words[11])
+ elif words[0] == 'per-op':
+ self.__rpc_data['per-op'] = words
+ else:
+ op = words[0][:-1]
+ self.__rpc_data['ops'] += [op]
+ self.__rpc_data[op] = [long(word) for word in words[1:]]
+
+ def parse_stats(self, lines):
+ """Turn a list of lines from a mount stat file into a
+ dictionary full of stats, keyed by name
+ """
+ found = False
+ for line in lines:
+ words = line.split()
+ if len(words) == 0:
+ continue
+ if (not found and words[0] != 'RPC'):
+ self.__parse_nfs_line(words)
+ continue
+
+ found = True
+ self.__parse_rpc_line(words)
+
+ def is_nfs_mountpoint(self):
+ """Return True if this is an NFS or NFSv4 mountpoint,
+ otherwise return False
+ """
+ if self.__nfs_data['fstype'] == 'nfs':
+ return True
+ elif self.__nfs_data['fstype'] == 'nfs4':
+ return True
+ return False
+
+ def display_nfs_options(self):
+ """Pretty-print the NFS options
+ """
+ print 'Stats for %s mounted on %s:' % \
+ (self.__nfs_data['export'], self.__nfs_data['mountpoint'])
+
+ print ' NFS mount options: %s' % ','.join(self.__nfs_data['mountoptions'])
+ print ' NFS server capabilities: %s' % ','.join(self.__nfs_data['servercapabilities'])
+ if self.__nfs_data.has_key('nfsv4flags'):
+ print ' NFSv4 capability flags: %s' % ','.join(self.__nfs_data['nfsv4flags'])
+ if self.__nfs_data.has_key('pseudoflavor'):
+ print ' NFS security flavor: %d pseudoflavor: %d' % \
+ (self.__nfs_data['flavor'], self.__nfs_data['pseudoflavor'])
+ else:
+ print ' NFS security flavor: %d' % self.__nfs_data['flavor']
+
+ def display_nfs_events(self):
+ """Pretty-print the NFS event counters
+ """
+ print
+ print 'Cache events:'
+ print ' data cache invalidated %d times' % self.__nfs_data['datainvalidates']
+ print ' attribute cache invalidated %d times' % self.__nfs_data['attrinvalidates']
+ print ' inodes synced %d times' % self.__nfs_data['syncinodes']
+ print
+ print 'VFS calls:'
+ print ' VFS requested %d inode revalidations' % self.__nfs_data['inoderevalidates']
+ print ' VFS requested %d dentry revalidations' % self.__nfs_data['dentryrevalidates']
+ print
+ print ' VFS called nfs_readdir() %d times' % self.__nfs_data['vfsreaddir']
+ print ' VFS called nfs_lookup() %d times' % self.__nfs_data['vfslookup']
+ print ' VFS called nfs_permission() %d times' % self.__nfs_data['vfspermission']
+ print ' VFS called nfs_file_open() %d times' % self.__nfs_data['vfsopen']
+ print ' VFS called nfs_file_flush() %d times' % self.__nfs_data['vfsflush']
+ print ' VFS called nfs_lock() %d times' % self.__nfs_data['vfslock']
+ print ' VFS called nfs_fsync() %d times' % self.__nfs_data['vfsfsync']
+ print ' VFS called nfs_file_release() %d times' % self.__nfs_data['vfsrelease']
+ print
+ print 'VM calls:'
+ print ' VFS called nfs_readpage() %d times' % self.__nfs_data['vfsreadpage']
+ print ' VFS called nfs_readpages() %d times' % self.__nfs_data['vfsreadpages']
+ print ' VFS called nfs_writepage() %d times' % self.__nfs_data['vfswritepage']
+ print ' VFS called nfs_writepages() %d times' % self.__nfs_data['vfswritepages']
+ print
+ print 'Generic NFS counters:'
+ print ' File size changing operations:'
+ print ' truncating SETATTRs: %d extending WRITEs: %d' % \
+ (self.__nfs_data['setattrtrunc'], self.__nfs_data['extendwrite'])
+ print ' %d silly renames' % self.__nfs_data['sillyrenames']
+ print ' short reads: %d short writes: %d' % \
+ (self.__nfs_data['shortreads'], self.__nfs_data['shortwrites'])
+ print ' NFSERR_DELAYs from server: %d' % self.__nfs_data['delay']
+
+ def display_nfs_bytes(self):
+ """Pretty-print the NFS event counters
+ """
+ print
+ print 'NFS byte counts:'
+ print ' applications read %d bytes via read(2)' % self.__nfs_data['normalreadbytes']
+ print ' applications wrote %d bytes via write(2)' % self.__nfs_data['normalwritebytes']
+ print ' applications read %d bytes via O_DIRECT read(2)' % self.__nfs_data['directreadbytes']
+ print ' applications wrote %d bytes via O_DIRECT write(2)' % self.__nfs_data['directwritebytes']
+ print ' client read %d bytes via NFS READ' % self.__nfs_data['serverreadbytes']
+ print ' client wrote %d bytes via NFS WRITE' % self.__nfs_data['serverwritebytes']
+
+ def display_rpc_generic_stats(self):
+ """Pretty-print the generic RPC stats
+ """
+ sends = self.__rpc_data['rpcsends']
+
+ print
+ print 'RPC statistics:'
+
+ print ' %d RPC requests sent, %d RPC replies received (%d XIDs not found)' % \
+ (sends, self.__rpc_data['rpcreceives'], self.__rpc_data['badxids'])
+ if sends != 0:
+ print ' average backlog queue length: %d' % \
+ (float(self.__rpc_data['backlogutil']) / sends)
+
+ def display_rpc_op_stats(self):
+ """Pretty-print the per-op stats
+ """
+ sends = self.__rpc_data['rpcsends']
+
+ # XXX: these should be sorted by 'count'
+ print
+ for op in self.__rpc_data['ops']:
+ stats = self.__rpc_data[op]
+ count = stats[0]
+ retrans = stats[1] - count
+ if count != 0:
+ print '%s:' % op
+ print '\t%d ops (%d%%)' % \
+ (count, ((count * 100) / sends)),
+ print '\t%d retrans (%d%%)' % (retrans, ((retrans * 100) / count)),
+ print '\t%d major timeouts' % stats[2]
+ print '\tavg bytes sent per op: %d\tavg bytes received per op: %d' % \
+ (stats[3] / count, stats[4] / count)
+ print '\tbacklog wait: %f' % (float(stats[5]) / count),
+ print '\tRTT: %f' % (float(stats[6]) / count),
+ print '\ttotal execute time: %f (milliseconds)' % \
+ (float(stats[7]) / count)
+
+ def compare_iostats(self, old_stats):
+ """Return the difference between two sets of stats
+ """
+ result = DeviceData()
+
+ # copy self into result
+ for key, value in self.__nfs_data.iteritems():
+ result.__nfs_data[key] = value
+ for key, value in self.__rpc_data.iteritems():
+ result.__rpc_data[key] = value
+
+ # compute the difference of each item in the list
+ # note the copy loop above does not copy the lists, just
+ # 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] = map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])
+
+ # update the remaining keys we care about
+ result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends']
+ result.__rpc_data['backlogutil'] -= old_stats.__rpc_data['backlogutil']
+ result.__nfs_data['serverreadbytes'] -= old_stats.__nfs_data['serverreadbytes']
+ result.__nfs_data['serverwritebytes'] -= old_stats.__nfs_data['serverwritebytes']
+
+ return result
+
+ def display_iostats(self, sample_time):
+ """Display NFS and RPC stats in an iostat-like way
+ """
+ sends = float(self.__rpc_data['rpcsends'])
+ if sample_time == 0:
+ sample_time = float(self.__nfs_data['age'])
+
+ print
+ print '%s mounted on %s:' % \
+ (self.__nfs_data['export'], self.__nfs_data['mountpoint'])
+
+ print '\top/s\trpc bklog'
+ print '\t%.2f' % (sends / sample_time),
+ if sends != 0:
+ print '\t%.2f' % \
+ ((float(self.__rpc_data['backlogutil']) / sends) / sample_time)
+ else:
+ print '\t0.00'
+
+ # reads: ops/s, Kb/s, avg rtt, and avg exe
+ # XXX: include avg xfer size and retransmits?
+ read_rpc_stats = self.__rpc_data['READ']
+ ops = float(read_rpc_stats[0])
+ kilobytes = float(self.__nfs_data['serverreadbytes']) / 1024
+ rtt = float(read_rpc_stats[6])
+ exe = float(read_rpc_stats[7])
+
+ print '\treads:\tops/s\t\tKb/s\t\tavg RTT (ms)\tavg exe (ms)'
+ print '\t\t%.2f' % (ops / sample_time),
+ print '\t\t%.2f' % (kilobytes / sample_time),
+ if ops != 0:
+ print '\t\t%.2f' % (rtt / ops),
+ print '\t\t%.2f' % (exe / ops)
+ else:
+ print '\t\t0.00',
+ print '\t\t0.00'
+
+ # writes: ops/s, Kb/s, avg rtt, and avg exe
+ # XXX: include avg xfer size and retransmits?
+ write_rpc_stats = self.__rpc_data['WRITE']
+ ops = float(write_rpc_stats[0])
+ kilobytes = float(self.__nfs_data['serverwritebytes']) / 1024
+ rtt = float(write_rpc_stats[6])
+ exe = float(write_rpc_stats[7])
+
+ print '\twrites:\tops/s\t\tKb/s\t\tavg RTT (ms)\tavg exe (ms)'
+ print '\t\t%.2f' % (ops / sample_time),
+ print '\t\t%.2f' % (kilobytes / sample_time),
+ if ops != 0:
+ print '\t\t%.2f' % (rtt / ops),
+ print '\t\t%.2f' % (exe / ops)
+ else:
+ print '\t\t0.00',
+ print '\t\t0.00'
+
+def parse_stats_file(filename):
+ """pop the contents of a mountstats file into a dictionary,
+ keyed by mount point. each value object is a list of the
+ lines in the mountstats file corresponding to the mount
+ point named in the key.
+ """
+ ms_dict = dict()
+ key = ''
+
+ f = file(filename)
+ for line in f.readlines():
+ words = line.split()
+ if len(words) == 0:
+ continue
+ if words[0] == 'device':
+ key = words[4]
+ new = [ line.strip() ]
+ else:
+ new += [ line.strip() ]
+ ms_dict[key] = new
+ f.close
+
+ return ms_dict
+
+def print_mountstats_help(name):
+ print 'usage: %s [ options ] <mount point>' % name
+ print
+ print ' Version %s' % Mountstats_version
+ print
+ print ' Display NFS client per-mount statistics.'
+ print
+ print ' --version display the version of this command'
+ print ' --nfs display only the NFS statistics'
+ print ' --rpc display only the RPC statistics'
+ print ' --start sample and save statistics'
+ print ' --end resample statistics and compare them with saved'
+ print
+
+def mountstats_command():
+ """Mountstats command
+ """
+ mountpoints = []
+ nfs_only = False
+ rpc_only = False
+
+ for arg in sys.argv:
+ if arg in ['-h', '--help', 'help', 'usage']:
+ print_mountstats_help(prog)
+ return
+
+ if arg in ['-v', '--version', 'version']:
+ print '%s version %s' % (sys.argv[0], Mountstats_version)
+ sys.exit(0)
+
+ if arg in ['-n', '--nfs']:
+ nfs_only = True
+ continue
+
+ if arg in ['-r', '--rpc']:
+ rpc_only = True
+ continue
+
+ if arg in ['-s', '--start']:
+ raise Exception, 'Sampling is not yet implemented'
+
+ if arg in ['-e', '--end']:
+ raise Exception, 'Sampling is not yet implemented'
+
+ if arg == sys.argv[0]:
+ continue
+
+ mountpoints += [arg]
+
+ if mountpoints == []:
+ print_mountstats_help(prog)
+ return
+
+ if rpc_only == True and nfs_only == True:
+ print_mountstats_help(prog)
+ return
+
+ mountstats = parse_stats_file('/proc/self/mountstats')
+
+ for mp in mountpoints:
+ if mp not in mountstats:
+ print 'Statistics for mount point %s not found' % mp
+ continue
+
+ stats = DeviceData()
+ stats.parse_stats(mountstats[mp])
+
+ if not stats.is_nfs_mountpoint():
+ print 'Mount point %s exists but is not an NFS mount' % mp
+ continue
+
+ if nfs_only:
+ stats.display_nfs_options()
+ stats.display_nfs_events()
+ stats.display_nfs_bytes()
+ elif rpc_only:
+ stats.display_rpc_generic_stats()
+ stats.display_rpc_op_stats()
+ else:
+ stats.display_nfs_options()
+ stats.display_nfs_bytes()
+ stats.display_rpc_generic_stats()
+ stats.display_rpc_op_stats()
+
+def print_nfsstat_help(name):
+ print 'usage: %s [ options ]' % name
+ print
+ print ' Version %s' % Mountstats_version
+ print
+ print ' nfsstat-like program that uses NFS client per-mount statistics.'
+ print
+
+def nfsstat_command():
+ print_nfsstat_help(prog)
+
+def print_iostat_help(name):
+ print 'usage: %s [ <interval> [ <count> ] ] [ <mount point> ] ' % name
+ print
+ print ' Version %s' % Mountstats_version
+ print
+ print ' iostat-like program to display NFS client per-mount statistics.'
+ print
+ print ' The <interval> parameter specifies the amount of time in seconds between'
+ print ' each report. The first report contains statistics for the time since each'
+ print ' file system was mounted. Each subsequent report contains statistics'
+ print ' collected during the interval since the previous report.'
+ print
+ print ' If the <count> parameter is specified, the value of <count> determines the'
+ print ' number of reports generated at <interval> seconds apart. If the interval'
+ print ' parameter is specified without the <count> parameter, the command generates'
+ print ' reports continuously.'
+ print
+ print ' If one or more <mount point> names are specified, statistics for only these'
+ print ' mount points will be displayed. Otherwise, all NFS mount points on the'
+ print ' client are listed.'
+ print
+
+def print_iostat_summary(old, new, devices, time):
+ for device in devices:
+ stats = DeviceData()
+ stats.parse_stats(new[device])
+ if not old:
+ stats.display_iostats(time)
+ else:
+ old_stats = DeviceData()
+ old_stats.parse_stats(old[device])
+ diff_stats = stats.compare_iostats(old_stats)
+ diff_stats.display_iostats(time)
+
+def iostat_command():
+ """iostat-like command for NFS mount points
+ """
+ mountstats = parse_stats_file('/proc/self/mountstats')
+ devices = []
+ interval_seen = False
+ count_seen = False
+
+ for arg in sys.argv:
+ if arg in ['-h', '--help', 'help', 'usage']:
+ print_iostat_help(prog)
+ return
+
+ if arg in ['-v', '--version', 'version']:
+ print '%s version %s' % (sys.argv[0], Mountstats_version)
+ return
+
+ if arg == sys.argv[0]:
+ continue
+
+ if arg in mountstats:
+ devices += [arg]
+ elif not interval_seen:
+ interval = int(arg)
+ if interval > 0:
+ interval_seen = True
+ else:
+ print 'Illegal <interval> value'
+ return
+ elif not count_seen:
+ count = int(arg)
+ if count > 0:
+ count_seen = True
+ else:
+ print 'Illegal <count> value'
+ return
+
+ # make certain devices contains only NFS mount points
+ if len(devices) > 0:
+ check = []
+ for device in devices:
+ stats = DeviceData()
+ stats.parse_stats(mountstats[device])
+ if stats.is_nfs_mountpoint():
+ check += [device]
+ devices = check
+ else:
+ for device, descr in mountstats.iteritems():
+ stats = DeviceData()
+ stats.parse_stats(descr)
+ if stats.is_nfs_mountpoint():
+ devices += [device]
+ if len(devices) == 0:
+ print 'No NFS mount points were found'
+ return
+
+ old_mountstats = None
+ sample_time = 0
+
+ if not interval_seen:
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
+ return
+
+ if count_seen:
+ while count != 0:
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
+ old_mountstats = mountstats
+ time.sleep(interval)
+ sample_time = interval
+ mountstats = parse_stats_file('/proc/self/mountstats')
+ count -= 1
+ else:
+ while True:
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
+ old_mountstats = mountstats
+ time.sleep(interval)
+ sample_time = interval
+ mountstats = parse_stats_file('/proc/self/mountstats')
+
+#
+# Main
+#
+prog = os.path.basename(sys.argv[0])
+
+try:
+ if prog == 'mountstats':
+ mountstats_command()
+ elif prog == 'ms-nfsstat':
+ nfsstat_command()
+ elif prog == 'ms-iostat':
+ iostat_command()
+except KeyboardInterrupt:
+ print 'Caught ^C... exiting'
+ sys.exit(1)
+
+sys.exit(0)

View File

@ -0,0 +1,42 @@
commit 2ef57222b10a91f4b96a06808d05a47e8f4c14f7
Author: Tom Talpey <tmt@netapp.com>
Date: Mon Jun 23 12:57:29 2008 -0400
Add RDMA as a supported transport for reporting
the mountstats statistics
Signed-off-by: Tom Talpey <tmt@netapp.com>
Acked-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
index 794d4a8..649c1bd 100644
--- a/tools/nfs-iostat/nfs-iostat.py
+++ b/tools/nfs-iostat/nfs-iostat.py
@@ -134,6 +134,26 @@ class DeviceData:
self.__rpc_data['badxids'] = int(words[9])
self.__rpc_data['inflightsends'] = long(words[10])
self.__rpc_data['backlogutil'] = long(words[11])
+ elif words[1] == 'rdma':
+ self.__rpc_data['port'] = words[2]
+ self.__rpc_data['bind_count'] = int(words[3])
+ self.__rpc_data['connect_count'] = int(words[4])
+ self.__rpc_data['connect_time'] = int(words[5])
+ self.__rpc_data['idle_time'] = int(words[6])
+ self.__rpc_data['rpcsends'] = int(words[7])
+ self.__rpc_data['rpcreceives'] = int(words[8])
+ self.__rpc_data['badxids'] = int(words[9])
+ self.__rpc_data['backlogutil'] = int(words[10])
+ self.__rpc_data['read_chunks'] = int(words[11])
+ self.__rpc_data['write_chunks'] = int(words[12])
+ self.__rpc_data['reply_chunks'] = int(words[13])
+ self.__rpc_data['total_rdma_req'] = int(words[14])
+ self.__rpc_data['total_rdma_rep'] = int(words[15])
+ self.__rpc_data['pullup'] = int(words[16])
+ self.__rpc_data['fixup'] = int(words[17])
+ self.__rpc_data['hardway'] = int(words[18])
+ self.__rpc_data['failed_marshal'] = int(words[19])
+ self.__rpc_data['bad_reply'] = int(words[20])
elif words[0] == 'per-op':
self.__rpc_data['per-op'] = words
else:

View File

@ -0,0 +1,560 @@
commit ba18e469a8507befdf8969c5ce7a25564744ae01
Author: Chuck Lever <chuck.lever@oracle.com>
Date: Mon Jun 23 12:56:14 2008 -0400
The "nfs-iostat" utility is a Python program that extracts and displays NFS
client performance information from /proc/self/mountstats.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
new file mode 100644
index 0000000..794d4a8
--- /dev/null
+++ b/tools/nfs-iostat/nfs-iostat.py
@@ -0,0 +1,544 @@
+#!/usr/bin/env python
+# -*- python-mode -*-
+"""Emulate iostat for NFS mount points using /proc/self/mountstats
+"""
+
+__copyright__ = """
+Copyright (C) 2005, Chuck Lever <cel@netapp.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys, os, time
+
+Iostats_version = '0.2'
+
+def difference(x, y):
+ """Used for a map() function
+ """
+ return x - y
+
+NfsEventCounters = [
+ 'inoderevalidates',
+ 'dentryrevalidates',
+ 'datainvalidates',
+ 'attrinvalidates',
+ 'vfsopen',
+ 'vfslookup',
+ 'vfspermission',
+ 'vfsupdatepage',
+ 'vfsreadpage',
+ 'vfsreadpages',
+ 'vfswritepage',
+ 'vfswritepages',
+ 'vfsreaddir',
+ 'vfssetattr',
+ 'vfsflush',
+ 'vfsfsync',
+ 'vfslock',
+ 'vfsrelease',
+ 'congestionwait',
+ 'setattrtrunc',
+ 'extendwrite',
+ 'sillyrenames',
+ 'shortreads',
+ 'shortwrites',
+ 'delay'
+]
+
+NfsByteCounters = [
+ 'normalreadbytes',
+ 'normalwritebytes',
+ 'directreadbytes',
+ 'directwritebytes',
+ 'serverreadbytes',
+ 'serverwritebytes',
+ 'readpages',
+ 'writepages'
+]
+
+class DeviceData:
+ """DeviceData objects provide methods for parsing and displaying
+ data for a single mount grabbed from /proc/self/mountstats
+ """
+ def __init__(self):
+ self.__nfs_data = dict()
+ self.__rpc_data = dict()
+ self.__rpc_data['ops'] = []
+
+ def __parse_nfs_line(self, words):
+ if words[0] == 'device':
+ self.__nfs_data['export'] = words[1]
+ self.__nfs_data['mountpoint'] = words[4]
+ self.__nfs_data['fstype'] = words[7]
+ if words[7] == 'nfs':
+ self.__nfs_data['statvers'] = words[8]
+ elif words[0] == 'age:':
+ self.__nfs_data['age'] = long(words[1])
+ elif words[0] == 'opts:':
+ self.__nfs_data['mountoptions'] = ''.join(words[1:]).split(',')
+ elif words[0] == 'caps:':
+ self.__nfs_data['servercapabilities'] = ''.join(words[1:]).split(',')
+ elif words[0] == 'nfsv4:':
+ self.__nfs_data['nfsv4flags'] = ''.join(words[1:]).split(',')
+ elif words[0] == 'sec:':
+ keys = ''.join(words[1:]).split(',')
+ self.__nfs_data['flavor'] = int(keys[0].split('=')[1])
+ self.__nfs_data['pseudoflavor'] = 0
+ if self.__nfs_data['flavor'] == 6:
+ self.__nfs_data['pseudoflavor'] = int(keys[1].split('=')[1])
+ elif words[0] == 'events:':
+ i = 1
+ for key in NfsEventCounters:
+ self.__nfs_data[key] = int(words[i])
+ i += 1
+ elif words[0] == 'bytes:':
+ i = 1
+ for key in NfsByteCounters:
+ self.__nfs_data[key] = long(words[i])
+ i += 1
+
+ def __parse_rpc_line(self, words):
+ if words[0] == 'RPC':
+ self.__rpc_data['statsvers'] = float(words[3])
+ self.__rpc_data['programversion'] = words[5]
+ elif words[0] == 'xprt:':
+ self.__rpc_data['protocol'] = words[1]
+ if words[1] == 'udp':
+ self.__rpc_data['port'] = int(words[2])
+ self.__rpc_data['bind_count'] = int(words[3])
+ self.__rpc_data['rpcsends'] = int(words[4])
+ self.__rpc_data['rpcreceives'] = int(words[5])
+ self.__rpc_data['badxids'] = int(words[6])
+ self.__rpc_data['inflightsends'] = long(words[7])
+ self.__rpc_data['backlogutil'] = long(words[8])
+ elif words[1] == 'tcp':
+ self.__rpc_data['port'] = words[2]
+ self.__rpc_data['bind_count'] = int(words[3])
+ self.__rpc_data['connect_count'] = int(words[4])
+ self.__rpc_data['connect_time'] = int(words[5])
+ self.__rpc_data['idle_time'] = int(words[6])
+ self.__rpc_data['rpcsends'] = int(words[7])
+ self.__rpc_data['rpcreceives'] = int(words[8])
+ self.__rpc_data['badxids'] = int(words[9])
+ self.__rpc_data['inflightsends'] = long(words[10])
+ self.__rpc_data['backlogutil'] = long(words[11])
+ elif words[0] == 'per-op':
+ self.__rpc_data['per-op'] = words
+ else:
+ op = words[0][:-1]
+ self.__rpc_data['ops'] += [op]
+ self.__rpc_data[op] = [long(word) for word in words[1:]]
+
+ def parse_stats(self, lines):
+ """Turn a list of lines from a mount stat file into a
+ dictionary full of stats, keyed by name
+ """
+ found = False
+ for line in lines:
+ words = line.split()
+ if len(words) == 0:
+ continue
+ if (not found and words[0] != 'RPC'):
+ self.__parse_nfs_line(words)
+ continue
+
+ found = True
+ self.__parse_rpc_line(words)
+
+ def is_nfs_mountpoint(self):
+ """Return True if this is an NFS or NFSv4 mountpoint,
+ otherwise return False
+ """
+ if self.__nfs_data['fstype'] == 'nfs':
+ return True
+ elif self.__nfs_data['fstype'] == 'nfs4':
+ return True
+ return False
+
+ def compare_iostats(self, old_stats):
+ """Return the difference between two sets of stats
+ """
+ result = DeviceData()
+
+ # copy self into result
+ for key, value in self.__nfs_data.iteritems():
+ result.__nfs_data[key] = value
+ for key, value in self.__rpc_data.iteritems():
+ result.__rpc_data[key] = value
+
+ # compute the difference of each item in the list
+ # note the copy loop above does not copy the lists, just
+ # 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] = map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])
+
+ # update the remaining keys we care about
+ result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends']
+ result.__rpc_data['backlogutil'] -= old_stats.__rpc_data['backlogutil']
+
+ for key in NfsEventCounters:
+ result.__nfs_data[key] -= old_stats.__nfs_data[key]
+ for key in NfsByteCounters:
+ result.__nfs_data[key] -= old_stats.__nfs_data[key]
+
+ return result
+
+ def __print_data_cache_stats(self):
+ """Print the data cache hit rate
+ """
+ nfs_stats = self.__nfs_data
+ app_bytes_read = float(nfs_stats['normalreadbytes'])
+ if app_bytes_read != 0:
+ client_bytes_read = float(nfs_stats['serverreadbytes'] - nfs_stats['directreadbytes'])
+ ratio = ((app_bytes_read - client_bytes_read) * 100) / app_bytes_read
+
+ print
+ print 'app bytes: %f client bytes %f' % (app_bytes_read, client_bytes_read)
+ print 'Data cache hit ratio: %4.2f%%' % ratio
+
+ def __print_attr_cache_stats(self, sample_time):
+ """Print attribute cache efficiency stats
+ """
+ nfs_stats = self.__nfs_data
+ getattr_stats = self.__rpc_data['GETATTR']
+
+ if nfs_stats['inoderevalidates'] != 0:
+ getattr_ops = float(getattr_stats[1])
+ opens = float(nfs_stats['vfsopen'])
+ revalidates = float(nfs_stats['inoderevalidates']) - opens
+ if revalidates != 0:
+ ratio = ((revalidates - getattr_ops) * 100) / revalidates
+ else:
+ ratio = 0.0
+
+ data_invalidates = float(nfs_stats['datainvalidates'])
+ attr_invalidates = float(nfs_stats['attrinvalidates'])
+
+ print
+ print '%d inode revalidations, hitting in cache %4.2f%% of the time' % \
+ (revalidates, ratio)
+ print '%d open operations (mandatory GETATTR requests)' % opens
+ if getattr_ops != 0:
+ print '%4.2f%% of GETATTRs resulted in data cache invalidations' % \
+ ((data_invalidates * 100) / getattr_ops)
+
+ def __print_dir_cache_stats(self, sample_time):
+ """Print directory stats
+ """
+ nfs_stats = self.__nfs_data
+ lookup_ops = self.__rpc_data['LOOKUP'][0]
+ readdir_ops = self.__rpc_data['READDIR'][0]
+ if self.__rpc_data.has_key('READDIRPLUS'):
+ readdir_ops += self.__rpc_data['READDIRPLUS'][0]
+
+ dentry_revals = nfs_stats['dentryrevalidates']
+ opens = nfs_stats['vfsopen']
+ lookups = nfs_stats['vfslookup']
+ getdents = nfs_stats['vfsreaddir']
+
+ print
+ print '%d open operations (pathname lookups)' % opens
+ print '%d dentry revalidates and %d vfs lookup requests' % \
+ (dentry_revals, lookups),
+ print 'resulted in %d LOOKUPs on the wire' % lookup_ops
+ print '%d vfs getdents calls resulted in %d READDIRs on the wire' % \
+ (getdents, readdir_ops)
+
+ def __print_page_stats(self, sample_time):
+ """Print page cache stats
+ """
+ nfs_stats = self.__nfs_data
+
+ vfsreadpage = nfs_stats['vfsreadpage']
+ vfsreadpages = nfs_stats['vfsreadpages']
+ pages_read = nfs_stats['readpages']
+ vfswritepage = nfs_stats['vfswritepage']
+ vfswritepages = nfs_stats['vfswritepages']
+ pages_written = nfs_stats['writepages']
+
+ print
+ print '%d nfs_readpage() calls read %d pages' % \
+ (vfsreadpage, vfsreadpage)
+ print '%d nfs_readpages() calls read %d pages' % \
+ (vfsreadpages, pages_read - vfsreadpage),
+ if vfsreadpages != 0:
+ print '(%.1f pages per call)' % \
+ (float(pages_read - vfsreadpage) / vfsreadpages)
+ else:
+ print
+
+ print
+ print '%d nfs_updatepage() calls' % nfs_stats['vfsupdatepage']
+ print '%d nfs_writepage() calls wrote %d pages' % \
+ (vfswritepage, vfswritepage)
+ print '%d nfs_writepages() calls wrote %d pages' % \
+ (vfswritepages, pages_written - vfswritepage),
+ if (vfswritepages) != 0:
+ print '(%.1f pages per call)' % \
+ (float(pages_written - vfswritepage) / vfswritepages)
+ else:
+ print
+
+ congestionwaits = nfs_stats['congestionwait']
+ if congestionwaits != 0:
+ print
+ print '%d congestion waits' % congestionwaits
+
+ def __print_rpc_op_stats(self, op, sample_time):
+ """Print generic stats for one RPC op
+ """
+ if not self.__rpc_data.has_key(op):
+ return
+
+ rpc_stats = self.__rpc_data[op]
+ ops = float(rpc_stats[0])
+ retrans = float(rpc_stats[1] - rpc_stats[0])
+ kilobytes = float(rpc_stats[3] + rpc_stats[4]) / 1024
+ rtt = float(rpc_stats[6])
+ exe = float(rpc_stats[7])
+
+ # prevent floating point exceptions
+ if ops != 0:
+ kb_per_op = kilobytes / ops
+ retrans_percent = (retrans * 100) / ops
+ rtt_per_op = rtt / ops
+ exe_per_op = exe / ops
+ else:
+ kb_per_op = 0.0
+ retrans_percent = 0.0
+ rtt_per_op = 0.0
+ exe_per_op = 0.0
+
+ op += ':'
+ print '%s' % op.lower().ljust(15),
+ print ' ops/s\t\t Kb/s\t\t Kb/op\t\tretrans\t\tavg RTT (ms)\tavg exe (ms)'
+
+ print '\t\t%7.3f' % (ops / sample_time),
+ print '\t%7.3f' % (kilobytes / sample_time),
+ print '\t%7.3f' % kb_per_op,
+ print ' %7d (%3.1f%%)' % (retrans, retrans_percent),
+ print '\t%7.3f' % rtt_per_op,
+ print '\t%7.3f' % exe_per_op
+
+ def display_iostats(self, sample_time, which):
+ """Display NFS and RPC stats in an iostat-like way
+ """
+ sends = float(self.__rpc_data['rpcsends'])
+ if sample_time == 0:
+ sample_time = float(self.__nfs_data['age'])
+ if sends != 0:
+ backlog = (float(self.__rpc_data['backlogutil']) / sends) / sample_time
+ else:
+ backlog = 0.0
+
+ print
+ print '%s mounted on %s:' % \
+ (self.__nfs_data['export'], self.__nfs_data['mountpoint'])
+ print
+
+ print ' op/s\t\trpc bklog'
+ print '%7.2f' % (sends / sample_time),
+ print '\t%7.2f' % backlog
+
+ if which == 0:
+ self.__print_rpc_op_stats('READ', sample_time)
+ self.__print_rpc_op_stats('WRITE', sample_time)
+ elif which == 1:
+ self.__print_rpc_op_stats('GETATTR', sample_time)
+ self.__print_rpc_op_stats('ACCESS', sample_time)
+ 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)
+ if self.__rpc_data.has_key('READDIRPLUS'):
+ self.__print_rpc_op_stats('READDIRPLUS', sample_time)
+ 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_page_stats(sample_time)
+
+#
+# Functions
+#
+
+def print_iostat_help(name):
+ print 'usage: %s [ <interval> [ <count> ] ] [ <options> ] [ <mount point> ] ' % name
+ print
+ print ' Version %s' % Iostats_version
+ print
+ print ' Sample iostat-like program to display NFS client per-mount statistics.'
+ print
+ print ' The <interval> parameter specifies the amount of time in seconds between'
+ print ' each report. The first report contains statistics for the time since each'
+ print ' file system was mounted. Each subsequent report contains statistics'
+ print ' collected during the interval since the previous report.'
+ print
+ print ' If the <count> parameter is specified, the value of <count> determines the'
+ print ' number of reports generated at <interval> seconds apart. If the interval'
+ print ' parameter is specified without the <count> parameter, the command generates'
+ print ' reports continuously.'
+ print
+ print ' Options include "--attr", which displays statistics related to the attribute'
+ print ' cache, "--dir", which displays statistics related to directory operations,'
+ print ' and "--page", which displays statistics related to the page cache.'
+ print ' By default, if no option is specified, statistics related to file I/O are'
+ print ' displayed.'
+ print
+ print ' If one or more <mount point> names are specified, statistics for only these'
+ print ' mount points will be displayed. Otherwise, all NFS mount points on the'
+ print ' client are listed.'
+
+def parse_stats_file(filename):
+ """pop the contents of a mountstats file into a dictionary,
+ keyed by mount point. each value object is a list of the
+ lines in the mountstats file corresponding to the mount
+ point named in the key.
+ """
+ ms_dict = dict()
+ key = ''
+
+ f = file(filename)
+ for line in f.readlines():
+ words = line.split()
+ if len(words) == 0:
+ continue
+ if words[0] == 'device':
+ key = words[4]
+ new = [ line.strip() ]
+ else:
+ new += [ line.strip() ]
+ ms_dict[key] = new
+ f.close
+
+ return ms_dict
+
+def print_iostat_summary(old, new, devices, time, ac):
+ for device in devices:
+ stats = DeviceData()
+ stats.parse_stats(new[device])
+ if not old:
+ stats.display_iostats(time, ac)
+ else:
+ old_stats = DeviceData()
+ old_stats.parse_stats(old[device])
+ diff_stats = stats.compare_iostats(old_stats)
+ diff_stats.display_iostats(time, ac)
+
+def iostat_command(name):
+ """iostat-like command for NFS mount points
+ """
+ mountstats = parse_stats_file('/proc/self/mountstats')
+ devices = []
+ which = 0
+ interval_seen = False
+ count_seen = False
+
+ for arg in sys.argv:
+ if arg in ['-h', '--help', 'help', 'usage']:
+ print_iostat_help(name)
+ return
+
+ if arg in ['-v', '--version', 'version']:
+ print '%s version %s' % (name, Iostats_version)
+ return
+
+ if arg in ['-a', '--attr']:
+ which = 1
+ continue
+
+ if arg in ['-d', '--dir']:
+ which = 2
+ continue
+
+ if arg in ['-p', '--page']:
+ which = 3
+ continue
+
+ if arg == sys.argv[0]:
+ continue
+
+ if arg in mountstats:
+ devices += [arg]
+ elif not interval_seen:
+ interval = int(arg)
+ if interval > 0:
+ interval_seen = True
+ else:
+ print 'Illegal <interval> value'
+ return
+ elif not count_seen:
+ count = int(arg)
+ if count > 0:
+ count_seen = True
+ else:
+ print 'Illegal <count> value'
+ return
+
+ # make certain devices contains only NFS mount points
+ if len(devices) > 0:
+ check = []
+ for device in devices:
+ stats = DeviceData()
+ stats.parse_stats(mountstats[device])
+ if stats.is_nfs_mountpoint():
+ check += [device]
+ devices = check
+ else:
+ for device, descr in mountstats.iteritems():
+ stats = DeviceData()
+ stats.parse_stats(descr)
+ if stats.is_nfs_mountpoint():
+ devices += [device]
+ if len(devices) == 0:
+ print 'No NFS mount points were found'
+ return
+
+ old_mountstats = None
+ sample_time = 0.0
+
+ if not interval_seen:
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which)
+ return
+
+ if count_seen:
+ while count != 0:
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which)
+ old_mountstats = mountstats
+ time.sleep(interval)
+ sample_time = interval
+ mountstats = parse_stats_file('/proc/self/mountstats')
+ count -= 1
+ else:
+ while True:
+ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, which)
+ old_mountstats = mountstats
+ time.sleep(interval)
+ sample_time = interval
+ mountstats = parse_stats_file('/proc/self/mountstats')
+
+#
+# Main
+#
+prog = os.path.basename(sys.argv[0])
+
+try:
+ iostat_command(prog)
+except KeyboardInterrupt:
+ print 'Caught ^C... exiting'
+ sys.exit(1)
+
+sys.exit(0)

View File

@ -0,0 +1,60 @@
commit 5be04020788598cb811e51c4b1342cf0796cbb65
Author: Jeff Layton <jlayton@redhat.com>
Date: Mon Jun 23 07:21:52 2008 -0400
The nfsstat program reads /proc/net/rpc/* files to gets info about
calls. This info is output as unsigned numbers (at least on any
relatively recent kernel). When nfsstat prints these numbers, they are
printed as signed integers. When the call counters reach 2^31, things
start being printed as negative numbers.
This patch changes nfsstat to read and print all counters as unsigned
integers. Tested by hacking up a kernel to initialize call counters to
2^31+1.
Thanks to Takafumi Miki for the initial version of this patch.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve Dickson <steved@redhat.com>
diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
index d2cca8d..1517414 100644
--- a/utils/nfsstat/nfsstat.c
+++ b/utils/nfsstat/nfsstat.c
@@ -539,7 +539,7 @@ print_numbers(const char *hdr, unsigned int *info, unsigned int nr)
fputs(hdr, stdout);
for (i = 0; i < nr; i++)
- printf("%s%-8d", i? " " : "", info[i]);
+ printf("%s%-8u", i? " " : "", info[i]);
printf("\n");
}
@@ -562,7 +562,7 @@ print_callstats(const char *hdr, const char **names,
printf("\n");
for (j = 0; j < 6 && i + j < nr; j++) {
pct = ((unsigned long long) info[i+j]*100)/total;
- printf("%-8d%3llu%% ", info[i+j], pct);
+ printf("%-8u%3llu%% ", info[i+j], pct);
}
printf("\n");
}
@@ -604,7 +604,7 @@ parse_raw_statfile(const char *name, struct statinfo *statp)
for (i = 0; i < cnt; i++) {
if (!(sp = strtok(NULL, " \t")))
break;
- ip->valptr[i] = atoi(sp);
+ ip->valptr[i] = (unsigned int) strtoul(sp, NULL, 0);
total += ip->valptr[i];
}
ip->valptr[cnt - 1] = total;
@@ -618,7 +618,8 @@ parse_raw_statfile(const char *name, struct statinfo *statp)
static int
parse_pretty_statfile(const char *filename, struct statinfo *info)
{
- int numvals, curindex, numconsumed, n, sum, err = 1;
+ int numvals, curindex, numconsumed, n, err = 1;
+ unsigned int sum;
char buf[4096], *bufp, *fmt, is_proc;
FILE *fp = NULL;
struct statinfo *ip;

View File

@ -47,8 +47,11 @@ Patch112: nfs-utils-1.1.2-mount-statd-chk.patch
Patch113: nfs-utils-1.1.2-mount-cleanup.patch
Patch114: nfs-utils-1.1.2-mount-error-reporting.patch
Patch115: nfs-utils-1.1.2-nfsstat-m-arg.patch
Patch116: nfs-utils-1.1.2-nfsstat-counters.patch
Patch117: nfs-utils-1.1.2-mountstats.patch
Patch118: nfs-utils-1.1.2-mountstats-rdma.patch
Patch119: nfs-utils-1.1.2-nfs-iostat.patch
Patch120: nfs-utils-1.1.2-nfs-iostat-rdma.patch
%if %{enablefscache}
Patch90: nfs-utils-1.1.0-mount-fsc.patch
@ -124,6 +127,11 @@ This package also contains the mount.nfs and umount.nfs program.
%patch113 -p1
%patch114 -p1
%patch115 -p1
%patch116 -p1
%patch117 -p1
%patch118 -p1
%patch119 -p1
%patch120 -p1
%if %{enablefscache}
%patch90 -p1
@ -143,7 +151,7 @@ export PIE
sh -x autogen.sh
CFLAGS="`echo $RPM_OPT_FLAGS $ARCH_OPT_FLAGS $PIE`"
CFLAGS="`echo $RPM_OPT_FLAGS $ARCH_OPT_FLAGS $PIE -D_FILE_OFFSET_BITS=64`"
%configure \
CFLAGS="$CFLAGS" \
CPPFLAGS="$DEFINES" \
@ -291,6 +299,12 @@ fi
%attr(4755,root,root) /sbin/umount.nfs4
%changelog
* Mon Jun 23 2008 Steve Dickson <steved@redhat.com> 1.1.2-7
- Added -D_FILE_OFFSET_BITS=64 to CFLAGS
- make nfsstat read and print stats as unsigned integers
- Added (but not installed) the mountstats and nfs-iostat
python scripts.
* Fri Jun 6 2008 Steve Dickson <steved@redhat.com> 1.1.2-6
- Added 5 (111 thru 115) upstream patches that fixed
things mostly in the text mounting code.