diff options
Diffstat (limited to 'fs/btrfs/scrub.c')
-rw-r--r-- | fs/btrfs/scrub.c | 114 |
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 | ||
47 | struct scrub_bio; | 44 | struct 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); |
1227 | again: | ||
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 | ||
1372 | out: | 1361 | out: |
1373 | blk_finish_plug(&plug); | 1362 | blk_finish_plug(&plug); |
1374 | out_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 | } |