aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/scrub.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs/scrub.c')
-rw-r--r--fs/btrfs/scrub.c114
1 files changed, 51 insertions, 63 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index eba42e5fd5fd..94cd3a19e9c8 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -33,15 +33,12 @@
33 * any can be found. 33 * any can be found.
34 * 34 *
35 * Future enhancements: 35 * Future enhancements:
36 * - To enhance the performance, better read-ahead strategies for the
37 * extent-tree can be employed.
38 * - In case an unrepairable extent is encountered, track which files are 36 * - In case an unrepairable extent is encountered, track which files are
39 * affected and report them 37 * affected and report them
40 * - In case of a read error on files with nodatasum, map the file and read 38 * - In case of a read error on files with nodatasum, map the file and read
41 * the extent to trigger a writeback of the good copy 39 * the extent to trigger a writeback of the good copy
42 * - track and record media errors, throw out bad devices 40 * - track and record media errors, throw out bad devices
43 * - add a mode to also read unallocated space 41 * - add a mode to also read unallocated space
44 * - make the prefetch cancellable
45 */ 42 */
46 43
47struct scrub_bio; 44struct scrub_bio;
@@ -209,7 +206,7 @@ struct scrub_dev *scrub_setup_dev(struct btrfs_device *dev)
209 atomic_set(&sdev->in_flight, 0); 206 atomic_set(&sdev->in_flight, 0);
210 atomic_set(&sdev->fixup_cnt, 0); 207 atomic_set(&sdev->fixup_cnt, 0);
211 atomic_set(&sdev->cancel_req, 0); 208 atomic_set(&sdev->cancel_req, 0);
212 sdev->csum_size = btrfs_super_csum_size(&fs_info->super_copy); 209 sdev->csum_size = btrfs_super_csum_size(fs_info->super_copy);
213 INIT_LIST_HEAD(&sdev->csum_list); 210 INIT_LIST_HEAD(&sdev->csum_list);
214 211
215 spin_lock_init(&sdev->list_lock); 212 spin_lock_init(&sdev->list_lock);
@@ -1130,13 +1127,16 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev,
1130 int slot; 1127 int slot;
1131 int i; 1128 int i;
1132 u64 nstripes; 1129 u64 nstripes;
1133 int start_stripe;
1134 struct extent_buffer *l; 1130 struct extent_buffer *l;
1135 struct btrfs_key key; 1131 struct btrfs_key key;
1136 u64 physical; 1132 u64 physical;
1137 u64 logical; 1133 u64 logical;
1138 u64 generation; 1134 u64 generation;
1139 int mirror_num; 1135 int mirror_num;
1136 struct reada_control *reada1;
1137 struct reada_control *reada2;
1138 struct btrfs_key key_start;
1139 struct btrfs_key key_end;
1140 1140
1141 u64 increment = map->stripe_len; 1141 u64 increment = map->stripe_len;
1142 u64 offset; 1142 u64 offset;
@@ -1168,81 +1168,67 @@ static noinline_for_stack int scrub_stripe(struct scrub_dev *sdev,
1168 if (!path) 1168 if (!path)
1169 return -ENOMEM; 1169 return -ENOMEM;
1170 1170
1171 path->reada = 2;
1172 path->search_commit_root = 1; 1171 path->search_commit_root = 1;
1173 path->skip_locking = 1; 1172 path->skip_locking = 1;
1174 1173
1175 /* 1174 /*
1176 * find all extents for each stripe and just read them to get 1175 * trigger the readahead for extent tree csum tree and wait for
1177 * them into the page cache 1176 * completion. During readahead, the scrub is officially paused
1178 * FIXME: we can do better. build a more intelligent prefetching 1177 * to not hold off transaction commits
1179 */ 1178 */
1180 logical = base + offset; 1179 logical = base + offset;
1181 physical = map->stripes[num].physical;
1182 ret = 0;
1183 for (i = 0; i < nstripes; ++i) {
1184 key.objectid = logical;
1185 key.type = BTRFS_EXTENT_ITEM_KEY;
1186 key.offset = (u64)0;
1187
1188 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
1189 if (ret < 0)
1190 goto out_noplug;
1191
1192 /*
1193 * we might miss half an extent here, but that doesn't matter,
1194 * as it's only the prefetch
1195 */
1196 while (1) {
1197 l = path->nodes[0];
1198 slot = path->slots[0];
1199 if (slot >= btrfs_header_nritems(l)) {
1200 ret = btrfs_next_leaf(root, path);
1201 if (ret == 0)
1202 continue;
1203 if (ret < 0)
1204 goto out_noplug;
1205 1180
1206 break; 1181 wait_event(sdev->list_wait,
1207 } 1182 atomic_read(&sdev->in_flight) == 0);
1208 btrfs_item_key_to_cpu(l, &key, slot); 1183 atomic_inc(&fs_info->scrubs_paused);
1184 wake_up(&fs_info->scrub_pause_wait);
1209 1185
1210 if (key.objectid >= logical + map->stripe_len) 1186 /* FIXME it might be better to start readahead at commit root */
1211 break; 1187 key_start.objectid = logical;
1188 key_start.type = BTRFS_EXTENT_ITEM_KEY;
1189 key_start.offset = (u64)0;
1190 key_end.objectid = base + offset + nstripes * increment;
1191 key_end.type = BTRFS_EXTENT_ITEM_KEY;
1192 key_end.offset = (u64)0;
1193 reada1 = btrfs_reada_add(root, &key_start, &key_end);
1194
1195 key_start.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
1196 key_start.type = BTRFS_EXTENT_CSUM_KEY;
1197 key_start.offset = logical;
1198 key_end.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
1199 key_end.type = BTRFS_EXTENT_CSUM_KEY;
1200 key_end.offset = base + offset + nstripes * increment;
1201 reada2 = btrfs_reada_add(csum_root, &key_start, &key_end);
1202
1203 if (!IS_ERR(reada1))
1204 btrfs_reada_wait(reada1);
1205 if (!IS_ERR(reada2))
1206 btrfs_reada_wait(reada2);
1212 1207
1213 path->slots[0]++; 1208 mutex_lock(&fs_info->scrub_lock);
1214 } 1209 while (atomic_read(&fs_info->scrub_pause_req)) {
1215 btrfs_release_path(path); 1210 mutex_unlock(&fs_info->scrub_lock);
1216 logical += increment; 1211 wait_event(fs_info->scrub_pause_wait,
1217 physical += map->stripe_len; 1212 atomic_read(&fs_info->scrub_pause_req) == 0);
1218 cond_resched(); 1213 mutex_lock(&fs_info->scrub_lock);
1219 } 1214 }
1215 atomic_dec(&fs_info->scrubs_paused);
1216 mutex_unlock(&fs_info->scrub_lock);
1217 wake_up(&fs_info->scrub_pause_wait);
1220 1218
1221 /* 1219 /*
1222 * collect all data csums for the stripe to avoid seeking during 1220 * collect all data csums for the stripe to avoid seeking during
1223 * the scrub. This might currently (crc32) end up to be about 1MB 1221 * the scrub. This might currently (crc32) end up to be about 1MB
1224 */ 1222 */
1225 start_stripe = 0;
1226 blk_start_plug(&plug); 1223 blk_start_plug(&plug);
1227again:
1228 logical = base + offset + start_stripe * increment;
1229 for (i = start_stripe; i < nstripes; ++i) {
1230 ret = btrfs_lookup_csums_range(csum_root, logical,
1231 logical + map->stripe_len - 1,
1232 &sdev->csum_list, 1);
1233 if (ret)
1234 goto out;
1235 1224
1236 logical += increment;
1237 cond_resched();
1238 }
1239 /* 1225 /*
1240 * now find all extents for each stripe and scrub them 1226 * now find all extents for each stripe and scrub them
1241 */ 1227 */
1242 logical = base + offset + start_stripe * increment; 1228 logical = base + offset;
1243 physical = map->stripes[num].physical + start_stripe * map->stripe_len; 1229 physical = map->stripes[num].physical;
1244 ret = 0; 1230 ret = 0;
1245 for (i = start_stripe; i < nstripes; ++i) { 1231 for (i = 0; i < nstripes; ++i) {
1246 /* 1232 /*
1247 * canceled? 1233 * canceled?
1248 */ 1234 */
@@ -1271,11 +1257,14 @@ again:
1271 atomic_dec(&fs_info->scrubs_paused); 1257 atomic_dec(&fs_info->scrubs_paused);
1272 mutex_unlock(&fs_info->scrub_lock); 1258 mutex_unlock(&fs_info->scrub_lock);
1273 wake_up(&fs_info->scrub_pause_wait); 1259 wake_up(&fs_info->scrub_pause_wait);
1274 scrub_free_csums(sdev);
1275 start_stripe = i;
1276 goto again;
1277 } 1260 }
1278 1261
1262 ret = btrfs_lookup_csums_range(csum_root, logical,
1263 logical + map->stripe_len - 1,
1264 &sdev->csum_list, 1);
1265 if (ret)
1266 goto out;
1267
1279 key.objectid = logical; 1268 key.objectid = logical;
1280 key.type = BTRFS_EXTENT_ITEM_KEY; 1269 key.type = BTRFS_EXTENT_ITEM_KEY;
1281 key.offset = (u64)0; 1270 key.offset = (u64)0;
@@ -1371,7 +1360,6 @@ next:
1371 1360
1372out: 1361out:
1373 blk_finish_plug(&plug); 1362 blk_finish_plug(&plug);
1374out_noplug:
1375 btrfs_free_path(path); 1363 btrfs_free_path(path);
1376 return ret < 0 ? ret : 0; 1364 return ret < 0 ? ret : 0;
1377} 1365}