diff options
Diffstat (limited to 'fs/btrfs/scrub.c')
-rw-r--r-- | fs/btrfs/scrub.c | 195 |
1 files changed, 121 insertions, 74 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 822c08a420c2..15ac82ae5770 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -46,6 +46,12 @@ struct scrub_ctx; | |||
46 | 46 | ||
47 | #define SCRUB_PAGES_PER_BIO 16 /* 64k per bio */ | 47 | #define SCRUB_PAGES_PER_BIO 16 /* 64k per bio */ |
48 | #define SCRUB_BIOS_PER_CTX 16 /* 1 MB per device in flight */ | 48 | #define SCRUB_BIOS_PER_CTX 16 /* 1 MB per device in flight */ |
49 | |||
50 | /* | ||
51 | * the following value times PAGE_SIZE needs to be large enough to match the | ||
52 | * largest node/leaf/sector size that shall be supported. | ||
53 | * Values larger than BTRFS_STRIPE_LEN are not supported. | ||
54 | */ | ||
49 | #define SCRUB_MAX_PAGES_PER_BLOCK 16 /* 64k per node/leaf/sector */ | 55 | #define SCRUB_MAX_PAGES_PER_BLOCK 16 /* 64k per node/leaf/sector */ |
50 | 56 | ||
51 | struct scrub_page { | 57 | struct scrub_page { |
@@ -56,6 +62,7 @@ struct scrub_page { | |||
56 | u64 generation; | 62 | u64 generation; |
57 | u64 logical; | 63 | u64 logical; |
58 | u64 physical; | 64 | u64 physical; |
65 | atomic_t ref_count; | ||
59 | struct { | 66 | struct { |
60 | unsigned int mirror_num:8; | 67 | unsigned int mirror_num:8; |
61 | unsigned int have_csum:1; | 68 | unsigned int have_csum:1; |
@@ -79,7 +86,7 @@ struct scrub_bio { | |||
79 | }; | 86 | }; |
80 | 87 | ||
81 | struct scrub_block { | 88 | struct scrub_block { |
82 | struct scrub_page pagev[SCRUB_MAX_PAGES_PER_BLOCK]; | 89 | struct scrub_page *pagev[SCRUB_MAX_PAGES_PER_BLOCK]; |
83 | int page_count; | 90 | int page_count; |
84 | atomic_t outstanding_pages; | 91 | atomic_t outstanding_pages; |
85 | atomic_t ref_count; /* free mem on transition to zero */ | 92 | atomic_t ref_count; /* free mem on transition to zero */ |
@@ -165,6 +172,8 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock); | |||
165 | static int scrub_checksum_super(struct scrub_block *sblock); | 172 | static int scrub_checksum_super(struct scrub_block *sblock); |
166 | static void scrub_block_get(struct scrub_block *sblock); | 173 | static void scrub_block_get(struct scrub_block *sblock); |
167 | static void scrub_block_put(struct scrub_block *sblock); | 174 | static void scrub_block_put(struct scrub_block *sblock); |
175 | static void scrub_page_get(struct scrub_page *spage); | ||
176 | static void scrub_page_put(struct scrub_page *spage); | ||
168 | static int scrub_add_page_to_bio(struct scrub_ctx *sctx, | 177 | static int scrub_add_page_to_bio(struct scrub_ctx *sctx, |
169 | struct scrub_page *spage); | 178 | struct scrub_page *spage); |
170 | static int scrub_pages(struct scrub_ctx *sctx, u64 logical, u64 len, | 179 | static int scrub_pages(struct scrub_ctx *sctx, u64 logical, u64 len, |
@@ -364,15 +373,15 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock) | |||
364 | int ret; | 373 | int ret; |
365 | 374 | ||
366 | WARN_ON(sblock->page_count < 1); | 375 | WARN_ON(sblock->page_count < 1); |
367 | dev = sblock->pagev[0].dev; | 376 | dev = sblock->pagev[0]->dev; |
368 | fs_info = sblock->sctx->dev_root->fs_info; | 377 | fs_info = sblock->sctx->dev_root->fs_info; |
369 | 378 | ||
370 | path = btrfs_alloc_path(); | 379 | path = btrfs_alloc_path(); |
371 | 380 | ||
372 | swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS); | 381 | swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS); |
373 | swarn.msg_buf = kmalloc(bufsize, GFP_NOFS); | 382 | swarn.msg_buf = kmalloc(bufsize, GFP_NOFS); |
374 | swarn.sector = (sblock->pagev[0].physical) >> 9; | 383 | swarn.sector = (sblock->pagev[0]->physical) >> 9; |
375 | swarn.logical = sblock->pagev[0].logical; | 384 | swarn.logical = sblock->pagev[0]->logical; |
376 | swarn.errstr = errstr; | 385 | swarn.errstr = errstr; |
377 | swarn.dev = NULL; | 386 | swarn.dev = NULL; |
378 | swarn.msg_bufsize = bufsize; | 387 | swarn.msg_bufsize = bufsize; |
@@ -642,15 +651,15 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check) | |||
642 | BUG_ON(sblock_to_check->page_count < 1); | 651 | BUG_ON(sblock_to_check->page_count < 1); |
643 | fs_info = sctx->dev_root->fs_info; | 652 | fs_info = sctx->dev_root->fs_info; |
644 | length = sblock_to_check->page_count * PAGE_SIZE; | 653 | length = sblock_to_check->page_count * PAGE_SIZE; |
645 | logical = sblock_to_check->pagev[0].logical; | 654 | logical = sblock_to_check->pagev[0]->logical; |
646 | generation = sblock_to_check->pagev[0].generation; | 655 | generation = sblock_to_check->pagev[0]->generation; |
647 | BUG_ON(sblock_to_check->pagev[0].mirror_num < 1); | 656 | BUG_ON(sblock_to_check->pagev[0]->mirror_num < 1); |
648 | failed_mirror_index = sblock_to_check->pagev[0].mirror_num - 1; | 657 | failed_mirror_index = sblock_to_check->pagev[0]->mirror_num - 1; |
649 | is_metadata = !(sblock_to_check->pagev[0].flags & | 658 | is_metadata = !(sblock_to_check->pagev[0]->flags & |
650 | BTRFS_EXTENT_FLAG_DATA); | 659 | BTRFS_EXTENT_FLAG_DATA); |
651 | have_csum = sblock_to_check->pagev[0].have_csum; | 660 | have_csum = sblock_to_check->pagev[0]->have_csum; |
652 | csum = sblock_to_check->pagev[0].csum; | 661 | csum = sblock_to_check->pagev[0]->csum; |
653 | dev = sblock_to_check->pagev[0].dev; | 662 | dev = sblock_to_check->pagev[0]->dev; |
654 | 663 | ||
655 | /* | 664 | /* |
656 | * read all mirrors one after the other. This includes to | 665 | * read all mirrors one after the other. This includes to |
@@ -892,7 +901,7 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check) | |||
892 | 901 | ||
893 | success = 1; | 902 | success = 1; |
894 | for (page_num = 0; page_num < sblock_bad->page_count; page_num++) { | 903 | for (page_num = 0; page_num < sblock_bad->page_count; page_num++) { |
895 | struct scrub_page *page_bad = sblock_bad->pagev + page_num; | 904 | struct scrub_page *page_bad = sblock_bad->pagev[page_num]; |
896 | 905 | ||
897 | if (!page_bad->io_error) | 906 | if (!page_bad->io_error) |
898 | continue; | 907 | continue; |
@@ -903,8 +912,8 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check) | |||
903 | mirror_index++) { | 912 | mirror_index++) { |
904 | struct scrub_block *sblock_other = sblocks_for_recheck + | 913 | struct scrub_block *sblock_other = sblocks_for_recheck + |
905 | mirror_index; | 914 | mirror_index; |
906 | struct scrub_page *page_other = sblock_other->pagev + | 915 | struct scrub_page *page_other = sblock_other->pagev[ |
907 | page_num; | 916 | page_num]; |
908 | 917 | ||
909 | if (!page_other->io_error) { | 918 | if (!page_other->io_error) { |
910 | ret = scrub_repair_page_from_good_copy( | 919 | ret = scrub_repair_page_from_good_copy( |
@@ -971,11 +980,11 @@ out: | |||
971 | mirror_index; | 980 | mirror_index; |
972 | int page_index; | 981 | int page_index; |
973 | 982 | ||
974 | for (page_index = 0; page_index < SCRUB_PAGES_PER_BIO; | 983 | for (page_index = 0; page_index < sblock->page_count; |
975 | page_index++) | 984 | page_index++) { |
976 | if (sblock->pagev[page_index].page) | 985 | sblock->pagev[page_index]->sblock = NULL; |
977 | __free_page( | 986 | scrub_page_put(sblock->pagev[page_index]); |
978 | sblock->pagev[page_index].page); | 987 | } |
979 | } | 988 | } |
980 | kfree(sblocks_for_recheck); | 989 | kfree(sblocks_for_recheck); |
981 | } | 990 | } |
@@ -993,7 +1002,7 @@ static int scrub_setup_recheck_block(struct scrub_ctx *sctx, | |||
993 | int ret; | 1002 | int ret; |
994 | 1003 | ||
995 | /* | 1004 | /* |
996 | * note: the three members sctx, ref_count and outstanding_pages | 1005 | * note: the two members ref_count and outstanding_pages |
997 | * are not used (and not set) in the blocks that are used for | 1006 | * are not used (and not set) in the blocks that are used for |
998 | * the recheck procedure | 1007 | * the recheck procedure |
999 | */ | 1008 | */ |
@@ -1025,21 +1034,27 @@ static int scrub_setup_recheck_block(struct scrub_ctx *sctx, | |||
1025 | continue; | 1034 | continue; |
1026 | 1035 | ||
1027 | sblock = sblocks_for_recheck + mirror_index; | 1036 | sblock = sblocks_for_recheck + mirror_index; |
1028 | page = sblock->pagev + page_index; | 1037 | sblock->sctx = sctx; |
1029 | page->logical = logical; | 1038 | page = kzalloc(sizeof(*page), GFP_NOFS); |
1030 | page->physical = bbio->stripes[mirror_index].physical; | 1039 | if (!page) { |
1031 | /* for missing devices, dev->bdev is NULL */ | 1040 | leave_nomem: |
1032 | page->dev = bbio->stripes[mirror_index].dev; | ||
1033 | page->mirror_num = mirror_index + 1; | ||
1034 | page->page = alloc_page(GFP_NOFS); | ||
1035 | if (!page->page) { | ||
1036 | spin_lock(&sctx->stat_lock); | 1041 | spin_lock(&sctx->stat_lock); |
1037 | sctx->stat.malloc_errors++; | 1042 | sctx->stat.malloc_errors++; |
1038 | spin_unlock(&sctx->stat_lock); | 1043 | spin_unlock(&sctx->stat_lock); |
1039 | kfree(bbio); | 1044 | kfree(bbio); |
1040 | return -ENOMEM; | 1045 | return -ENOMEM; |
1041 | } | 1046 | } |
1047 | scrub_page_get(page); | ||
1048 | sblock->pagev[page_index] = page; | ||
1049 | page->logical = logical; | ||
1050 | page->physical = bbio->stripes[mirror_index].physical; | ||
1051 | /* for missing devices, dev->bdev is NULL */ | ||
1052 | page->dev = bbio->stripes[mirror_index].dev; | ||
1053 | page->mirror_num = mirror_index + 1; | ||
1042 | sblock->page_count++; | 1054 | sblock->page_count++; |
1055 | page->page = alloc_page(GFP_NOFS); | ||
1056 | if (!page->page) | ||
1057 | goto leave_nomem; | ||
1043 | } | 1058 | } |
1044 | kfree(bbio); | 1059 | kfree(bbio); |
1045 | length -= sublen; | 1060 | length -= sublen; |
@@ -1071,7 +1086,7 @@ static int scrub_recheck_block(struct btrfs_fs_info *fs_info, | |||
1071 | for (page_num = 0; page_num < sblock->page_count; page_num++) { | 1086 | for (page_num = 0; page_num < sblock->page_count; page_num++) { |
1072 | struct bio *bio; | 1087 | struct bio *bio; |
1073 | int ret; | 1088 | int ret; |
1074 | struct scrub_page *page = sblock->pagev + page_num; | 1089 | struct scrub_page *page = sblock->pagev[page_num]; |
1075 | DECLARE_COMPLETION_ONSTACK(complete); | 1090 | DECLARE_COMPLETION_ONSTACK(complete); |
1076 | 1091 | ||
1077 | if (page->dev->bdev == NULL) { | 1092 | if (page->dev->bdev == NULL) { |
@@ -1080,7 +1095,7 @@ static int scrub_recheck_block(struct btrfs_fs_info *fs_info, | |||
1080 | continue; | 1095 | continue; |
1081 | } | 1096 | } |
1082 | 1097 | ||
1083 | BUG_ON(!page->page); | 1098 | WARN_ON(!page->page); |
1084 | bio = bio_alloc(GFP_NOFS, 1); | 1099 | bio = bio_alloc(GFP_NOFS, 1); |
1085 | if (!bio) | 1100 | if (!bio) |
1086 | return -EIO; | 1101 | return -EIO; |
@@ -1125,14 +1140,14 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info, | |||
1125 | struct btrfs_root *root = fs_info->extent_root; | 1140 | struct btrfs_root *root = fs_info->extent_root; |
1126 | void *mapped_buffer; | 1141 | void *mapped_buffer; |
1127 | 1142 | ||
1128 | BUG_ON(!sblock->pagev[0].page); | 1143 | WARN_ON(!sblock->pagev[0]->page); |
1129 | if (is_metadata) { | 1144 | if (is_metadata) { |
1130 | struct btrfs_header *h; | 1145 | struct btrfs_header *h; |
1131 | 1146 | ||
1132 | mapped_buffer = kmap_atomic(sblock->pagev[0].page); | 1147 | mapped_buffer = kmap_atomic(sblock->pagev[0]->page); |
1133 | h = (struct btrfs_header *)mapped_buffer; | 1148 | h = (struct btrfs_header *)mapped_buffer; |
1134 | 1149 | ||
1135 | if (sblock->pagev[0].logical != le64_to_cpu(h->bytenr) || | 1150 | if (sblock->pagev[0]->logical != le64_to_cpu(h->bytenr) || |
1136 | memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE) || | 1151 | memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE) || |
1137 | memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid, | 1152 | memcmp(h->chunk_tree_uuid, fs_info->chunk_tree_uuid, |
1138 | BTRFS_UUID_SIZE)) { | 1153 | BTRFS_UUID_SIZE)) { |
@@ -1146,7 +1161,7 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info, | |||
1146 | if (!have_csum) | 1161 | if (!have_csum) |
1147 | return; | 1162 | return; |
1148 | 1163 | ||
1149 | mapped_buffer = kmap_atomic(sblock->pagev[0].page); | 1164 | mapped_buffer = kmap_atomic(sblock->pagev[0]->page); |
1150 | } | 1165 | } |
1151 | 1166 | ||
1152 | for (page_num = 0;;) { | 1167 | for (page_num = 0;;) { |
@@ -1162,9 +1177,9 @@ static void scrub_recheck_block_checksum(struct btrfs_fs_info *fs_info, | |||
1162 | page_num++; | 1177 | page_num++; |
1163 | if (page_num >= sblock->page_count) | 1178 | if (page_num >= sblock->page_count) |
1164 | break; | 1179 | break; |
1165 | BUG_ON(!sblock->pagev[page_num].page); | 1180 | WARN_ON(!sblock->pagev[page_num]->page); |
1166 | 1181 | ||
1167 | mapped_buffer = kmap_atomic(sblock->pagev[page_num].page); | 1182 | mapped_buffer = kmap_atomic(sblock->pagev[page_num]->page); |
1168 | } | 1183 | } |
1169 | 1184 | ||
1170 | btrfs_csum_final(crc, calculated_csum); | 1185 | btrfs_csum_final(crc, calculated_csum); |
@@ -1202,11 +1217,11 @@ static int scrub_repair_page_from_good_copy(struct scrub_block *sblock_bad, | |||
1202 | struct scrub_block *sblock_good, | 1217 | struct scrub_block *sblock_good, |
1203 | int page_num, int force_write) | 1218 | int page_num, int force_write) |
1204 | { | 1219 | { |
1205 | struct scrub_page *page_bad = sblock_bad->pagev + page_num; | 1220 | struct scrub_page *page_bad = sblock_bad->pagev[page_num]; |
1206 | struct scrub_page *page_good = sblock_good->pagev + page_num; | 1221 | struct scrub_page *page_good = sblock_good->pagev[page_num]; |
1207 | 1222 | ||
1208 | BUG_ON(sblock_bad->pagev[page_num].page == NULL); | 1223 | BUG_ON(page_bad->page == NULL); |
1209 | BUG_ON(sblock_good->pagev[page_num].page == NULL); | 1224 | BUG_ON(page_good->page == NULL); |
1210 | if (force_write || sblock_bad->header_error || | 1225 | if (force_write || sblock_bad->header_error || |
1211 | sblock_bad->checksum_error || page_bad->io_error) { | 1226 | sblock_bad->checksum_error || page_bad->io_error) { |
1212 | struct bio *bio; | 1227 | struct bio *bio; |
@@ -1247,8 +1262,8 @@ static void scrub_checksum(struct scrub_block *sblock) | |||
1247 | u64 flags; | 1262 | u64 flags; |
1248 | int ret; | 1263 | int ret; |
1249 | 1264 | ||
1250 | BUG_ON(sblock->page_count < 1); | 1265 | WARN_ON(sblock->page_count < 1); |
1251 | flags = sblock->pagev[0].flags; | 1266 | flags = sblock->pagev[0]->flags; |
1252 | ret = 0; | 1267 | ret = 0; |
1253 | if (flags & BTRFS_EXTENT_FLAG_DATA) | 1268 | if (flags & BTRFS_EXTENT_FLAG_DATA) |
1254 | ret = scrub_checksum_data(sblock); | 1269 | ret = scrub_checksum_data(sblock); |
@@ -1276,11 +1291,11 @@ static int scrub_checksum_data(struct scrub_block *sblock) | |||
1276 | int index; | 1291 | int index; |
1277 | 1292 | ||
1278 | BUG_ON(sblock->page_count < 1); | 1293 | BUG_ON(sblock->page_count < 1); |
1279 | if (!sblock->pagev[0].have_csum) | 1294 | if (!sblock->pagev[0]->have_csum) |
1280 | return 0; | 1295 | return 0; |
1281 | 1296 | ||
1282 | on_disk_csum = sblock->pagev[0].csum; | 1297 | on_disk_csum = sblock->pagev[0]->csum; |
1283 | page = sblock->pagev[0].page; | 1298 | page = sblock->pagev[0]->page; |
1284 | buffer = kmap_atomic(page); | 1299 | buffer = kmap_atomic(page); |
1285 | 1300 | ||
1286 | len = sctx->sectorsize; | 1301 | len = sctx->sectorsize; |
@@ -1295,8 +1310,8 @@ static int scrub_checksum_data(struct scrub_block *sblock) | |||
1295 | break; | 1310 | break; |
1296 | index++; | 1311 | index++; |
1297 | BUG_ON(index >= sblock->page_count); | 1312 | BUG_ON(index >= sblock->page_count); |
1298 | BUG_ON(!sblock->pagev[index].page); | 1313 | BUG_ON(!sblock->pagev[index]->page); |
1299 | page = sblock->pagev[index].page; | 1314 | page = sblock->pagev[index]->page; |
1300 | buffer = kmap_atomic(page); | 1315 | buffer = kmap_atomic(page); |
1301 | } | 1316 | } |
1302 | 1317 | ||
@@ -1326,7 +1341,7 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock) | |||
1326 | int index; | 1341 | int index; |
1327 | 1342 | ||
1328 | BUG_ON(sblock->page_count < 1); | 1343 | BUG_ON(sblock->page_count < 1); |
1329 | page = sblock->pagev[0].page; | 1344 | page = sblock->pagev[0]->page; |
1330 | mapped_buffer = kmap_atomic(page); | 1345 | mapped_buffer = kmap_atomic(page); |
1331 | h = (struct btrfs_header *)mapped_buffer; | 1346 | h = (struct btrfs_header *)mapped_buffer; |
1332 | memcpy(on_disk_csum, h->csum, sctx->csum_size); | 1347 | memcpy(on_disk_csum, h->csum, sctx->csum_size); |
@@ -1337,10 +1352,10 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock) | |||
1337 | * b) the page is already kmapped | 1352 | * b) the page is already kmapped |
1338 | */ | 1353 | */ |
1339 | 1354 | ||
1340 | if (sblock->pagev[0].logical != le64_to_cpu(h->bytenr)) | 1355 | if (sblock->pagev[0]->logical != le64_to_cpu(h->bytenr)) |
1341 | ++fail; | 1356 | ++fail; |
1342 | 1357 | ||
1343 | if (sblock->pagev[0].generation != le64_to_cpu(h->generation)) | 1358 | if (sblock->pagev[0]->generation != le64_to_cpu(h->generation)) |
1344 | ++fail; | 1359 | ++fail; |
1345 | 1360 | ||
1346 | if (memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE)) | 1361 | if (memcmp(h->fsid, fs_info->fsid, BTRFS_UUID_SIZE)) |
@@ -1365,8 +1380,8 @@ static int scrub_checksum_tree_block(struct scrub_block *sblock) | |||
1365 | break; | 1380 | break; |
1366 | index++; | 1381 | index++; |
1367 | BUG_ON(index >= sblock->page_count); | 1382 | BUG_ON(index >= sblock->page_count); |
1368 | BUG_ON(!sblock->pagev[index].page); | 1383 | BUG_ON(!sblock->pagev[index]->page); |
1369 | page = sblock->pagev[index].page; | 1384 | page = sblock->pagev[index]->page; |
1370 | mapped_buffer = kmap_atomic(page); | 1385 | mapped_buffer = kmap_atomic(page); |
1371 | mapped_size = PAGE_SIZE; | 1386 | mapped_size = PAGE_SIZE; |
1372 | p = mapped_buffer; | 1387 | p = mapped_buffer; |
@@ -1398,15 +1413,15 @@ static int scrub_checksum_super(struct scrub_block *sblock) | |||
1398 | int index; | 1413 | int index; |
1399 | 1414 | ||
1400 | BUG_ON(sblock->page_count < 1); | 1415 | BUG_ON(sblock->page_count < 1); |
1401 | page = sblock->pagev[0].page; | 1416 | page = sblock->pagev[0]->page; |
1402 | mapped_buffer = kmap_atomic(page); | 1417 | mapped_buffer = kmap_atomic(page); |
1403 | s = (struct btrfs_super_block *)mapped_buffer; | 1418 | s = (struct btrfs_super_block *)mapped_buffer; |
1404 | memcpy(on_disk_csum, s->csum, sctx->csum_size); | 1419 | memcpy(on_disk_csum, s->csum, sctx->csum_size); |
1405 | 1420 | ||
1406 | if (sblock->pagev[0].logical != le64_to_cpu(s->bytenr)) | 1421 | if (sblock->pagev[0]->logical != le64_to_cpu(s->bytenr)) |
1407 | ++fail_cor; | 1422 | ++fail_cor; |
1408 | 1423 | ||
1409 | if (sblock->pagev[0].generation != le64_to_cpu(s->generation)) | 1424 | if (sblock->pagev[0]->generation != le64_to_cpu(s->generation)) |
1410 | ++fail_gen; | 1425 | ++fail_gen; |
1411 | 1426 | ||
1412 | if (memcmp(s->fsid, fs_info->fsid, BTRFS_UUID_SIZE)) | 1427 | if (memcmp(s->fsid, fs_info->fsid, BTRFS_UUID_SIZE)) |
@@ -1426,8 +1441,8 @@ static int scrub_checksum_super(struct scrub_block *sblock) | |||
1426 | break; | 1441 | break; |
1427 | index++; | 1442 | index++; |
1428 | BUG_ON(index >= sblock->page_count); | 1443 | BUG_ON(index >= sblock->page_count); |
1429 | BUG_ON(!sblock->pagev[index].page); | 1444 | BUG_ON(!sblock->pagev[index]->page); |
1430 | page = sblock->pagev[index].page; | 1445 | page = sblock->pagev[index]->page; |
1431 | mapped_buffer = kmap_atomic(page); | 1446 | mapped_buffer = kmap_atomic(page); |
1432 | mapped_size = PAGE_SIZE; | 1447 | mapped_size = PAGE_SIZE; |
1433 | p = mapped_buffer; | 1448 | p = mapped_buffer; |
@@ -1447,10 +1462,10 @@ static int scrub_checksum_super(struct scrub_block *sblock) | |||
1447 | ++sctx->stat.super_errors; | 1462 | ++sctx->stat.super_errors; |
1448 | spin_unlock(&sctx->stat_lock); | 1463 | spin_unlock(&sctx->stat_lock); |
1449 | if (fail_cor) | 1464 | if (fail_cor) |
1450 | btrfs_dev_stat_inc_and_print(sblock->pagev[0].dev, | 1465 | btrfs_dev_stat_inc_and_print(sblock->pagev[0]->dev, |
1451 | BTRFS_DEV_STAT_CORRUPTION_ERRS); | 1466 | BTRFS_DEV_STAT_CORRUPTION_ERRS); |
1452 | else | 1467 | else |
1453 | btrfs_dev_stat_inc_and_print(sblock->pagev[0].dev, | 1468 | btrfs_dev_stat_inc_and_print(sblock->pagev[0]->dev, |
1454 | BTRFS_DEV_STAT_GENERATION_ERRS); | 1469 | BTRFS_DEV_STAT_GENERATION_ERRS); |
1455 | } | 1470 | } |
1456 | 1471 | ||
@@ -1468,12 +1483,25 @@ static void scrub_block_put(struct scrub_block *sblock) | |||
1468 | int i; | 1483 | int i; |
1469 | 1484 | ||
1470 | for (i = 0; i < sblock->page_count; i++) | 1485 | for (i = 0; i < sblock->page_count; i++) |
1471 | if (sblock->pagev[i].page) | 1486 | scrub_page_put(sblock->pagev[i]); |
1472 | __free_page(sblock->pagev[i].page); | ||
1473 | kfree(sblock); | 1487 | kfree(sblock); |
1474 | } | 1488 | } |
1475 | } | 1489 | } |
1476 | 1490 | ||
1491 | static void scrub_page_get(struct scrub_page *spage) | ||
1492 | { | ||
1493 | atomic_inc(&spage->ref_count); | ||
1494 | } | ||
1495 | |||
1496 | static void scrub_page_put(struct scrub_page *spage) | ||
1497 | { | ||
1498 | if (atomic_dec_and_test(&spage->ref_count)) { | ||
1499 | if (spage->page) | ||
1500 | __free_page(spage->page); | ||
1501 | kfree(spage); | ||
1502 | } | ||
1503 | } | ||
1504 | |||
1477 | static void scrub_submit(struct scrub_ctx *sctx) | 1505 | static void scrub_submit(struct scrub_ctx *sctx) |
1478 | { | 1506 | { |
1479 | struct scrub_bio *sbio; | 1507 | struct scrub_bio *sbio; |
@@ -1577,28 +1605,28 @@ static int scrub_pages(struct scrub_ctx *sctx, u64 logical, u64 len, | |||
1577 | return -ENOMEM; | 1605 | return -ENOMEM; |
1578 | } | 1606 | } |
1579 | 1607 | ||
1580 | /* one ref inside this function, plus one for each page later on */ | 1608 | /* one ref inside this function, plus one for each page added to |
1609 | * a bio later on */ | ||
1581 | atomic_set(&sblock->ref_count, 1); | 1610 | atomic_set(&sblock->ref_count, 1); |
1582 | sblock->sctx = sctx; | 1611 | sblock->sctx = sctx; |
1583 | sblock->no_io_error_seen = 1; | 1612 | sblock->no_io_error_seen = 1; |
1584 | 1613 | ||
1585 | for (index = 0; len > 0; index++) { | 1614 | for (index = 0; len > 0; index++) { |
1586 | struct scrub_page *spage = sblock->pagev + index; | 1615 | struct scrub_page *spage; |
1587 | u64 l = min_t(u64, len, PAGE_SIZE); | 1616 | u64 l = min_t(u64, len, PAGE_SIZE); |
1588 | 1617 | ||
1589 | BUG_ON(index >= SCRUB_MAX_PAGES_PER_BLOCK); | 1618 | spage = kzalloc(sizeof(*spage), GFP_NOFS); |
1590 | spage->page = alloc_page(GFP_NOFS); | 1619 | if (!spage) { |
1591 | if (!spage->page) { | 1620 | leave_nomem: |
1592 | spin_lock(&sctx->stat_lock); | 1621 | spin_lock(&sctx->stat_lock); |
1593 | sctx->stat.malloc_errors++; | 1622 | sctx->stat.malloc_errors++; |
1594 | spin_unlock(&sctx->stat_lock); | 1623 | spin_unlock(&sctx->stat_lock); |
1595 | while (index > 0) { | 1624 | scrub_block_put(sblock); |
1596 | index--; | ||
1597 | __free_page(sblock->pagev[index].page); | ||
1598 | } | ||
1599 | kfree(sblock); | ||
1600 | return -ENOMEM; | 1625 | return -ENOMEM; |
1601 | } | 1626 | } |
1627 | BUG_ON(index >= SCRUB_MAX_PAGES_PER_BLOCK); | ||
1628 | scrub_page_get(spage); | ||
1629 | sblock->pagev[index] = spage; | ||
1602 | spage->sblock = sblock; | 1630 | spage->sblock = sblock; |
1603 | spage->dev = dev; | 1631 | spage->dev = dev; |
1604 | spage->flags = flags; | 1632 | spage->flags = flags; |
@@ -1613,14 +1641,17 @@ static int scrub_pages(struct scrub_ctx *sctx, u64 logical, u64 len, | |||
1613 | spage->have_csum = 0; | 1641 | spage->have_csum = 0; |
1614 | } | 1642 | } |
1615 | sblock->page_count++; | 1643 | sblock->page_count++; |
1644 | spage->page = alloc_page(GFP_NOFS); | ||
1645 | if (!spage->page) | ||
1646 | goto leave_nomem; | ||
1616 | len -= l; | 1647 | len -= l; |
1617 | logical += l; | 1648 | logical += l; |
1618 | physical += l; | 1649 | physical += l; |
1619 | } | 1650 | } |
1620 | 1651 | ||
1621 | BUG_ON(sblock->page_count == 0); | 1652 | WARN_ON(sblock->page_count == 0); |
1622 | for (index = 0; index < sblock->page_count; index++) { | 1653 | for (index = 0; index < sblock->page_count; index++) { |
1623 | struct scrub_page *spage = sblock->pagev + index; | 1654 | struct scrub_page *spage = sblock->pagev[index]; |
1624 | int ret; | 1655 | int ret; |
1625 | 1656 | ||
1626 | ret = scrub_add_page_to_bio(sctx, spage); | 1657 | ret = scrub_add_page_to_bio(sctx, spage); |
@@ -2289,6 +2320,22 @@ int btrfs_scrub_dev(struct btrfs_root *root, u64 devid, u64 start, u64 end, | |||
2289 | return -EINVAL; | 2320 | return -EINVAL; |
2290 | } | 2321 | } |
2291 | 2322 | ||
2323 | if (fs_info->chunk_root->nodesize > | ||
2324 | PAGE_SIZE * SCRUB_MAX_PAGES_PER_BLOCK || | ||
2325 | fs_info->chunk_root->sectorsize > | ||
2326 | PAGE_SIZE * SCRUB_MAX_PAGES_PER_BLOCK) { | ||
2327 | /* | ||
2328 | * would exhaust the array bounds of pagev member in | ||
2329 | * struct scrub_block | ||
2330 | */ | ||
2331 | pr_err("btrfs_scrub: size assumption nodesize and sectorsize <= SCRUB_MAX_PAGES_PER_BLOCK (%d <= %d && %d <= %d) fails\n", | ||
2332 | fs_info->chunk_root->nodesize, | ||
2333 | SCRUB_MAX_PAGES_PER_BLOCK, | ||
2334 | fs_info->chunk_root->sectorsize, | ||
2335 | SCRUB_MAX_PAGES_PER_BLOCK); | ||
2336 | return -EINVAL; | ||
2337 | } | ||
2338 | |||
2292 | ret = scrub_workers_get(root); | 2339 | ret = scrub_workers_get(root); |
2293 | if (ret) | 2340 | if (ret) |
2294 | return ret; | 2341 | return ret; |