md: simplify updating of event count to sometimes avoid updating spares.
authorNeilBrown <neilb@suse.de>
Mon, 17 May 2010 23:28:43 +0000 (09:28 +1000)
committerNeilBrown <neilb@suse.de>
Tue, 18 May 2010 05:28:01 +0000 (15:28 +1000)
When updating the event count for a simple clean <-> dirty transition,
we try to avoid updating the spares so they can safely spin-down.
As the event_counts across an array must be +/- 1, this means
decrementing the event_count on a dirty->clean transition.
This is not always safe and we have to avoid the unsafe time.
We current do this with a misguided idea about it being safe or
not depending on whether the event_count is odd or even.  This
approach only works reliably in a few common instances, but easily
falls down.

So instead, simply keep internal state concerning whether it is safe
or not, and always assume it is not safe when an array is first
assembled.

Signed-off-by: NeilBrown <neilb@suse.de>
drivers/md/md.c
drivers/md/md.h

index fec4abcb9bb4d443e086af8198e9b26de865ee81..9ef21d9b8e27394e40c35454ecfea6c75997c443 100644 (file)
@@ -2088,7 +2088,6 @@ static void sync_sbs(mddev_t * mddev, int nospares)
                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;
@@ -2141,28 +2140,14 @@ repeat:
         * and 'events' is odd, we can roll back to the previous clean state */
        if (nospares
            && (mddev->in_sync && mddev->recovery_cp == MaxSector)
-           && (mddev->events & 1)
-           && mddev->events != 1)
+           && mddev->can_decrease_events
+           && mddev->events != 1) {
                mddev->events--;
-       else {
+               mddev->can_decrease_events = 0;
+       } 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, an 'even' event must also go
-                        * to spares. */
-                       if ((mddev->events&1)==0) {
-                               nospares = 0;
-                               sync_req = 2; /* force a second update to get the
-                                              * even/odd in sync */
-                       }
-               } else {
-                       /* otherwise an 'odd' event must go to spares */
-                       if ((mddev->events&1)) {
-                               nospares = 0;
-                               sync_req = 2; /* force a second update to get the
-                                              * even/odd in sync */
-                       }
-               }
+               mddev->can_decrease_events = nospares;
        }
 
        if (!mddev->events) {
@@ -4606,6 +4591,7 @@ static void md_clean(mddev_t *mddev)
        mddev->layout = 0;
        mddev->max_disks = 0;
        mddev->events = 0;
+       mddev->can_decrease_events = 0;
        mddev->delta_disks = 0;
        mddev->new_level = LEVEL_NONE;
        mddev->new_layout = 0;
index a536f54580970cb1114ed4b4b8717c7381a80d52..7ab5ea155452c5b745041e05c155df0ded2dd2c4 100644 (file)
@@ -150,6 +150,12 @@ struct mddev_s
        int                             external_size; /* size managed
                                                        * externally */
        __u64                           events;
+       /* If the last 'event' was simply a clean->dirty transition, and
+        * we didn't write it to the spares, then it is safe and simple
+        * to just decrement the event count on a dirty->clean transition.
+        * So we record that possibility here.
+        */
+       int                             can_decrease_events;
 
        char                            uuid[16];