6 # Copyright (c) 2003-2004 Silicon Graphics, Inc. All Rights Reserved.
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License as
10 # published by the Free Software Foundation.
12 # This program is distributed in the hope that it would be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write the Free Software Foundation,
19 # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 # Modify a filesystem's superblock and AGF metadata structures
23 # so that only a subset of the allocation groups will be used.
24 # Intended use is in testing large virtual devices (eg. loop)
25 # with extremely large filesystems, where we want to ensure
26 # high allocation groups are used as much as possible (where
27 # the block addresses are large).
31 getopts('cf:l:qr:v', \%opt);
33 die "Usage: $0 [-f AG] [-l AG] [-r bytes] [-cqv] device\n" unless (@ARGV == 1);
34 my $device = shift @ARGV;
35 die "$device: no such file\n" unless (-e $device);
37 my $clearall = defined($opt{'c'}) ? 1 : 0;
38 my $retain = defined($opt{'r'}) ? $opt{'r'} : -1;
39 my $quiet = defined($opt{'q'}) ? 1 : 0;
40 my $verbose = defined($opt{'v'}) ? 1 : 0;
41 my $nagfirst = defined($opt{'f'}) ? $opt{'f'} : 0;
42 my $naglast = defined($opt{'l'}) ? $opt{'l'} : 0;
45 # "clearall" means clear everything barring the final AG.
46 # "retain" means clearall and retain a specified amount in the
47 # second-from-last AG as well (value is the number of bytes).
48 # [NB: retain with a value of zero is now the same as clearall].
55 my $xfsdb = 'xfs_db -x';
59 $xfsdb .= ' -c ' . $_;
61 print $xfsdb, ' ', $device, "\n" if ($verbose);
63 die unless open(DB, "$xfsdb $device 2>/dev/null |");
65 if (/^(\S+) = (.*)$/) {
75 # Stage 1: Get control information from the superblock
78 my @sbprint = ( '"print fdblocks"', '"print agcount"', '"print blocksize"' );
79 my @agffree = ( '"print freeblks"' );
81 my %sb = xfs_db 'sb', @sbprint;
82 print "=== Initially ", $sb{'fdblocks'}, " blocks free across ",
83 $sb{'agcount'}, " AGs\n" unless $quiet;
84 if ($clearall && ($nagfirst || $naglast)) {
85 print STDERR " o Clearall/retain specified with first/last AG\n";
88 if ($nagfirst >= $sb{'agcount'}) {
89 print STDERR " o First AG number is too large\n";
92 if ($naglast >= $sb{'agcount'}) {
93 print STDERR " o Last AG number is too large\n";
96 if ($naglast - $nagfirst < 0) {
97 print STDERR " o No AGs to clear\n";
101 $naglast = $sb{'agcount'} - 2;
106 $retain /= $sb{'blocksize'}; # convert to fsblocks
107 %check = xfs_db "'agf $naglast'", @agffree;
108 if ($check{'freeblks'} < $retain) {
109 print STDERR " o Insufficient space to retain\n";
117 # Stage 2: Wipe out all completely masked allocation groups.
120 my @agfprint = ( '"print freeblks"', '"print flcount"' );
121 my @agfcommands = ( '"write freeblks 0"',
122 '"write longest 0"', '"write flcount 0"',
123 '"write bnolevel 1"', '"write cntlevel 1"',
124 '"write flfirst 0"', '"write fllast 0"' );
125 my @bnoprint = ( '"addr bnoroot"', '"print numrecs"' );
126 my @bnocommands = ( '"addr bnoroot"', '"write numrecs 0"',
127 '"write leftsib -1"', '"write rightsib -1"' );
128 my @cntprint = ( '"addr cntroot"', '"print numrecs"' );
129 my @cntcommands = ( '"addr cntroot"', '"write numrecs 0"',
130 '"write leftsib -1"', '"write rightsib -1"' );
132 print "=== Wiping ", $naglast - $nagfirst + 1,
133 " AGs starting from AG #", $nagfirst, "\n" unless $quiet;
136 while ($ag <= $naglast) {
137 print " o AG#", $ag, " AGF fields\n" unless $quiet;
139 my %agf = xfs_db "'agf $ag'", @agfprint;
140 xfs_db "'agf $ag'", @agfcommands;
142 my $blockcnt = $agf{'freeblks'} + $agf{'flcount'};
143 $sb{'fdblocks'} -= $blockcnt;
144 print " cleared ", $blockcnt, " blocks from AG#", $ag, "\n"
147 my %btree = xfs_db "'agf $ag'", @bnoprint;
148 xfs_db "'agf $ag'", @bnocommands, "'agf $ag'", @cntcommands;
149 print " cleared ", $btree{'numrecs'}, " BNO/CNT btree recs in AG#",
150 $ag, "\n" unless $quiet;
157 # Stage 3: Wipe out any partially masked allocation group.
161 print " o AG#", $ag, " AGF fields (partial)\n" unless $quiet;
163 my %ragf = xfs_db "'agf $ag'", '"print freeblks"',
164 '"addr bnoroot"', '"print recs[1].startblock"';
165 my $maskblks = $ragf{'freeblks'} - $retain;
166 my $newstart = $ragf{'recs[1].startblock'} + $maskblks;
168 "'write freeblks $retain'", "'write longest $retain'",
169 "'agf $ag'", '"addr bnoroot"',
170 "'write recs[1].startblock $newstart'",
171 "'write recs[1].blockcount $retain'",
172 "'agf $ag'", '"addr cntroot"',
173 "'write recs[1].startblock $newstart'",
174 "'write recs[1].blockcount $retain'";
176 $sb{'fdblocks'} -= $maskblks;
177 print " cleared ", $maskblks, " blocks from AG#", $ag, "\n"
181 print "=== Updating final freespace count, ", $sb{'fdblocks'}, " blocks\n"
183 xfs_db "'sb 0'", "'write fdblocks $sb{'fdblocks'}'"