016: Do not discard blocks at mkfs time
[xfstests-dev.git] / common.punch
1 ##/bin/bash
2 #
3 # Copyright (c) 2007 Silicon Graphics, Inc.  All Rights Reserved.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it would be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write the Free Software Foundation,
16 # Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 #
18 #
19 # common functions for excersizing hole punches with extent size hints etc.
20
21 # source dmap_scratch_mount etc.
22 . ./common.dmapi
23
24 _spawn_test_file() {
25         echo "# spawning test file with $*"
26         local blksize=$1
27         local file_size=`expr $2 \* $blksize`
28         local extent_size_hint=`expr $3 \* $blksize`
29         local test_file=$4
30         local reserve_space=$5
31
32         if [ $extent_size_hint -ne 0 ]; then
33                 echo "+ setting extent size hint to $extent_size_hint"
34                 $XFS_IO_PROG -f \
35                 -c "extsize $extent_size_hint" \
36                 $test_file
37         fi
38         # print extent size hint for $test_file
39         $XFS_IO_PROG -f \
40         -c "extsize" \
41         $test_file
42
43         if [ "$reserve_space" == "noresv" ]; then
44                 echo "+ not using resvsp at file creation"
45                 $XFS_IO_PROG -f \
46                 -c "truncate $file_size" \
47                 $test_file
48         else
49                 $XFS_IO_PROG -f \
50                 -c "truncate $file_size" \
51                 -c "resvsp 0 $file_size" \
52                 $test_file
53         fi
54 }
55
56 _do_punch() {
57         echo "# punching with $*"
58         # punch or bite the ear off $test_file to create a hole
59         local blksize=$1
60         local punch_offset=`expr $2 \* $blksize`
61         local punch_size=`expr $3 \* $blksize`
62         local punch_type=$4             # u for unresvsp, d for dm_punch
63         local test_file=$5
64
65         if [ "$punch_type" == "u" ]; then
66                 echo "+ hole punch using unresvsp"
67                 $XFS_IO_PROG -f \
68                 -c "unresvsp $punch_offset $punch_size" \
69                 $test_file
70         fi
71         if [ "$punch_type" == "d" ]; then
72                 echo "+ hole punch using dmapi punch_hole"
73                 ${DMAPI_QASUITE1_DIR}cmd/punch_hole -o $punch_offset -l $punch_size \
74                         ${SCRATCH_MNT}/$test_file
75         fi
76 }
77
78 _do_write() {
79         echo "# writing with $*"
80         local blksize=$1
81         local write_offset=`expr $2 \* $blksize`
82         local write_size=`expr $3 \* $blksize`
83         local test_file=$4
84
85         $XFS_IO_PROG -f \
86         -c "pwrite $write_offset $write_size" \
87         $test_file >/dev/null
88 }
89
90 _do_bmap() {
91         echo "# showing file state $*"
92         local test_file=$1
93
94         $XFS_IO_PROG -f \
95         -c "bmap -vvp" \
96         $test_file
97 }
98
99 _test_punch() {
100         echo "# testing $* ..."
101         local blksize=$1
102         # all points and sizes below are in terms of filesystem blocks
103         local extsize_hint_blks=$2              # extent size hint in FS blocks, 0=do not set
104         local file_size_blks=$3                 # the file size in blocks
105         local punch_points_blks=( $4 )  # array of places to punch holes in the file
106         local punch_sizes_blks=( $5 )   # array of size of each punch in blocks
107         local punch_types=( $6  )               # array of u=unresvsp or d=dm_punch
108         local write_points_blks=( $7 )  # array of places to pwrite in the file
109         local write_sizes_blks=( $8 )   # array of size of each write
110
111         local punch_write_order=( $9 )  # array of punch/write operation order
112                                                                         # e.g. "w p w w p" means: do 1st write...
113                                                                         # then 1st punch, 2nd & 3rd write, 2nd punch
114         local resvsp=${10}                              # if "noresv" then don't resvsp on file create
115         local filename=punch_test_file
116
117         cd /
118         umount $SCRATCH_MNT >/dev/null 2>&1
119
120         _scratch_mkfs_xfs -bsize=$blksize >/dev/null 2>&1 \
121                 || _fail "mkfs failed"
122
123         local this_punch_type=""
124         local dmap_punch_used=0
125         for this_punch_type in "${punch_types[@]}"; do
126                 [ "$this_punch_type" == "d" ] && dmap_punch_used=1
127         done
128         if [ $dmap_punch_used -ne 0 ]; then
129                 # a punch type of dm_punch has been specified, do a dmapi mount
130                 echo "+ mounting with dmapi enabled"
131                 _dmapi_scratch_mount
132         else
133                 # only unresvsp punch type is used, just do a normal mount
134                 _scratch_mount || _fail "mount failed"
135         fi
136
137         cd $SCRATCH_MNT
138
139         # check a size is specified for each punch
140         [ ${#punch_points_blks[*]} -eq ${#punch_sizes_blks[*]} ] \
141                 || _fail "num punch points given does not equal num punch sizes"
142
143         # check a type is specified for each punch
144         [ ${#punch_points_blks[*]} -eq ${#punch_types[*]} ] \
145                 || _fail "num punch points given does not equal num punch types"
146
147         # check a size is specified for each write
148         [ ${#write_points_blks[*]} -eq ${#write_sizes_blks[*]} ] \
149                 || _fail "num write points given does not equal num write sizes"
150
151         # check punch_write_order operations match number of punches + writes
152         local total_pw_operations=`expr ${#punch_points_blks[*]} + ${#write_points_blks[*]}`
153         [ $total_pw_operations -eq ${#punch_write_order[*]} ] \
154                 || _fail "punch_write_order ops doesn't match number of punches + writes"
155
156         # create the file and setup extent size hint
157         _spawn_test_file $blksize $file_size_blks $extsize_hint_blks $filename $resvsp
158
159         # do the writes and punches
160         local operation=""
161         local punch_index=0
162         local write_index=0
163         for operation in "${punch_write_order[@]}"; do
164                 if [ "$operation" == "p" ]; then
165                         _do_punch $blksize ${punch_points_blks[$punch_index]} \
166                                 ${punch_sizes_blks[$punch_index]} ${punch_types[$punch_index]} \
167                                 $filename
168                         punch_index=`expr $punch_index + 1`
169                 fi
170                 if [ "$operation" == "w" ]; then
171                         _do_write $blksize ${write_points_blks[$write_index]} \
172                                 ${write_sizes_blks[$write_index]} $filename
173                         write_index=`expr $write_index + 1`
174                 fi
175                 sync
176                 _do_bmap $filename              # print out the state of the file
177         done
178 }
179
180 _coalesce_extents()
181 {
182         awk -F: '
183         {
184                 range = $2;
185                 type = $3;
186
187                 split(range, bounds, "[\\[ \\.\\]]");
188                 low = bounds[3];
189                 high = bounds[5];
190
191                 if (type != prev_type) {
192                         if (prev_type != "")
193                                 printf("%u]:%s\n", low - 1, prev_type);
194                         printf("%u: [%u..", out_count++, low);
195                         prev_type = type;
196                 }
197         }
198         END {
199                 if (prev_type != "")
200                         printf("%u]:%s\n", high, prev_type);
201         }'
202 }
203
204 _filter_fiemap()
205 {
206         awk --posix '
207                 $3 ~ /hole/ {
208                         print $1, $2, $3;
209                         next;
210                 }
211                 $5 ~ /0x[[:xdigit:]]*8[[:xdigit:]]{2}/ {
212                         print $1, $2, "unwritten";
213                         next;
214                 }
215                 $5 ~ /0x[[:xdigit:]]+/ {
216                         print $1, $2, "data";
217                 }' |
218         _coalesce_extents
219 }
220
221 # Filters fiemap output to only print the 
222 # file offset column and whether or not
223 # it is an extent or a hole
224 _filter_hole_fiemap()
225 {
226         awk --posix '
227                 $3 ~ /hole/ {
228                         print $1, $2, $3; 
229                         next;
230                 }   
231                 $5 ~ /0x[[:xdigit:]]+/ {
232                         print $1, $2, "extent";
233                 }' |
234         _coalesce_extents
235 }
236
237
238 # Prints the md5 checksum of a given file
239 _md5_checksum()
240 {
241         md5sum $1 | cut -d ' ' -f1
242 }
243
244 _filter_bmap()
245 {
246         awk '
247                 $3 ~ /hole/ {
248                         print $1, $2, $3;
249                         next;
250                 }
251                 $7 ~ /10000/ {
252                         print $1, $2, "unwritten";
253                         next;
254                 }
255                 $7 ~ /00000/ {
256                         print $1, $2, "data"
257                 }' |
258         _coalesce_extents
259 }
260
261 die_now()
262 {
263         status=1
264         exit
265 }
266
267 # test the different corner cases for zeroing a range:
268 #
269 #       1. into a hole
270 #       2. into allocated space
271 #       3. into unwritten space
272 #       4. hole -> data
273 #       5. hole -> unwritten
274 #       6. data -> hole
275 #       7. data -> unwritten
276 #       8. unwritten -> hole
277 #       9. unwritten -> data
278 #       10. hole -> data -> hole
279 #       11. data -> hole -> data
280 #       12. unwritten -> data -> unwritten
281 #       13. data -> unwritten -> data
282 #       14. data -> hole @ EOF
283 #       15. data -> hole @ 0
284 #       16. data -> cache cold ->hole
285 #       17. data -> hole in single block file
286 #
287 # Test file is removed, created and sync'd between tests.
288 #
289 # Use -k flag to keep the file between tests.  This will
290 # test the handling of pre-existing holes.
291 #
292 # Use the -d flag to not sync the file between tests.
293 # This will test the handling of delayed extents
294 #
295 _test_generic_punch()
296 {
297
298         remove_testfile=1
299         sync_cmd="-c fsync"
300         OPTIND=1
301         while getopts 'dk' OPTION
302         do
303                 case $OPTION in
304                 k)      remove_testfile=
305                 ;;
306                 d)      sync_cmd=
307                 ;;
308                 ?)      echo Invalid flag
309                 exit 1
310                 ;;
311                 esac
312         done
313         shift $(($OPTIND - 1))
314
315         alloc_cmd=$1
316         punch_cmd=$2
317         zero_cmd=$3     #if not testing zero just set to punch
318         map_cmd=$4
319         filter_cmd=$5
320         testfile=$6
321         xfs_io_opt=$7   #needs to be -F if not testing xfs
322
323         echo "  1. into a hole"
324         if [ "$remove_testfile" ]; then
325                 rm -f $testfile
326         fi
327         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
328                 -c "$zero_cmd 4k 8k" \
329                 -c "$map_cmd -v" $testfile | $filter_cmd
330         [ $? -ne 0 ] && die_now
331         _md5_checksum $testfile
332
333         echo "  2. into allocated space"
334         if [ "$remove_testfile" ]; then
335                 rm -f $testfile
336         fi
337         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
338                 -c "pwrite 0 20k" $sync_cmd \
339                 -c "$zero_cmd 4k 8k" \
340                 -c "$map_cmd -v" $testfile | $filter_cmd
341         [ $? -ne 0 ] && die_now
342         _md5_checksum $testfile
343
344         echo "  3. into unwritten space"
345         if [ "$remove_testfile" ]; then
346                 rm -f $testfile
347         fi
348         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
349                 -c "$alloc_cmd 0 20k" \
350                 -c "$zero_cmd 4k 8k" \
351                 -c "$map_cmd -v" $testfile | $filter_cmd
352         [ $? -ne 0 ] && die_now
353         _md5_checksum $testfile
354
355         echo "  4. hole -> data"
356         if [ "$remove_testfile" ]; then
357                 rm -f $testfile
358         fi
359         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
360                 -c "pwrite 8k 8k" $sync_cmd \
361                 -c "$zero_cmd 4k 8k" \
362                 -c "$map_cmd -v" $testfile | $filter_cmd
363         [ $? -ne 0 ] && die_now
364         _md5_checksum $testfile
365
366         echo "  5. hole -> unwritten"
367         if [ "$remove_testfile" ]; then
368                 rm -f $testfile
369         fi
370         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
371                 -c "$alloc_cmd 8k 8k" \
372                 -c "$zero_cmd 4k 8k" \
373                 -c "$map_cmd -v" $testfile | $filter_cmd
374         [ $? -ne 0 ] && die_now
375         _md5_checksum $testfile
376
377         echo "  6. data -> hole"
378         if [ "$remove_testfile" ]; then
379                 rm -f $testfile
380         fi
381         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
382                 -c "pwrite 0 8k" $sync_cmd \
383                 -c "$zero_cmd 4k 8k" \
384                 -c "$map_cmd -v" $testfile | $filter_cmd
385         [ $? -ne 0 ] && die_now
386         _md5_checksum $testfile
387
388         echo "  7. data -> unwritten"
389         if [ "$remove_testfile" ]; then
390                 rm -f $testfile
391         fi
392         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
393                 -c "pwrite 0 8k" $sync_cmd \
394                 -c "$alloc_cmd 8k 8k" \
395                 -c "$zero_cmd 4k 8k" \
396                 -c "$map_cmd -v" $testfile | $filter_cmd
397         [ $? -ne 0 ] && die_now
398         _md5_checksum $testfile
399
400         echo "  8. unwritten -> hole"
401         if [ "$remove_testfile" ]; then
402                 rm -f $testfile
403         fi
404         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
405                 -c "$alloc_cmd 0 8k" \
406                 -c "$zero_cmd 4k 8k" \
407                 -c "$map_cmd -v" $testfile | $filter_cmd
408         [ $? -ne 0 ] && die_now
409         _md5_checksum $testfile
410
411         echo "  9. unwritten -> data"
412         if [ "$remove_testfile" ]; then
413                 rm -f $testfile
414         fi
415         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
416                 -c "$alloc_cmd 0 8k" \
417                 -c "pwrite 8k 8k" $sync_cmd \
418                 -c "$zero_cmd 4k 8k" \
419                 -c "$map_cmd -v" $testfile | $filter_cmd
420         [ $? -ne 0 ] && die_now
421         _md5_checksum $testfile
422
423         echo "  10. hole -> data -> hole"
424         if [ "$remove_testfile" ]; then
425                 rm -f $testfile
426         fi
427         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
428                 -c "pwrite 8k 4k" $sync_cmd \
429                 -c "$zero_cmd 4k 12k" \
430                 -c "$map_cmd -v" $testfile | $filter_cmd
431         [ $? -ne 0 ] && die_now
432         _md5_checksum $testfile
433
434         echo "  11. data -> hole -> data"
435         if [ "$remove_testfile" ]; then
436                 rm -f $testfile
437         fi
438         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
439                 -c "$alloc_cmd 0 20k" \
440                 -c "pwrite 0 8k" \
441                 -c "pwrite 12k 8k" $sync_cmd \
442                 -c "$punch_cmd 8k 4k" \
443                 -c "$zero_cmd 4k 12k" \
444                 -c "$map_cmd -v" $testfile | $filter_cmd
445         [ $? -ne 0 ] && die_now
446         _md5_checksum $testfile
447
448         echo "  12. unwritten -> data -> unwritten"
449         if [ "$remove_testfile" ]; then
450                 rm -f $testfile
451         fi
452         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
453                 -c "$alloc_cmd 0 20k" \
454                 -c "pwrite 8k 4k" $sync_cmd \
455                 -c "$zero_cmd 4k 12k" \
456                 -c "$map_cmd -v" $testfile | $filter_cmd
457         [ $? -ne 0 ] && die_now
458         _md5_checksum $testfile
459
460         echo "  13. data -> unwritten -> data"
461         if [ "$remove_testfile" ]; then
462                 rm -f $testfile
463         fi
464         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
465                 -c "$alloc_cmd 0 20k" \
466                 -c "pwrite 0k 8k" $sync_cmd \
467                 -c "pwrite 12k 8k" -c "fsync" \
468                 -c "$zero_cmd 4k 12k" \
469                 -c "$map_cmd -v" $testfile | $filter_cmd
470         [ $? -ne 0 ] && die_now
471         _md5_checksum $testfile
472
473         echo "  14. data -> hole @ EOF"
474         rm -f $testfile
475         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
476                 -c "pwrite 0 20k" $sync_cmd \
477                 -c "$zero_cmd 12k 8k" \
478                 -c "$map_cmd -v" $testfile | $filter_cmd
479         [ $? -ne 0 ] && die_now
480         _md5_checksum $testfile
481
482         echo "  15. data -> hole @ 0"
483         if [ "$remove_testfile" ]; then
484                 rm -f $testfile
485         fi
486         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
487                 -c "pwrite 0 20k" $sync_cmd \
488                 -c "$zero_cmd 0k 8k" \
489                 -c "$map_cmd -v" $testfile | $filter_cmd
490         [ $? -ne 0 ] && die_now
491         _md5_checksum $testfile
492
493         echo "  16. data -> cache cold ->hole"
494         if [ "$remove_testfile" ]; then
495                 rm -f $testfile
496                 rm -f $testfile.2
497         else
498                 cp $testfile $testfile.2
499         fi
500         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
501                 -c "pwrite 8k 12k" -c "fsync" $testfile.2 \
502                 > /dev/null
503         $XFS_IO_PROG $xfs_io_opt -f -c "truncate 20k" \
504                 -c "pwrite 0 20k" $sync_cmd \
505                 -c "$zero_cmd 0k 8k" \
506                 -c "fadvise -d" \
507                 -c "$map_cmd -v" $testfile | $filter_cmd
508         diff $testfile $testfile.2
509         [ $? -ne 0 ] && die_now
510         rm -f $testfile.2
511         _md5_checksum $testfile
512
513         echo "  17. data -> hole in single block file"
514         if [ "$remove_testfile" ]; then
515                 rm -f $testfile
516         fi
517         block_size=`stat -f $TEST_DEV | grep "Block size" | cut -d " " -f3`
518         $XFS_IO_PROG $xfs_io_opt -f -c "truncate $block_size" \
519                 -c "pwrite 0 $block_size" $sync_cmd \
520                 -c "$zero_cmd 128 128" \
521                 -c "$map_cmd -v" $testfile | $filter_cmd
522         [ $? -ne 0 ] && die_now
523         _md5_checksum $testfile
524
525 }