summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/md/md.c65
-rw-r--r--include/linux/raid/md_k.h1
2 files changed, 58 insertions, 8 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6f97817d28b..34b6902cda4 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1558,15 +1558,30 @@ static void md_print_devices(void)
}
-static void sync_sbs(mddev_t * mddev)
+static void sync_sbs(mddev_t * mddev, int nospares)
{
+ /* Update each superblock (in-memory image), but
+ * if we are allowed to, skip spares which already
+ * have the right event counter, or have one earlier
+ * (which would mean they aren't being marked as dirty
+ * with the rest of the array)
+ */
mdk_rdev_t *rdev;
struct list_head *tmp;
ITERATE_RDEV(mddev,rdev,tmp) {
- super_types[mddev->major_version].
- sync_super(mddev, rdev);
- rdev->sb_loaded = 1;
+ if (rdev->sb_events == mddev->events ||
+ (nospares &&
+ rdev->raid_disk < 0 &&
+ (rdev->sb_events&1)==0 &&
+ rdev->sb_events+1 == mddev->events)) {
+ /* Don't update this superblock */
+ rdev->sb_loaded = 2;
+ } else {
+ super_types[mddev->major_version].
+ sync_super(mddev, rdev);
+ rdev->sb_loaded = 1;
+ }
}
}
@@ -1576,12 +1591,42 @@ void md_update_sb(mddev_t * mddev)
struct list_head *tmp;
mdk_rdev_t *rdev;
int sync_req;
+ int nospares = 0;
repeat:
spin_lock_irq(&mddev->write_lock);
sync_req = mddev->in_sync;
mddev->utime = get_seconds();
- mddev->events ++;
+ if (mddev->sb_dirty == 3)
+ /* just a clean<-> dirty transition, possibly leave spares alone,
+ * though if events isn't the right even/odd, we will have to do
+ * spares after all
+ */
+ nospares = 1;
+
+ /* If this is just a dirty<->clean transition, and the array is clean
+ * and 'events' is odd, we can roll back to the previous clean state */
+ if (mddev->sb_dirty == 3
+ && (mddev->in_sync && mddev->recovery_cp == MaxSector)
+ && (mddev->events & 1))
+ mddev->events--;
+ else {
+ /* otherwise we have to go forward and ... */
+ mddev->events ++;
+ if (!mddev->in_sync || mddev->recovery_cp != MaxSector) { /* not clean */
+ /* .. if the array isn't clean, insist on an odd 'events' */
+ if ((mddev->events&1)==0) {
+ mddev->events++;
+ nospares = 0;
+ }
+ } else {
+ /* otherwise insist on an even 'events' (for clean states) */
+ if ((mddev->events&1)) {
+ mddev->events++;
+ nospares = 0;
+ }
+ }
+ }
if (!mddev->events) {
/*
@@ -1593,7 +1638,7 @@ repeat:
mddev->events --;
}
mddev->sb_dirty = 2;
- sync_sbs(mddev);
+ sync_sbs(mddev, nospares);
/*
* do not write anything to disk if using
@@ -1615,6 +1660,8 @@ repeat:
ITERATE_RDEV(mddev,rdev,tmp) {
char b[BDEVNAME_SIZE];
dprintk(KERN_INFO "md: ");
+ if (rdev->sb_loaded != 1)
+ continue; /* no noise on spare devices */
if (test_bit(Faulty, &rdev->flags))
dprintk("(skipping faulty ");
@@ -1626,6 +1673,7 @@ repeat:
dprintk(KERN_INFO "(write) %s's sb offset: %llu\n",
bdevname(rdev->bdev,b),
(unsigned long long)rdev->sb_offset);
+ rdev->sb_events = mddev->events;
} else
dprintk(")\n");
@@ -1895,6 +1943,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
rdev->desc_nr = -1;
rdev->flags = 0;
rdev->data_offset = 0;
+ rdev->sb_events = 0;
atomic_set(&rdev->nr_pending, 0);
atomic_set(&rdev->read_errors, 0);
atomic_set(&rdev->corrected_errors, 0);
@@ -4708,7 +4757,7 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
spin_lock_irq(&mddev->write_lock);
if (mddev->in_sync) {
mddev->in_sync = 0;
- mddev->sb_dirty = 1;
+ mddev->sb_dirty = 3;
md_wakeup_thread(mddev->thread);
}
spin_unlock_irq(&mddev->write_lock);
@@ -5055,7 +5104,7 @@ void md_check_recovery(mddev_t *mddev)
if (mddev->safemode && !atomic_read(&mddev->writes_pending) &&
!mddev->in_sync && mddev->recovery_cp == MaxSector) {
mddev->in_sync = 1;
- mddev->sb_dirty = 1;
+ mddev->sb_dirty = 3;
}
if (mddev->safemode == 1)
mddev->safemode = 0;
diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h
index 682574f3bd3..c1e0ac55bab 100644
--- a/include/linux/raid/md_k.h
+++ b/include/linux/raid/md_k.h
@@ -58,6 +58,7 @@ struct mdk_rdev_s
struct page *sb_page;
int sb_loaded;
+ __u64 sb_events;
sector_t data_offset; /* start of data in array */
sector_t sb_offset;
int sb_size; /* bytes in the superblock */