md: Introduction - some revised/reordered patches.
PATCH 1/1 md: Introduction - some revised/reordered patches.
Following are revisions of 4 patches currently in -rc4-mm3 which reorder the last to be the first, as that first should go to 2.6.18, but the others don't need to.
The patches are:
md-fix-issues-with-referencing-rdev-in-md-raid1.patch md-factor-out-part-of-raid1d-into-a-separate-function.patch md-remove-working_disks-from-raid1-state-data.patch md-improve-locking-around-error-handling.patch
The first one listed above was at the end of the list, and should go into 2.6.18.
Thanks,
NeilBrown
PATCH 1/1 md: Fix issues with referencing rdev in md/raid1
We need to be careful when referencing mirrors[i].rdev. so it can disappear under us at various times.
So:
fix a couple of problem places.
comment a couple of non-problem places
move an 'atomic_add' which deferences rdev down a little
way to some where where it is sure to not be NULL.
---
drivers/md/raid1.c +35 -22
@@ -930,10 +930,13 @@ static void status(struct seq_file *seq,seq_printf (seq," [%d/%d] [" , conf->raid_disks, conf->working_disks); -for (i =0 ; i < conf->raid_disks; i++) +rcu_read_lock (); +for (i =0 ; i < conf->raid_disks; i++) { +mdk_rdev_t *rdev =rcu_dereference (conf->mirrors[i].rdev);seq_printf (seq,"%s" , - conf->mirrors[i].rdev && -test_bit (In_sync, &conf->mirrors[i].rdev->flags) ?"U" :"_" ); + rdev &&test_bit (In_sync, &rdev->flags) ?"U" :"_" ); + } +rcu_read_unlock ();seq_printf (seq,"]" ); }
@@ -975,7 +978,6 @@ static void error(mddev_t *mddev, mdk_rdstatic void print_conf (conf_t *conf) {int i; -mirror_info_t *tmp;printk ("RAID1 conf printout:\n" );if (!conf) {
@@ -985,14 +987,17 @@ static void print_conf(conf_t *conf)printk (" --- wd:%d rd:%d\n" , conf->working_disks, conf->raid_disks); +rcu_read_lock ();for (i =0 ; i < conf->raid_disks; i++) {char b[BDEVNAME_SIZE ]; - tmp = conf->mirrors + i; -if (tmp->rdev) +mdk_rdev_t *rdev =rcu_dereference (conf->mirrors[i].rdev); +if (rdev)printk (" disk %d, wo:%d, o:%d, dev:%s\n" , - i, !test_bit (In_sync, &tmp->rdev->flags), !test_bit (Faulty, &tmp->rdev->flags), -bdevname (tmp->rdev->bdev,b)); + i, !test_bit (In_sync, &rdev->flags), + !test_bit (Faulty, &rdev->flags), +bdevname (rdev->bdev,b)); } +rcu_read_unlock (); }static void close_sync (conf_t *conf)
@@ -1008,20 +1013,20 @@ static int raid1_spare_active(mddev_t *m {int i;conf_t *conf = mddev->private; -mirror_info_t *tmp;/* * Find all failed disks within the RAID1 configuration -* and mark them readable +* and mark them readable. +* Called under mddev lock, so rcu protection not needed. */ for (i =0 ; i < conf->raid_disks; i++) { - tmp = conf->mirrors + i; -if (tmp->rdev - && !test_bit (Faulty, &tmp->rdev->flags) - && !test_bit (In_sync, &tmp->rdev->flags)) { +mdk_rdev_t *rdev = conf->mirrors[i].rdev; +if (rdev + && !test_bit (Faulty, &rdev->flags) + && !test_bit (In_sync, &rdev->flags)) { conf->working_disks++; mddev->degraded--; -set_bit (In_sync, &tmp->rdev->flags); +set_bit (In_sync, &rdev->flags); } }
@@ -1237,7 +1242,7 @@ static void sync_request_write(mddev_t */* ouch - failed to read all of that. * Try some synchronous reads of other devices to get * good data, much like with normal read errors. Only -* read into the pages we already have so they we don't +* read into the pages we already have so we don't * need to re-issue the read request. * We don't need to freeze the array, because being in an * active sync request, there is no normal IO, and
@@ -1257,6 +1262,10 @@ static void sync_request_write(mddev_t * s =PAGE_SIZE >>9 ; do { if (r1_bio->bios[d]->bi_end_io == end_sync_read) { + /* No rcu protection needed here devices + * can only be removed when no resync is + * active, and resync is currently active + */ rdev = conf->mirrors[d].rdev; if (sync_page_io(rdev->bdev, sect + rdev->data_offset,
@@ -1463,6 +1472,11 @@ static void raid1d(mddev_t *mddev) s =PAGE_SIZE >>9 ; do { + /* Note: no rcu protection needed here + * as this is synchronous in the raid1d thread + * which is the thread that might remove + * a device. If raid1d ever becomes multi-threaded.... + */ rdev = conf->mirrors[d].rdev; if (rdev && test_bit(In_sync, &rdev->flags) &&
@@ -1486,7 +1500,6 @@ static void raid1d(mddev_t *mddev) d = conf->raid_disks; d--; rdev = conf->mirrors[d].rdev; -atomic_add (s, &rdev->corrected_errors);if (rdev &&test_bit (In_sync, &rdev->flags)) { if (sync_page_io(rdev->bdev,
@@ -1509,9 +1522,11 @@ static void raid1d(mddev_t *mddev) s<<9, conf->tmppage, READ) == 0)/* Well, this device is dead */ md_error (mddev, rdev); - else + else { +atomic_add (s, &rdev->corrected_errors);printk (KERN_INFO "raid1:%s: read error corrected (%d sectors at %llu on %s)\n" ,mdname (mddev), s, (unsigned long long )(sect + rdev->data_offset),bdevname (rdev->bdev, b)); + } } } }else {
@@ -1787,19 +1802,17 @@ static sector_t sync_request(mddev_t *mdfor (i=0 ; i<conf->raid_disks; i++) { bio = r1_bio->bios[i];if (bio->bi_end_io == end_sync_read) { -md_sync_acct (conf->mirrors[i].rdev->bdev, nr_sectors); +md_sync_acct (bio->bi_bdev, nr_sectors);generic_make_request (bio); } } }else {atomic_set (&r1_bio->remaining,1 ); bio = r1_bio->bios[r1_bio->read_disk]; -md_sync_acct (conf->mirrors[r1_bio->read_disk].rdev->bdev, - nr_sectors); +md_sync_acct (bio->bi_bdev, nr_sectors);generic_make_request (bio); } -return nr_sectors; }
PATCH 1/1 md: Factor out part of raid1d into a separate function
In reply to NeilBrownFrom: NeilBrown <[email protected]>
raid1d has toooo many nested block, so take the fix_read_error functionality out into a separate function.
---
drivers/md/raid1.c | 161 +++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 72 deletions(-)
drivers/md/raid1.c +95 -78
@@ -1368,6 +1368,95 @@ static void sync_request_write(mddev_t ** 3. Performs writes following reads for array syncronising. */ +static void fix_read_error (conf_t *conf,int read_disk, +sector_t sect,int sectors) +{ +mddev_t *mddev = conf->mddev; +while (sectors) { +int s = sectors; +int d = read_disk; +int success =0 ; +int start; +mdk_rdev_t *rdev; + +if (s > (PAGE_SIZE >>9 )) + s =PAGE_SIZE >>9 ; + +do { +/* Note: no rcu protection needed here +* as this is synchronous in the raid1d thread +* which is the thread that might remove +* a device. If raid1d ever becomes multi-threaded.... +*/ + rdev = conf->mirrors[d].rdev; +if (rdev && +test_bit (In_sync, &rdev->flags) && +sync_page_io (rdev->bdev, + sect + rdev->data_offset, + s<<9 , + conf->tmppage,READ )) + success =1 ; +else { + d++; +if (d == conf->raid_disks) + d =0 ; + } + }while (!success && d != read_disk); + +if (!success) { +/* Cannot read from anywhere -- bye bye array */ +md_error (mddev, conf->mirrors[read_disk].rdev); +break ; + } +/* write it back and re-read */ + start = d; +while (d != read_disk) { +if (d==0 ) + d = conf->raid_disks; + d--; + rdev = conf->mirrors[d].rdev; +if (rdev && +test_bit (In_sync, &rdev->flags)) { +if (sync_page_io (rdev->bdev, + sect + rdev->data_offset, + s<<9 , conf->tmppage,WRITE ) + ==0 ) +/* Well, this device is dead */ +md_error (mddev, rdev); + } + } + d = start; +while (d != read_disk) { +char b[BDEVNAME_SIZE ]; +if (d==0 ) + d = conf->raid_disks; + d--; + rdev = conf->mirrors[d].rdev; +if (rdev && +test_bit (In_sync, &rdev->flags)) { +if (sync_page_io (rdev->bdev, + sect + rdev->data_offset, + s<<9 , conf->tmppage,READ ) + ==0 ) +/* Well, this device is dead */ +md_error (mddev, rdev); +else { +atomic_add (s, &rdev->corrected_errors); +printk (KERN_INFO +"raid1:%s: read error corrected " +"(%d sectors at %llu on %s)\n" , +mdname (mddev), s, + (unsigned long long )sect + + rdev->data_offset, +bdevname (rdev->bdev, b)); + } + } + } + sectors -= s; + sect += s; + } +} +static void raid1d (mddev_t *mddev) {r1bio_t *r1_bio;
@@ -1460,86 +1549,14 @@ static void raid1d(mddev_t *mddev)* This is all done synchronously while the array is * frozen */ -sector_t sect = r1_bio->sector; -int sectors = r1_bio->sectors; -freeze_array (conf); -if (mddev->ro ==0 )while (sectors) { -int s = sectors; -int d = r1_bio->read_disk; -int success =0 ; - -if (s > (PAGE_SIZE >>9 )) - s =PAGE_SIZE >>9 ; - -do { -/* Note: no rcu protection needed here -* as this is synchronous in the raid1d thread -* which is the thread that might remove -* a device. If raid1d ever becomes multi-threaded.... -*/ - rdev = conf->mirrors[d].rdev; -if (rdev && -test_bit (In_sync, &rdev->flags) && -sync_page_io (rdev->bdev, - sect + rdev->data_offset, - s<<9 , - conf->tmppage,READ )) - success =1 ; -else { - d++; -if (d == conf->raid_disks) - d =0 ; - } - }while (!success && d != r1_bio->read_disk); - -if (success) { -/* write it back and re-read */ -int start = d; -while (d != r1_bio->read_disk) { -if (d==0 ) - d = conf->raid_disks; - d--; - rdev = conf->mirrors[d].rdev; -if (rdev && -test_bit (In_sync, &rdev->flags)) { -if (sync_page_io (rdev->bdev, - sect + rdev->data_offset, - s<<9 , conf->tmppage,WRITE ) ==0 ) -/* Well, this device is dead */ -md_error (mddev, rdev); - } - } - d = start; -while (d != r1_bio->read_disk) { -if (d==0 ) - d = conf->raid_disks; - d--; - rdev = conf->mirrors[d].rdev; -if (rdev && -test_bit (In_sync, &rdev->flags)) { -if (sync_page_io (rdev->bdev, - sect + rdev->data_offset, - s<<9 , conf->tmppage,READ ) ==0 ) -/* Well, this device is dead */ -md_error (mddev, rdev); -else { -atomic_add (s, &rdev->corrected_errors); -printk (KERN_INFO "raid1:%s: read error corrected (%d sectors at %llu on %s)\n" , -mdname (mddev), s, (unsigned long long )(sect + rdev->data_offset),bdevname (rdev->bdev, b)); - } - } - } - }else { -/* Cannot read from anywhere -- bye bye array */ -md_error (mddev, conf->mirrors[r1_bio->read_disk].rdev); -break ; - } - sectors -= s; - sect += s; +if (mddev->ro ==0 ) { +freeze_array (conf); +fix_read_error (conf, r1_bio->read_disk, + r1_bio->sector, + r1_bio->sectors); +unfreeze_array (conf); } -unfreeze_array (conf); - bio = r1_bio->bios[r1_bio->read_disk];if ((disk=read_balance (conf, r1_bio)) ==-1 ) { printk(KERN_ALERT "raid1: %s: unrecoverable I/O"
PATCH 1/1 md: Remove working_disks from raid1 state data
In reply to NeilBrownFrom: NeilBrown <[email protected]>
It is equivalent to conf->raid_disks - conf->mddev->degraded.
---
drivers/md/raid1.c | 28 ++++++++++++---------------- include/linux/raid/raid1.h | 1 - 2 files changed, 12 insertions(+), 17 deletions(-)
drivers/md/raid1.c +12 -16
@@ -271,7 +271,7 @@ static int raid1_end_read_request(struct*/ update_head_pos (mirror, r1_bio); -if (uptodate || conf->working_disks <=1 ) { +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
@@ -929,7 +929,7 @@ static void status(struct seq_file *seq,int i;seq_printf (seq," [%d/%d] [" , conf->raid_disks, - conf->working_disks); + conf->raid_disks - mddev->degraded);rcu_read_lock ();for (i =0 ; i < conf->raid_disks; i++) {mdk_rdev_t *rdev =rcu_dereference (conf->mirrors[i].rdev);
@@ -953,7 +953,7 @@ static void error(mddev_t *mddev, mdk_rd* else mark the drive as failed */ if (test_bit (In_sync, &rdev->flags) - && conf->working_disks ==1 ) + && (conf->raid_disks - mddev->degraded) ==1 )/* * Don't fail the drive, act as though we were just a * normal single drive
@@ -961,7 +961,6 @@ static void error(mddev_t *mddev, mdk_rdreturn ;if (test_bit (In_sync, &rdev->flags)) { mddev->degraded++; - conf->working_disks--;/* * if recovery is running, make sure it aborts. */
@@ -972,7 +971,7 @@ static void error(mddev_t *mddev, mdk_rdset_bit (MD_CHANGE_DEVS , &mddev->flags);printk (KERN_ALERT "raid1: Disk failure on %s, disabling device. \n" " Operation continuing on %d devices\n" , -bdevname (rdev->bdev,b), conf->working_disks); +bdevname (rdev->bdev,b), conf->raid_disks - mddev->degraded); }static void print_conf (conf_t *conf)
@@ -984,7 +983,7 @@ static void print_conf(conf_t *conf)printk ("(!conf)\n" );return ; } -printk (" --- wd:%d rd:%d\n" , conf->working_disks, +printk (" --- wd:%d rd:%d\n" , conf->raid_disks - conf->mddev->degraded, conf->raid_disks);rcu_read_lock ();
@@ -1024,7 +1023,6 @@ static int raid1_spare_active(mddev_t *mif (rdev && !test_bit (Faulty, &rdev->flags) && !test_bit (In_sync, &rdev->flags)) { - conf->working_disks++; mddev->degraded--;set_bit (In_sync, &rdev->flags); }
@@ -1901,15 +1899,11 @@ static int run(mddev_t *mddev)blk_queue_max_sectors (mddev->queue,PAGE_SIZE >>9 ); disk->head_position =0 ; -if (!test_bit (Faulty, &rdev->flags) &&test_bit (In_sync, &rdev->flags)) - conf->working_disks++; } conf->raid_disks = mddev->raid_disks; conf->mddev = mddev;spin_lock_init (&conf->device_lock);INIT_LIST_HEAD (&conf->retry_list); -if (conf->working_disks ==1 ) - mddev->recovery_cp = MaxSector;spin_lock_init (&conf->resync_lock);init_waitqueue_head (&conf->wait_barrier);
@@ -1917,11 +1911,6 @@ static int run(mddev_t *mddev)bio_list_init (&conf->pending_bio_list);bio_list_init (&conf->flushing_bio_list); -if (!conf->working_disks) { -printk (KERN_ERR "raid1: no operational mirrors for %s\n" , -mdname (mddev)); - goto out_free_conf; - } mddev->degraded =0 ;for (i =0 ; i < conf->raid_disks; i++) {
@@ -1934,6 +1923,13 @@ static int run(mddev_t *mddev) mddev->degraded++; } } +if (mddev->degraded == conf->raid_disks) { +printk (KERN_ERR "raid1: no operational mirrors for %s\n" , +mdname (mddev)); + goto out_free_conf; + } +if (conf->raid_disks - mddev->degraded ==1 ) + mddev->recovery_cp = MaxSector;/* * find the first working one and use it as a starting point
include/linux/raid/raid1.h +0 -1
@@ -30,7 +30,6 @@ struct r1_private_data_s {mddev_t *mddev;mirror_info_t *mirrors;int raid_disks; -int working_disks;int last_used;sector_t next_seq_sect;spinlock_t device_lock;
PATCH 1/1 md: Improve locking around error handling
In reply to NeilBrownFrom: NeilBrown <[email protected]>
The error handling routines don't use proper locking, and so two concurrent errors could trigger a problem.
So:
- use test-and-set and test-and-clear to synchonise
the In_sync bits with the ->degraded count
- use the spinlock to protect updates to the degraded count (could use an atomic_t but that would be a bigger change in code, and isn't really justified)
- remove un-necessary locking in raid5
---
drivers/md/raid1.c | 16 +++++++++++----- drivers/md/raid10.c | 12 ++++++++---- drivers/md/raid5.c | 20 ++++++++++++-------- 3 files changed, 31 insertions(+), 17 deletions(-)
drivers/md/raid1.c +11 -5
@@ -959,14 +959,16 @@ static void error(mddev_t *mddev, mdk_rd* normal single drive */ return ; -if (test_bit (In_sync, &rdev->flags)) { +if (test_and_clear_bit (In_sync, &rdev->flags)) { +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded++; +spin_unlock_irqrestore (&conf->device_lock, flags);/* * if recovery is running, make sure it aborts. */ set_bit (MD_RECOVERY_ERR , &mddev->recovery); } -clear_bit (In_sync, &rdev->flags);set_bit (Faulty, &rdev->flags);set_bit (MD_CHANGE_DEVS , &mddev->flags); printk(KERN_ALERT "raid1: Disk failure on %s, disabling device. \n"
@@ -1022,9 +1024,11 @@ static int raid1_spare_active(mddev_t *mmdk_rdev_t *rdev = conf->mirrors[i].rdev;if (rdev && !test_bit (Faulty, &rdev->flags) - && !test_bit (In_sync, &rdev->flags)) { + && !test_and_set_bit (In_sync, &rdev->flags)) { +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded--; -set_bit (In_sync, &rdev->flags); +spin_unlock_irqrestore (&conf->device_lock, flags); } }
@@ -2048,7 +2052,7 @@ static int raid1_reshape(mddev_t *mddev)mirror_info_t *newmirrors;conf_t *conf =mddev_to_conf (mddev);int cnt, raid_disks; - +unsigned long flags;int d, d2;/* Cannot change chunk_size, layout, or level */
@@ -2107,7 +2111,9 @@ static int raid1_reshape(mddev_t *mddev)kfree (conf->poolinfo); conf->poolinfo = newpoolinfo; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded += (raid_disks - conf->raid_disks); +spin_unlock_irqrestore (&conf->device_lock, flags); conf->raid_disks = mddev->raid_disks = raid_disks; mddev->delta_disks =0 ;
drivers/md/raid10.c +8 -4
@@ -950,14 +950,16 @@ static void error(mddev_t *mddev, mdk_rd* really dead" tests... */ return ; -if (test_bit (In_sync, &rdev->flags)) { +if (test_and_clear_bit (In_sync, &rdev->flags)) { +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded++; +spin_unlock_irqrestore (&conf->device_lock, flags);/* * if recovery is running, make sure it aborts. */ set_bit (MD_RECOVERY_ERR , &mddev->recovery); } -clear_bit (In_sync, &rdev->flags);set_bit (Faulty, &rdev->flags);set_bit (MD_CHANGE_DEVS , &mddev->flags); printk(KERN_ALERT "raid10: Disk failure on %s, disabling device. \n"
@@ -1033,9 +1035,11 @@ static int raid10_spare_active(mddev_t * tmp = conf->mirrors + i;if (tmp->rdev && !test_bit (Faulty, &tmp->rdev->flags) - && !test_bit (In_sync, &tmp->rdev->flags)) { + && !test_and_set_bit (In_sync, &tmp->rdev->flags)) { +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded--; -set_bit (In_sync, &tmp->rdev->flags); +spin_unlock_irqrestore (&conf->device_lock, flags); } }
drivers/md/raid5.c +12 -8
@@ -636,7 +636,6 @@ static int raid5_end_write_request (strustruct stripe_head *sh = bi->bi_private;raid5_conf_t *conf = sh->raid_conf;int disks = sh->disks, i; -unsigned long flags;int uptodate =test_bit (BIO_UPTODATE , &bi->bi_flags);if (bi->bi_size)
@@ -654,7 +653,6 @@ static int raid5_end_write_request (strureturn 0 ; } -spin_lock_irqsave (&conf->device_lock, flags);if (!uptodate)md_error (conf->mddev, conf->disks[i].rdev);
@@ -662,8 +660,7 @@ static int raid5_end_write_request (struclear_bit (R5_LOCKED , &sh->dev[i].flags);set_bit (STRIPE_HANDLE , &sh->state); -__release_stripe (conf, sh); -spin_unlock_irqrestore (&conf->device_lock, flags); +release_stripe (sh);return 0 ; }
@@ -697,9 +694,11 @@ static void error(mddev_t *mddev, mdk_rdif (!test_bit (Faulty, &rdev->flags)) {set_bit (MD_CHANGE_DEVS , &mddev->flags); -if (test_bit (In_sync, &rdev->flags)) { +if (test_and_clear_bit (In_sync, &rdev->flags)) { +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded++; -clear_bit (In_sync, &rdev->flags); +spin_unlock_irqrestore (&conf->device_lock, flags);/* * if recovery was running, make sure it aborts. */
@@ -3419,9 +3418,11 @@ static int raid5_spare_active(mddev_t *m tmp = conf->disks + i;if (tmp->rdev && !test_bit (Faulty, &tmp->rdev->flags) - && !test_bit (In_sync, &tmp->rdev->flags)) { + && !test_and_set_bit (In_sync, &tmp->rdev->flags)) { +unsigned long flags; +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded--; -set_bit (In_sync, &tmp->rdev->flags); +spin_unlock_irqrestore (&conf->device_lock, flags); } }print_raid5_conf (conf);
@@ -3557,6 +3558,7 @@ static int raid5_start_reshape(mddev_t *struct list_head *rtmp;int spares =0 ;int added_devices =0 ; +unsigned long flags;if (mddev->degraded ||test_bit (MD_RECOVERY_RUNNING , &mddev->recovery))
@@ -3598,7 +3600,9 @@ static int raid5_start_reshape(mddev_t *break ; } +spin_lock_irqsave (&conf->device_lock, flags); mddev->degraded = (conf->raid_disks - conf->previous_raid_disks) - added_devices; +spin_unlock_irqrestore (&conf->device_lock, flags); mddev->raid_disks = conf->raid_disks; mddev->reshape_position =0 ;set_bit (MD_CHANGE_DEVS , &mddev->flags);
Recommended kernel?
In reply to NeilBrownI'm thinking about upgrading from Linux 2.6.14 to some newer kernel - probably to whatever is in Debian unstable. They're all basically safe for md and RAID1, right? No gotcha kernel versions to especially avoid?
--Jeff
Re: Recommended kernel?
Quoted 3 lines · On Sunday August 27, [email protected] wrote:
I'm thinking about upgrading from Linux 2.6.14 to some newer kernel - probably to whatever is in Debian unstable. They're all basically safe for md and RAID1, right? No gotcha kernel versions to especially avoid?
2.6.17.11 should be fine.
NeilBrown