6 # Copyright (c) 2003-2004 Silicon Graphics, Inc. All Rights Reserved.
8 # Modify a filesystem's superblock and AGF metadata structures
9 # so that only a subset of the allocation groups will be used.
10 # Intended use is in testing large virtual devices (eg. loop)
11 # with extremely large filesystems, where we want to ensure
12 # high allocation groups are used as much as possible (where
13 # the block addresses are large).
17 getopts('cf:l:qr:v', \%opt);
19 die "Usage: $0 [-f AG] [-l AG] [-r bytes] [-cqv] device\n" unless (@ARGV == 1);
20 my $device = shift @ARGV;
21 die "$device: no such file\n" unless (-e $device);
23 my $clearall = defined($opt{'c'}) ? 1 : 0;
24 my $retain = defined($opt{'r'}) ? $opt{'r'} : -1;
25 my $quiet = defined($opt{'q'}) ? 1 : 0;
26 my $verbose = defined($opt{'v'}) ? 1 : 0;
27 my $nagfirst = defined($opt{'f'}) ? $opt{'f'} : 0;
28 my $naglast = defined($opt{'l'}) ? $opt{'l'} : 0;
31 # "clearall" means clear everything barring the final AG.
32 # "retain" means clearall and retain a specified amount in the
33 # second-from-last AG as well (value is the number of bytes).
34 # [NB: retain with a value of zero is now the same as clearall].
41 my $xfsdb = 'xfs_db -x';
45 $xfsdb .= ' -c ' . $_;
47 print $xfsdb, ' ', $device, "\n" if ($verbose);
49 die unless open(DB, "$xfsdb $device 2>/dev/null |");
51 if (/^(\S+) = (.*)$/) {
61 # Stage 1: Get control information from the superblock
64 my @sbprint = ( '"print fdblocks"', '"print agcount"', '"print blocksize"' );
65 my @agffree = ( '"print freeblks"' );
67 my %sb = xfs_db 'sb', @sbprint;
68 print "=== Initially ", $sb{'fdblocks'}, " blocks free across ",
69 $sb{'agcount'}, " AGs\n" unless $quiet;
70 if ($clearall && ($nagfirst || $naglast)) {
71 print STDERR " o Clearall/retain specified with first/last AG\n";
74 if ($nagfirst >= $sb{'agcount'}) {
75 print STDERR " o First AG number is too large\n";
78 if ($naglast >= $sb{'agcount'}) {
79 print STDERR " o Last AG number is too large\n";
82 if ($naglast - $nagfirst < 0) {
83 print STDERR " o No AGs to clear\n";
87 $naglast = $sb{'agcount'} - 2;
92 $retain /= $sb{'blocksize'}; # convert to fsblocks
93 %check = xfs_db "'agf $naglast'", @agffree;
94 if ($check{'freeblks'} < $retain) {
95 print STDERR " o Insufficient space to retain\n";
103 # Stage 2: Wipe out all completely masked allocation groups.
106 my @agfprint = ( '"print freeblks"', '"print flcount"' );
107 my @agfcommands = ( '"write freeblks 0"',
108 '"write longest 0"', '"write flcount 0"',
109 '"write bnolevel 1"', '"write cntlevel 1"',
110 '"write flfirst 0"', '"write fllast 0"' );
111 my @bnoprint = ( '"addr bnoroot"', '"print numrecs"' );
112 my @bnocommands = ( '"addr bnoroot"', '"write numrecs 0"',
113 '"write leftsib -1"', '"write rightsib -1"' );
114 my @cntprint = ( '"addr cntroot"', '"print numrecs"' );
115 my @cntcommands = ( '"addr cntroot"', '"write numrecs 0"',
116 '"write leftsib -1"', '"write rightsib -1"' );
118 print "=== Wiping ", $naglast - $nagfirst + 1,
119 " AGs starting from AG #", $nagfirst, "\n" unless $quiet;
122 while ($ag <= $naglast) {
123 print " o AG#", $ag, " AGF fields\n" unless $quiet;
125 my %agf = xfs_db "'agf $ag'", @agfprint;
126 xfs_db "'agf $ag'", @agfcommands;
128 my $blockcnt = $agf{'freeblks'} + $agf{'flcount'};
129 $sb{'fdblocks'} -= $blockcnt;
130 print " cleared ", $blockcnt, " blocks from AG#", $ag, "\n"
133 my %btree = xfs_db "'agf $ag'", @bnoprint;
134 xfs_db "'agf $ag'", @bnocommands, "'agf $ag'", @cntcommands;
135 print " cleared ", $btree{'numrecs'}, " BNO/CNT btree recs in AG#",
136 $ag, "\n" unless $quiet;
143 # Stage 3: Wipe out any partially masked allocation group.
147 print " o AG#", $ag, " AGF fields (partial)\n" unless $quiet;
149 my %ragf = xfs_db "'agf $ag'", '"print freeblks"',
150 '"addr bnoroot"', '"print recs[1].startblock"';
151 my $maskblks = $ragf{'freeblks'} - $retain;
152 my $newstart = $ragf{'recs[1].startblock'} + $maskblks;
154 "'write freeblks $retain'", "'write longest $retain'",
155 "'agf $ag'", '"addr bnoroot"',
156 "'write recs[1].startblock $newstart'",
157 "'write recs[1].blockcount $retain'",
158 "'agf $ag'", '"addr cntroot"',
159 "'write recs[1].startblock $newstart'",
160 "'write recs[1].blockcount $retain'";
162 $sb{'fdblocks'} -= $maskblks;
163 print " cleared ", $maskblks, " blocks from AG#", $ag, "\n"
167 print "=== Updating final freespace count, ", $sb{'fdblocks'}, " blocks\n"
169 xfs_db "'sb 0'", "'write fdblocks $sb{'fdblocks'}'"