diff options
author | NeilBrown <neilb@suse.de> | 2011-07-27 21:31:47 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2011-07-27 21:31:47 -0400 |
commit | 2699b67223aca6b1450fc2f72e40fada952afc85 (patch) | |
tree | c84d0cbb763ad03cd770a218a37f0f1ec31118af /drivers | |
parent | 34b343cff4354ab9864be83be88405fd53d928a0 (diff) |
md: load/store badblock list from v1.x metadata
Space must have been allocated when array was created.
A feature flag is set when the badblock list is non-empty, to
ensure old kernels don't load and trust the whole device.
We only update the on-disk badblocklist when it has changed.
If the badblocklist (or other metadata) is stored on a bad block, we
don't cope very well.
If metadata has no room for bad block, flag bad-blocks as disabled,
and do the same for 0.90 metadata.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/md/md.c | 108 | ||||
-rw-r--r-- | drivers/md/md.h | 5 |
2 files changed, 106 insertions, 7 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 7f5ffc51c28e..e2ec8bd0fb94 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -757,6 +757,10 @@ static void free_disk_sb(mdk_rdev_t * rdev) | |||
757 | rdev->sb_start = 0; | 757 | rdev->sb_start = 0; |
758 | rdev->sectors = 0; | 758 | rdev->sectors = 0; |
759 | } | 759 | } |
760 | if (rdev->bb_page) { | ||
761 | put_page(rdev->bb_page); | ||
762 | rdev->bb_page = NULL; | ||
763 | } | ||
760 | } | 764 | } |
761 | 765 | ||
762 | 766 | ||
@@ -1395,6 +1399,8 @@ static __le32 calc_sb_1_csum(struct mdp_superblock_1 * sb) | |||
1395 | return cpu_to_le32(csum); | 1399 | return cpu_to_le32(csum); |
1396 | } | 1400 | } |
1397 | 1401 | ||
1402 | static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors, | ||
1403 | int acknowledged); | ||
1398 | static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) | 1404 | static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) |
1399 | { | 1405 | { |
1400 | struct mdp_superblock_1 *sb; | 1406 | struct mdp_superblock_1 *sb; |
@@ -1473,6 +1479,47 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) | |||
1473 | else | 1479 | else |
1474 | rdev->desc_nr = le32_to_cpu(sb->dev_number); | 1480 | rdev->desc_nr = le32_to_cpu(sb->dev_number); |
1475 | 1481 | ||
1482 | if (!rdev->bb_page) { | ||
1483 | rdev->bb_page = alloc_page(GFP_KERNEL); | ||
1484 | if (!rdev->bb_page) | ||
1485 | return -ENOMEM; | ||
1486 | } | ||
1487 | if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BAD_BLOCKS) && | ||
1488 | rdev->badblocks.count == 0) { | ||
1489 | /* need to load the bad block list. | ||
1490 | * Currently we limit it to one page. | ||
1491 | */ | ||
1492 | s32 offset; | ||
1493 | sector_t bb_sector; | ||
1494 | u64 *bbp; | ||
1495 | int i; | ||
1496 | int sectors = le16_to_cpu(sb->bblog_size); | ||
1497 | if (sectors > (PAGE_SIZE / 512)) | ||
1498 | return -EINVAL; | ||
1499 | offset = le32_to_cpu(sb->bblog_offset); | ||
1500 | if (offset == 0) | ||
1501 | return -EINVAL; | ||
1502 | bb_sector = (long long)offset; | ||
1503 | if (!sync_page_io(rdev, bb_sector, sectors << 9, | ||
1504 | rdev->bb_page, READ, true)) | ||
1505 | return -EIO; | ||
1506 | bbp = (u64 *)page_address(rdev->bb_page); | ||
1507 | rdev->badblocks.shift = sb->bblog_shift; | ||
1508 | for (i = 0 ; i < (sectors << (9-3)) ; i++, bbp++) { | ||
1509 | u64 bb = le64_to_cpu(*bbp); | ||
1510 | int count = bb & (0x3ff); | ||
1511 | u64 sector = bb >> 10; | ||
1512 | sector <<= sb->bblog_shift; | ||
1513 | count <<= sb->bblog_shift; | ||
1514 | if (bb + 1 == 0) | ||
1515 | break; | ||
1516 | if (md_set_badblocks(&rdev->badblocks, | ||
1517 | sector, count, 1) == 0) | ||
1518 | return -EINVAL; | ||
1519 | } | ||
1520 | } else if (sb->bblog_offset == 0) | ||
1521 | rdev->badblocks.shift = -1; | ||
1522 | |||
1476 | if (!refdev) { | 1523 | if (!refdev) { |
1477 | ret = 1; | 1524 | ret = 1; |
1478 | } else { | 1525 | } else { |
@@ -1624,7 +1671,6 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) | |||
1624 | sb->pad0 = 0; | 1671 | sb->pad0 = 0; |
1625 | sb->recovery_offset = cpu_to_le64(0); | 1672 | sb->recovery_offset = cpu_to_le64(0); |
1626 | memset(sb->pad1, 0, sizeof(sb->pad1)); | 1673 | memset(sb->pad1, 0, sizeof(sb->pad1)); |
1627 | memset(sb->pad2, 0, sizeof(sb->pad2)); | ||
1628 | memset(sb->pad3, 0, sizeof(sb->pad3)); | 1674 | memset(sb->pad3, 0, sizeof(sb->pad3)); |
1629 | 1675 | ||
1630 | sb->utime = cpu_to_le64((__u64)mddev->utime); | 1676 | sb->utime = cpu_to_le64((__u64)mddev->utime); |
@@ -1664,6 +1710,40 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) | |||
1664 | sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors); | 1710 | sb->new_chunk = cpu_to_le32(mddev->new_chunk_sectors); |
1665 | } | 1711 | } |
1666 | 1712 | ||
1713 | if (rdev->badblocks.count == 0) | ||
1714 | /* Nothing to do for bad blocks*/ ; | ||
1715 | else if (sb->bblog_offset == 0) | ||
1716 | /* Cannot record bad blocks on this device */ | ||
1717 | md_error(mddev, rdev); | ||
1718 | else { | ||
1719 | struct badblocks *bb = &rdev->badblocks; | ||
1720 | u64 *bbp = (u64 *)page_address(rdev->bb_page); | ||
1721 | u64 *p = bb->page; | ||
1722 | sb->feature_map |= cpu_to_le32(MD_FEATURE_BAD_BLOCKS); | ||
1723 | if (bb->changed) { | ||
1724 | unsigned seq; | ||
1725 | |||
1726 | retry: | ||
1727 | seq = read_seqbegin(&bb->lock); | ||
1728 | |||
1729 | memset(bbp, 0xff, PAGE_SIZE); | ||
1730 | |||
1731 | for (i = 0 ; i < bb->count ; i++) { | ||
1732 | u64 internal_bb = *p++; | ||
1733 | u64 store_bb = ((BB_OFFSET(internal_bb) << 10) | ||
1734 | | BB_LEN(internal_bb)); | ||
1735 | *bbp++ = cpu_to_le64(store_bb); | ||
1736 | } | ||
1737 | if (read_seqretry(&bb->lock, seq)) | ||
1738 | goto retry; | ||
1739 | |||
1740 | bb->sector = (rdev->sb_start + | ||
1741 | (int)le32_to_cpu(sb->bblog_offset)); | ||
1742 | bb->size = le16_to_cpu(sb->bblog_size); | ||
1743 | bb->changed = 0; | ||
1744 | } | ||
1745 | } | ||
1746 | |||
1667 | max_dev = 0; | 1747 | max_dev = 0; |
1668 | list_for_each_entry(rdev2, &mddev->disks, same_set) | 1748 | list_for_each_entry(rdev2, &mddev->disks, same_set) |
1669 | if (rdev2->desc_nr+1 > max_dev) | 1749 | if (rdev2->desc_nr+1 > max_dev) |
@@ -2196,6 +2276,7 @@ static void md_update_sb(mddev_t * mddev, int force_change) | |||
2196 | mdk_rdev_t *rdev; | 2276 | mdk_rdev_t *rdev; |
2197 | int sync_req; | 2277 | int sync_req; |
2198 | int nospares = 0; | 2278 | int nospares = 0; |
2279 | int any_badblocks_changed = 0; | ||
2199 | 2280 | ||
2200 | repeat: | 2281 | repeat: |
2201 | /* First make sure individual recovery_offsets are correct */ | 2282 | /* First make sure individual recovery_offsets are correct */ |
@@ -2267,6 +2348,11 @@ repeat: | |||
2267 | MD_BUG(); | 2348 | MD_BUG(); |
2268 | mddev->events --; | 2349 | mddev->events --; |
2269 | } | 2350 | } |
2351 | |||
2352 | list_for_each_entry(rdev, &mddev->disks, same_set) | ||
2353 | if (rdev->badblocks.changed) | ||
2354 | any_badblocks_changed++; | ||
2355 | |||
2270 | sync_sbs(mddev, nospares); | 2356 | sync_sbs(mddev, nospares); |
2271 | spin_unlock_irq(&mddev->write_lock); | 2357 | spin_unlock_irq(&mddev->write_lock); |
2272 | 2358 | ||
@@ -2292,6 +2378,13 @@ repeat: | |||
2292 | bdevname(rdev->bdev,b), | 2378 | bdevname(rdev->bdev,b), |
2293 | (unsigned long long)rdev->sb_start); | 2379 | (unsigned long long)rdev->sb_start); |
2294 | rdev->sb_events = mddev->events; | 2380 | rdev->sb_events = mddev->events; |
2381 | if (rdev->badblocks.size) { | ||
2382 | md_super_write(mddev, rdev, | ||
2383 | rdev->badblocks.sector, | ||
2384 | rdev->badblocks.size << 9, | ||
2385 | rdev->bb_page); | ||
2386 | rdev->badblocks.size = 0; | ||
2387 | } | ||
2295 | 2388 | ||
2296 | } else | 2389 | } else |
2297 | dprintk(")\n"); | 2390 | dprintk(")\n"); |
@@ -2315,6 +2408,9 @@ repeat: | |||
2315 | if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) | 2408 | if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) |
2316 | sysfs_notify(&mddev->kobj, NULL, "sync_completed"); | 2409 | sysfs_notify(&mddev->kobj, NULL, "sync_completed"); |
2317 | 2410 | ||
2411 | if (any_badblocks_changed) | ||
2412 | list_for_each_entry(rdev, &mddev->disks, same_set) | ||
2413 | md_ack_all_badblocks(&rdev->badblocks); | ||
2318 | } | 2414 | } |
2319 | 2415 | ||
2320 | /* words written to sysfs files may, or may not, be \n terminated. | 2416 | /* words written to sysfs files may, or may not, be \n terminated. |
@@ -2822,6 +2918,8 @@ int md_rdev_init(mdk_rdev_t *rdev) | |||
2822 | rdev->sb_events = 0; | 2918 | rdev->sb_events = 0; |
2823 | rdev->last_read_error.tv_sec = 0; | 2919 | rdev->last_read_error.tv_sec = 0; |
2824 | rdev->last_read_error.tv_nsec = 0; | 2920 | rdev->last_read_error.tv_nsec = 0; |
2921 | rdev->sb_loaded = 0; | ||
2922 | rdev->bb_page = NULL; | ||
2825 | atomic_set(&rdev->nr_pending, 0); | 2923 | atomic_set(&rdev->nr_pending, 0); |
2826 | atomic_set(&rdev->read_errors, 0); | 2924 | atomic_set(&rdev->read_errors, 0); |
2827 | atomic_set(&rdev->corrected_errors, 0); | 2925 | atomic_set(&rdev->corrected_errors, 0); |
@@ -2910,11 +3008,9 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi | |||
2910 | return rdev; | 3008 | return rdev; |
2911 | 3009 | ||
2912 | abort_free: | 3010 | abort_free: |
2913 | if (rdev->sb_page) { | 3011 | if (rdev->bdev) |
2914 | if (rdev->bdev) | 3012 | unlock_rdev(rdev); |
2915 | unlock_rdev(rdev); | 3013 | free_disk_sb(rdev); |
2916 | free_disk_sb(rdev); | ||
2917 | } | ||
2918 | kfree(rdev->badblocks.page); | 3014 | kfree(rdev->badblocks.page); |
2919 | kfree(rdev); | 3015 | kfree(rdev); |
2920 | return ERR_PTR(err); | 3016 | return ERR_PTR(err); |
diff --git a/drivers/md/md.h b/drivers/md/md.h index 85af8433f8b8..aea9e9ff8a33 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h | |||
@@ -55,7 +55,7 @@ struct mdk_rdev_s | |||
55 | struct block_device *meta_bdev; | 55 | struct block_device *meta_bdev; |
56 | struct block_device *bdev; /* block device handle */ | 56 | struct block_device *bdev; /* block device handle */ |
57 | 57 | ||
58 | struct page *sb_page; | 58 | struct page *sb_page, *bb_page; |
59 | int sb_loaded; | 59 | int sb_loaded; |
60 | __u64 sb_events; | 60 | __u64 sb_events; |
61 | sector_t data_offset; /* start of data in array */ | 61 | sector_t data_offset; /* start of data in array */ |
@@ -127,6 +127,9 @@ struct mdk_rdev_s | |||
127 | u64 *page; /* badblock list */ | 127 | u64 *page; /* badblock list */ |
128 | int changed; | 128 | int changed; |
129 | seqlock_t lock; | 129 | seqlock_t lock; |
130 | |||
131 | sector_t sector; | ||
132 | sector_t size; /* in sectors */ | ||
130 | } badblocks; | 133 | } badblocks; |
131 | }; | 134 | }; |
132 | 135 | ||