md: Two more bugfixes.
PATCH 1/1 md: Two more bugfixes.
Following are two bugfixes for md in current kernels. The first is suitable for -stable is it can allow drive errors through to the filesystem wrongly.
Both are suitable for 2.6.22.
Thanks,
NeilBrown
[PATCH 001 of 2] md: Avoid a possibility that a read error can wrongly propagate through md/raid1 to a filesystem. [PATCH 002 of 2] md: Improve the is_mddev_idle test
PATCH 1/1 md: Avoid a possibility that a read error can wrongly propagate through md/raid1 to a filesystem.
When a raid1 has only one working drive, we want read error to propagate up to the filesystem as there is no point failing the last drive in an array.
Currently the code perform this check is racy. If a write and a read a both submitted to a device on a 2-drive raid1, and the write fails followed by the read failing, the read will see that there is only one working drive and will pass the failure up, even though the one working drive is actually the *other* one.
So, tighten up the locking.
### Diffstat output
./drivers/md/raid1.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
drivers/md/raid1.c +19 -14
@@ -271,21 +271,25 @@ static int raid1_end_read_request(struct*/ update_head_pos (mirror, r1_bio); -if (uptodate || (conf->raid_disks - conf->mddev->degraded) <=1 ) { -/* -* Set R1BIO_Uptodate in our master bio, so that -* we will return a good error code for to the higher -* levels even if IO on some other mirrored buffer fails. -* -* The 'master' represents the composite IO operation to -* user-side. So if something waits for IO, then it will -* wait for the 'master' bio. +if (uptodate) +set_bit (R1BIO_Uptodate, &r1_bio->state); +else { +/* If all other devices have failed, we want to return +* the error upwards rather than fail the last device. +* Here we redefine "uptodate" to mean "Don't want to retry" */ -if (uptodate) -set_bit (R1BIO_Uptodate, &r1_bio->state); +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); +if (r1_bio->mddev->degraded == conf->raid_disks || + (r1_bio->mddev->degraded == conf->raid_disks-1 && + !test_bit (Faulty, &conf->mirrors[mirror].rdev->flags))) + uptodate =1 ; +spin_unlock_irqrestore (&conf->device_lock, flags); + } +if (uptodate)raid_end_bio_io (r1_bio); - }else { +else {/* * oops, read error: */
@@ -992,13 +996,14 @@ static void error(mddev_t *mddev, mdk_rdunsigned long flags;spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded++; +set_bit (Faulty, &rdev->flags);spin_unlock_irqrestore (&conf->device_lock, flags);/* * if recovery is running, make sure it aborts. */ set_bit (MD_RECOVERY_ERR , &mddev->recovery); - } -set_bit (Faulty, &rdev->flags); + }else +set_bit (Faulty, &rdev->flags);set_bit (MD_CHANGE_DEVS , &mddev->flags); printk(KERN_ALERT "raid1: Disk failure on %s, disabling device. \n" " Operation continuing on %d devices\n",
PATCH 1/1 md: Improve the is_mddev_idle test
In reply to NeilBrownDuring a 'resync' or similar activity, md checks if the devices in the array are otherwise active and winds back resync activity when they are. This test in done in is_mddev_idle, and it is somewhat fragile - it sometimes thinks there is non-sync io when there isn't.
The test compares the total sectors of io (disk_stat_read) with the sectors of resync io (disk->sync_io).
This has problems because total sectors gets updated when a request completes, while resync io gets updated when the request is submitted. The time difference can cause large differenced between the two which do not actually imply non-resync activity. The test currently allows for some fuzz (+/- 4096) but there are some cases when it is not enough.
The test currently looks for any (non-fuzz) difference, either positive or negative. This clearly is not needed. Any non-sync activity will cause the total sectors to grow faster than the sync_io count (never slower) so we only need to look for a positive differences.
If we do this then the amount of in-flight sync io will never cause the appearance of non-sync IO. Once enough non-sync IO to worry about starts happening, resync will be slowed down and the measurements will thus be more precise (as there is less in-flight) and control of resync will still be suitably responsive.
### Diffstat output
./drivers/md/md.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/md.c +1 -1
@@ -5095,7 +5095,7 @@ static int is_mddev_idle(mddev_t *mddev)* * Note: the following is an unsigned comparison. */ -if ((curr_events - rdev->last_events +4096 ) >8192 ) { +if ((long )curr_events - (long )rdev->last_events >4096 ) { rdev->last_events = curr_events; idle =0 ; }
Re: [PATCH 002 of 2] md: Improve the is_mddev_idle test
Quoted 10 lines · On Thu, 10 May 2007 16:22:31 +1000 NeilBrown <[email protected]> wrote:
The test currently looks for any (non-fuzz) difference, either positive or negative. This clearly is not needed. Any non-sync activity will cause the total sectors to grow faster than the
sync_iocount (never slower) so we only need to look for a positive differences....
drivers/md/md.c static int is_mddev_idle(mddev_t *mddev) · line 5100
Quoted 7 lines
drivers/md/md.c +1
-1@@ -5095,7 +5095,7 @@ static int is_mddev_idle(mddev_t *mddev)* * Note: the following is an unsigned comparison. */ -if ((curr_events - rdev->last_events +4096 ) >8192 ) { +if ((long )curr_events - (long )rdev->last_events >4096 ) { rdev->last_events = curr_events; idle =0 ;
In which case would unsigned counters be more appropriate?
Re: [PATCH 002 of 2] md: Improve the is_mddev_idle test
Quoted 9 lines · On Thursday May 10, [email protected] wrote:
Quoted 7 lines · On Thu, 10 May 2007 16:22:31 +1000 NeilBrown <[email protected]> wrote:
The test currently looks for any (non-fuzz) difference, either positive or negative. This clearly is not needed. Any non-sync activity will cause the total sectors to grow faster than the
sync_iocount (never slower) so we only need to look for a positive differences....
Quoted 12 lines
drivers/md/md.c static int is_mddev_idle(mddev_t *mddev) · line 5100
Quoted 10 lines
drivers/md/md.c +1
-1@@ -5095,7 +5095,7 @@ static int is_mddev_idle(mddev_t *mddev)* * Note: the following is an unsigned comparison. */ -if ((curr_events - rdev->last_events +4096 ) >8192 ) { +if ((long )curr_events - (long )rdev->last_events >4096 ) { rdev->last_events = curr_events; idle =0 ;In which case would unsigned counters be more appropriate?
I guess.....
It is really the comparison that I want to be signed, I don't much care about the counted - they are expected to wrap (though they might not).
So maybe I really want
if ((signed long)(curr_events - rdev->last_events) > 4096) {to make it clear...
But people expect number to be signed by default, so that probably isn't necessary.
Yeah, I'll make them signed one day.
Thanks,
NeilBrown
Re: [PATCH 002 of 2] md: Improve the is_mddev_idle test
In reply to NeilBrowndrivers/md/md.c static int is_mddev_idle(mddev_t *mddev) · line 5101
Quoted 13 lines · NeilBrown
diff
.prev/drivers/md/md.c./drivers/md/md.cdrivers/md/md.c +1
-1@@ -5095,7 +5095,7 @@ static int is_mddev_idle(mddev_t *mddev)* * Note: the following is an unsigned comparison. */ -if ((curr_events - rdev->last_events +4096 ) >8192 ) { +if ((long )curr_events - (long )rdev->last_events >4096 ) { rdev->last_events = curr_events; idle =0 ; }
What did really change? Unless I am seriously mistaken,
curr_events - last_evens + 4096 > 8192
is mathematically equivalent to
curr_events - last_evens > 4096
The casting to (long) may however force a signed comparison which turns things quite upside down, and the comment does not apply anymore.
Jan
Re: [PATCH 002 of 2] md: Improve the is_mddev_idle test
Quoted 18 lines · On Thursday May 10, [email protected] wrote:
drivers/md/md.c static int is_mddev_idle(mddev_t *mddev) · line 5101
Quoted 13 lines · NeilBrown
diff
.prev/drivers/md/md.c./drivers/md/md.cdrivers/md/md.c +1
-1@@ -5095,7 +5095,7 @@ static int is_mddev_idle(mddev_t *mddev)* * Note: the following is an unsigned comparison. */ -if ((curr_events - rdev->last_events +4096 ) >8192 ) { +if ((long )curr_events - (long )rdev->last_events >4096 ) { rdev->last_events = curr_events; idle =0 ; }What did really change? Unless I am seriously mistaken,
Quoted 8 lines
curr_events - last_evens + 4096 > 8192is mathematically equivalent to
curr_events-last_evens> 4096The casting to (long) may however force a signed comparison which turns things quite upside down, and the comment does not apply anymore.
Yes, the use of a signed comparison is the significant difference. And yes, the comment becomes wrong. I'm in the process of redrafting that. It currently stands at:
/* sync IO will cause sync_io to increase before the disk_stats
* as sync_io is counted when a request starts, and
* disk_stats is counted when it completes.
* So resync activity will cause curr_events to be smaller than
* when there was no such activity.
* non-sync IO will cause disk_stat to increase without
* increasing sync_io so curr_events will (eventually)
* be larger than it was before. Once it becomes
* substantially larger, the test below will cause
* the array to appear non-idle, and resync will slow
* down.
* If there is a lot of outstanding resync activity when
* we set last_event to curr_events, then all that activity
* completing might cause the array to appear non-idle
* and resync will be slowed down even though there might
* not have been non-resync activity. This will only
* happen once though. 'last_events' will soon reflect
* the state where there is little or no outstanding
* resync requests, and further resync activity will
* always make curr_events less than last_events. *
*/
Does that read at all well?
NeilBrown
Re: [PATCH 002 of 2] md: Improve the is_mddev_idle test
Quoted 29 lines · Neil Brown
Quoted 6 lines
Quoted 5 lines
drivers/md/md.c +1
-1-if ((curr_events - rdev->last_events +4096 ) >8192 ) { +if ((long )curr_events - (long )rdev->last_events >4096 ) {rdev->last_events = curr_events; idle = 0; }/* sync IO will cause
sync_ioto increase before thedisk_stats
* assync_iois counted when a request starts, and
*disk_statsis counted when it completes.
* So resync activity will causecurr_eventsto be smaller than
* when there was no such activity.
* non-sync IO will causedisk_statto increase without
* increasingsync_iosocurr_eventswill (eventually)
* be larger than it was before. Once it becomes
* substantially larger, the test below will cause
* the array to appear non-idle, and resync will slow
* down.
* If there is a lot of outstanding resync activity when
* we setlast_eventtocurr_events, then all that activity
* completing might cause the array to appear non-idle
* and resync will be slowed down even though there might
* not have been non-resync activity. This will only
* happen once though. 'last_events' will soon reflect
* the state where there is little or no outstanding
* resync requests, and further resync activity will
* always makecurr_eventsless thanlast_events.
*
*/
Quoted 1 line
Does that read at all well?
It is a more verbose explanation of your patch description, yes.
Jan