generic: test MADV_POPULATE_READ with IO errors
[xfstests-dev.git] / tests / xfs / 177
1 #! /bin/bash
2 # SPDX-License-Identifier: GPL-2.0
3 # Copyright (c) 2021 Oracle.  All Rights Reserved.
4 #
5 # FS QA Test No. 177
6 #
7 # Functional test for commit:
8 #
9 # f38a032b165d ("xfs: fix I_DONTCACHE")
10 #
11 # Functional testing for the I_DONTCACHE inode flag, as set by the BULKSTAT
12 # ioctl.  This flag neuters the inode cache's tendency to try to hang on to
13 # incore inodes for a while after the last program closes the file, which
14 # is helpful for filesystem scanners to avoid trashing the inode cache.
15 #
16 # However, the inode cache doesn't always honor the DONTCACHE behavior -- the
17 # only time it really applies is to cache misses from a bulkstat scan.  If
18 # any other threads accessed the inode before or immediately after the scan,
19 # the DONTCACHE flag is ignored.  This includes other scans.
20 #
21 # Regrettably, there is no way to poke /only/ XFS inode reclamation directly,
22 # so we're stuck with setting xfssyncd_centisecs to a low value and sleeping
23 # while watching the internal inode cache counters.
24 #
25 . ./common/preamble
26 _begin_fstest auto ioctl
27
28 _cleanup()
29 {
30         cd /
31         rm -r -f $tmp.*
32         test -n "$old_centisecs" && echo "$old_centisecs" > "$xfs_centisecs_file"
33 }
34
35 # Import common functions.
36 . ./common/filter
37
38 # real QA test starts here
39
40 # Modify as appropriate.
41 _supported_fs xfs
42 _fixed_by_kernel_commit f38a032b165d "xfs: fix I_DONTCACHE"
43
44 _require_xfs_io_command "bulkstat"
45 _require_scratch
46
47 # We require /sys/fs/xfs/$device/stats/stats to monitor per-filesystem inode
48 # cache usage.
49 _require_fs_sysfs stats/stats
50
51 count_xfs_inode_objs() {
52         _get_fs_sysfs_attr $SCRATCH_DEV stats/stats | awk '/vnodes/ {print $2}'
53 }
54
55 dump_debug_info() {
56         echo "round $1 baseline: $baseline_count high: $high_count fresh: $fresh_count post: $post_count end: $end_count" >> $seqres.full
57 }
58
59 # Either of these need to be available to monitor slab usage
60 xfs_ino_objcount_file=/sys/kernel/slab/xfs_inode/objects
61 slabinfo_file=/proc/slabinfo
62 if [ ! -r "$xfs_ino_objcount_file" ] && [ ! -r "$slabinfo_file" ]; then
63         _notrun "Cannot find xfs_inode slab count?"
64 fi
65
66 # Background reclamation of disused xfs inodes is scheduled for ($xfssyncd / 6)
67 # centiseconds after the first inode is tagged for reclamation.  It's not great
68 # to encode this implementation detail in a test like this, but there isn't
69 # any means to trigger *only* inode cache reclaim -- actual memory pressure
70 # can trigger the VFS to drop non-DONTCACHE inodes, which is not what we want.
71 xfs_centisecs_file=/proc/sys/fs/xfs/xfssyncd_centisecs
72 test -w "$xfs_centisecs_file" || _notrun "Cannot find xfssyncd_centisecs?"
73
74 # Set the syncd knob to the minimum value 100cs (aka 1s) so that we don't have
75 # to wait forever.
76 old_centisecs="$(cat "$xfs_centisecs_file")"
77 echo 100 > "$xfs_centisecs_file" || _notrun "Cannot adjust xfssyncd_centisecs?"
78 delay_centisecs="$(cat "$xfs_centisecs_file")"
79
80 # Sleep one second more than the xfssyncd delay to give background inode
81 # reclaim enough time to run.
82 sleep_seconds=$(( ( (99 + (delay_centisecs / 6) ) / 100) + 1))
83 echo "Will sleep $sleep_seconds seconds to expire inodes" >> $seqres.full
84
85 _scratch_mkfs >> $seqres.full
86 _scratch_mount >> $seqres.full
87
88 junkdir=$SCRATCH_MNT/$seq.junk
89
90 # Sample the baseline count of cached inodes after a fresh remount.
91 _scratch_cycle_mount
92 baseline_count=$(count_xfs_inode_objs)
93
94 # Create a junk directory with about a thousand files.
95 nr_files=1024
96 mkdir -p $junkdir
97 for ((i = 0; i < nr_files; i++)); do
98         touch "$junkdir/$i"
99 done
100 new_files=$(find $junkdir | wc -l)
101 echo "created $new_files files" >> $seqres.full
102 _within_tolerance "new file count" $new_files $nr_files 5 -v
103
104 # Sanity check: Make sure that all those new inodes are still in the cache.
105 # We assume that memory limits are not so low that reclaim started for a bunch
106 # of empty files.  We hope there will never be more than 100 metadata inodes
107 # or automatic mount time scanners.
108 high_count=$(count_xfs_inode_objs)
109 echo "cached inodes: $high_count" >> $seqres.full
110 _within_tolerance "inodes after creating files" $high_count $new_files 0 100 -v
111
112 ################
113 # Round 1: Check DONTCACHE behavior when it is invoked once.  The inodes should
114 # be reclaimed if we wait longer than the reclaim interval.
115 echo "Round 1"
116
117 _scratch_cycle_mount
118 fresh_count=$(count_xfs_inode_objs)
119 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
120 post_count=$(count_xfs_inode_objs)
121 sleep $sleep_seconds
122 end_count=$(count_xfs_inode_objs)
123 dump_debug_info 1
124
125 # Even with our greatly reduced reclaim timeout, the inodes should still be in
126 # memory immediately after the bulkstat concludes.
127 _within_tolerance "inodes after bulkstat" $post_count $high_count 5 -v
128
129 # After we've given inode reclaim time to run, the inodes should no longer be
130 # cached in memory, which means we should have the fresh count again.
131 _within_tolerance "inodes after expire" $end_count $fresh_count 5 -v
132
133 ################
134 # Round 2: Check DONTCACHE behavior when it is invoked multiple times in rapid
135 # succession.  The inodes should remain in memory even after reclaim because
136 # the cache notices repeat DONTCACHE hits and ignores them.
137 echo "Round 2"
138
139 _scratch_cycle_mount
140 fresh_count=$(count_xfs_inode_objs)
141 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
142 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
143 post_count=$(count_xfs_inode_objs)
144 sleep $sleep_seconds
145 end_count=$(count_xfs_inode_objs)
146 dump_debug_info 2
147
148 # Inodes should still be in memory immediately after the second bulkstat
149 # concludes and after the reclaim interval.
150 _within_tolerance "inodes after double bulkstat" $post_count $high_count 5 -v
151 _within_tolerance "inodes after expire" $end_count $high_count 5 -v
152
153 ################
154 # Round 3: Check DONTCACHE evictions when it is invoked repeatedly but with
155 # enough time between scans for inode reclaim to remove the inodes.
156 echo "Round 3"
157
158 _scratch_cycle_mount
159 fresh_count=$(count_xfs_inode_objs)
160 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
161 sleep $sleep_seconds
162 post_count=$(count_xfs_inode_objs)
163 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
164 sleep $sleep_seconds
165 end_count=$(count_xfs_inode_objs)
166 dump_debug_info 3
167
168 # Inodes should not still be cached after either scan and reclaim interval.
169 _within_tolerance "inodes after slow bulkstat 1" $post_count $fresh_count 5 -v
170 _within_tolerance "inodes after slow bulkstat 2" $end_count $fresh_count 5 -v
171
172 ################
173 # Round 4: Check that DONTCACHE has no effect when all the files are read
174 # immediately after a bulkstat.
175 echo "Round 4"
176
177 _scratch_cycle_mount
178 fresh_count=$(count_xfs_inode_objs)
179 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
180 find $junkdir -type f -print0 | xargs -0 cat > /dev/null
181 post_count=$(count_xfs_inode_objs)
182 sleep $sleep_seconds
183 end_count=$(count_xfs_inode_objs)
184 dump_debug_info 4
185
186 # Inodes should still be cached after the scan/read and the reclaim interval.
187 _within_tolerance "inodes after bulkstat/read" $post_count $high_count 5 -v
188 _within_tolerance "inodes after reclaim" $end_count $high_count 5 -v
189
190 ################
191 # Round 5: Check that DONTCACHE has no effect if the inodes were already in
192 # cache due to reader programs.
193 echo "Round 5"
194
195 _scratch_cycle_mount
196 fresh_count=$(count_xfs_inode_objs)
197 find $junkdir -type f -print0 | xargs -0 cat > /dev/null
198 $XFS_IO_PROG -c 'bulkstat' $junkdir > /dev/null
199 post_count=$(count_xfs_inode_objs)
200 sleep $sleep_seconds
201 end_count=$(count_xfs_inode_objs)
202 dump_debug_info 5
203
204 # Inodes should still be cached after the read/scan and the reclaim interval.
205 _within_tolerance "inodes after read/bulkstat" $post_count $high_count 5 -v
206 _within_tolerance "inodes after reclaim" $end_count $high_count 5 -v
207
208 status=0
209 exit