2 # SPDX-License-Identifier: GPL-2.0
5 # This file is part of Lustre, http://www.lustre.org/
7 # This script is to be run on a client machine and will test all the
8 # OSTs to determine which is the fastest and slowest
9 # The current test method is as follows:
10 # -Create a directory for each OST
11 # -Use 'lfs setstripe' to set the Lustre striping such that IO goes to
13 # -Use 'dd' to write and read a file of a specified size
14 # -Compute the average, and Standard deviation
15 # -Find the slowest OST for read and write
16 # -Find the Fastest OST for read and write
20 $pname = $0; # to hold program name
21 $OSTS = 0; # Number of OSTS we will loop over
22 $BSIZE = 1024 * 1024; # Size of i/o block
23 $MBSIZE = 1024 * 1024; # One MB
24 $MNT = "/mnt/lustre"; # Location of Lustre file system
25 $CACHESZ = 0; # max_cached_mb parameter
26 $FSIZE = 30; # Number of i/o blocks
30 print "Usage: $pname [-s <size>] [-h] <Lustre_Path>\n";
32 print " -s: size of test file in MB (default $FSIZE MB)\n";
33 print " -h: To display this help\n";
34 print "example : $pname /mnt/lustre\n";
38 # ost_count subroutine sets global variable $OST with number of OSTs
39 # Also fills 1 for active OST indexes in ACTIVEOST_INX array.
41 $OSTS = `lctl get_param -n lov.$FSNAME-clilov-*.activeobd`;
43 die "Read lov.*-clilov-$FSNAME.activeobd error: $?\n";
45 print "Number of Active OST devices : $OSTS";
47 $numost = `lctl get_param -n lov.$FSNAME-clilov-*.numobd`;
49 die "Read lov.$FSNAME-clilov-*.numobd error: $?\n";
52 if ( $numost != $OSTS ) {
53 printf "Number of non active ots(s): %d\n", ( $numost - $OSTS );
57 $targets = `lctl get_param -n lov.$FSNAME-clilov-*.target_obd`;
59 die "Read lov.$FSNAME-clilov-*.target_obd error: $?\n";
63 foreach $line (split /\n/, $targets) {
64 my ($ost_num, $ost_name, $ost_status) = split(/\s+/, $line);
65 if ( $ost_status eq "ACTIVE" ) {
66 $ACTIVEOST_INX[$count] = 1;
72 # cache_mod subroutine sets global variable $CACHESZ and sets max_cached_mb
73 # to 256 * system page size in MB.
75 use POSIX qw(sysconf _SC_PAGESIZE);
76 $CACHESZ = `lctl get_param -n llite.$FSNAME-*.max_cached_mb`;
78 die "Read llite.$FSNAME-*.max_cached_mb error: $?\n";
81 $CACHESZ = `echo "$CACHESZ" | grep max_cached_mb | awk '{print \$2}'`;
82 my $pagesize = sysconf(_SC_PAGESIZE);
83 $req_cache_mb = ($pagesize * 256)/ $MBSIZE;
85 system("lctl set_param -n llite.$FSNAME-*.max_cached_mb=$req_cache_mb");
87 die "Set llite.$FSNAME-*.max_cached_mb error: $?\n";
91 # cache_return subroutine returns max_cached_mb to original value
93 system("lctl set_param -n llite.$FSNAME-*.max_cached_mb=$CACHESZ");
96 # make_dummy subroutine creates a dummy file that will be used for read operation.
100 system ("dd of=$tempfile if=/dev/zero count=$SIZE bs=$BSIZE 2> /dev/null");
103 # run_test subroutine actually writes and reads data to/from dummy file
104 # and computes corresponding time taken for read and write operation and
105 # byte transfer for the both operations.
106 # This subroutine also fill corresponding global arrays with above information.
111 my $tempfile = $_[3];
113 if ( !(-f $tempfile) && $ACTION eq "read" ) {
114 &make_dummy($SIZE, $tempfile);
117 my ($ts0, $tu0) = gettimeofday();
118 $tu0 = $ts0 + ($tu0 / 1000000);
119 if ( $ACTION eq "write" ) {
120 system("dd of=$tempfile if=/dev/zero count=$SIZE bs=$BSIZE 2> /dev/null");
121 } elsif ( $ACTION eq "read" ) {
122 system("dd if=$tempfile of=/dev/null count=$SIZE bs=$BSIZE 2> /dev/null");
124 print "Action is neither read nor write\n";
128 my ($ts1, $tu1) = gettimeofday();
129 $tu1 = $ts1 + ($tu1/1000000);
130 my $tdelta = $tu1 - $tu0;
131 my $delta = ($SIZE * $BSIZE / ( $tu1 - $tu0 )) / (1024 * 1024);
132 if ( $ACTION eq "write" ) {
133 $wTime[$INX] = $tdelta;
134 $wMBs[$INX] = $delta;
136 $rTime[$INX] = $tdelta;
137 $rMBs[$INX] = $delta;
141 # calculate subroutine compute following things and displays them.
142 # - Finds worst and best OST for both read and write operations.
143 # - Compute average of read and write rate from all OSTS
144 # - Compute Standard deviation for read and write form all OST's
156 my $min_mb = 999999999;
157 while ($count < $OSTS ) {
158 if ( $ACTIVEOST_INX[$count] ) {
159 $total = $total + $MBs[$count];
160 if ($max_mb < $MBs[$count] ) {
161 $max_mb = $MBs[$count];
164 if ($min_mb > $MBs[$count] ) {
165 $min_mb = $MBs[$count];
174 while ($count < $OSTS ) {
175 if ( $ACTIVEOST_INX[$count] ) {
176 $total = $total + ($MBs[$count] - $avg) * ($MBs[$count] - $avg);
180 $sd = sqrt($total/$OSTS);
181 printf "Worst %s OST indx: %d speed: %f\n", $op, $worst_OST, $min_mb;
182 printf "Best %s OST indx: %d speed: %f\n", $op, $best_OST, $max_mb;
183 printf "%s Average: %f +/- %f MB/s\n", $op, $avg, $sd;
186 # output_all_data subroutine displays speed and time information
187 # for all OST's for both read and write operations.
188 sub output_all_data () {
190 print "Ost# Read(MB/s) Write(MB/s) Read-time Write-time\n";
191 print "----------------------------------------------------\n";
192 while ( $count < $OSTS ) {
193 if ( $ACTIVEOST_INX[$count] ) {
194 printf "%d %.3f %.3f %.3f %.3f\n",$count,
195 $rMBs[$count], $wMBs[$count], $rTime[$count], $wTime[$count];
197 printf "%d Inactive ost\n",$count;
214 # Check number of arguments
215 my $numargs = $#ARGV + 1;
216 usage() if $numargs < 1;
218 # Command line parameter parsing
220 getopts('s:h') or usage();
222 $FSIZE = $opt_s if $opt_s;
229 print "ERROR: extra argument $_\n";
233 #Check for Time::HiRes module
234 my $CheckTimeHiRes = "require Time::HiRes";
235 eval ($CheckTimeHiRes) or die "You need to install the perl-Time-HiRes package to use this script\n";
236 my $LoadTimeHiRes = "use Time::HiRes qw(gettimeofday)";
237 eval ($LoadTimeHiRes);
239 use POSIX qw(strftime);
241 my $hostname = `lctl list_nids | head -1` or die "You need to install lctl to use this script\n";
243 print "$pname: ", strftime("%D", localtime($time_v));
244 print " OST speed survey on $MNT from $hostname\n";
246 # Get the file system name.
247 $FSNAME = `lfs getname $MNT`;
249 die "`lfs getname $MNT` error: $?\n";
259 $dirpath = "$MNT/ost_survey_tmp";
260 eval { mkpath($dirpath) };
262 print "Couldn't create $dirpath: $@";
268 while ($CNT < $OSTS) {
269 $filename = "$dirpath/file$CNT";
270 if ( $ACTIVEOST_INX[$CNT] ) {
271 # set stripe for OST number $CNT
272 system ("lfs setstripe -S 0 -i $CNT -c 1 $filename");
273 # Perform write for OST number $CNT
274 &run_test($FSIZE,$CNT,"write",$filename);
280 while ($CNT < $OSTS) {
281 $filename = "$dirpath/file$CNT";
282 if ( $ACTIVEOST_INX[$CNT] ) {
283 # Perform read for OST number $CNT
284 &run_test($FSIZE,$CNT,"read",$filename);
290 # if read or write performed on any OST then display information.
293 &calculate("Read",@rMBs);
294 &calculate("Write",@wMBs);
298 print "There is no active OST's found\n";
301 # Return cache to original size
304 eval { rmtree($dirpath) };
306 print "Warning: Couldn't remove $dirpath: $@";