From c9f21aaff1d1fb5629325130af469532d19beb93 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 23 Jul 2008 12:05:51 -0700 Subject: [PATCH 1/6] md: move async_tx_issue_pending_all outside spin_lock_irq Some dma drivers need to call spin_lock_bh in their device_issue_pending routines. This change avoids: WARNING: at kernel/softirq.c:136 local_bh_enable_ip+0x3a/0x85() Signed-off-by: Dan Williams --- drivers/md/raid5.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 42a480ba767b..8a6f101d3225 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3809,10 +3809,8 @@ static void raid5d(mddev_t *mddev) sh = __get_priority_stripe(conf); - if (!sh) { - async_tx_issue_pending_all(); + if (!sh) break; - } spin_unlock_irq(&conf->device_lock); handled++; @@ -3825,6 +3823,7 @@ static void raid5d(mddev_t *mddev) spin_unlock_irq(&conf->device_lock); + async_tx_issue_pending_all(); unplug_slaves(mddev); pr_debug("--- raid5d inactive\n"); From 2339788376e2d69a9154130e4dacd5b21ce63094 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 23 Jul 2008 20:05:34 -0700 Subject: [PATCH 2/6] md: fix merge error The original STRIPE_OP_IO removal patch had the following hunk: - for (i = conf->raid_disks; i--; ) { + for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; - } However it appears the hunk became broken after merging: - for (i = conf->raid_disks; i--; ) { + for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; - } Signed-off-by: Dan Williams --- drivers/md/raid5.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8a6f101d3225..46132fca3469 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2717,10 +2717,11 @@ static void handle_stripe5(struct stripe_head *sh) if (sh->reconstruct_state == reconstruct_state_result) { sh->reconstruct_state = reconstruct_state_idle; clear_bit(STRIPE_EXPANDING, &sh->state); - for (i = conf->raid_disks; i--; ) + for (i = conf->raid_disks; i--; ) { set_bit(R5_Wantwrite, &sh->dev[i].flags); - set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_LOCKED, &sh->dev[i].flags); s.locked++; + } } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && From d8e64406a037a64444175730294e449c9e21f5ec Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 23 Jul 2008 13:09:48 -0700 Subject: [PATCH 3/6] md: delay notification of 'active_idle' to the recovery thread sysfs_notify might sleep, so do not call it from md_safemode_timeout. Signed-off-by: Dan Williams --- drivers/md/md.c | 5 ++++- include/linux/raid/md_k.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index c2ff77ccec50..0f1b83096425 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3483,7 +3483,7 @@ static void md_safemode_timeout(unsigned long data) if (!atomic_read(&mddev->writes_pending)) { mddev->safemode = 1; if (mddev->external) - sysfs_notify(&mddev->kobj, NULL, "array_state"); + set_bit(MD_NOTIFY_ARRAY_STATE, &mddev->flags); } md_wakeup_thread(mddev->thread); } @@ -6051,6 +6051,9 @@ void md_check_recovery(mddev_t *mddev) if (mddev->bitmap) bitmap_daemon_work(mddev->bitmap); + if (test_and_clear_bit(MD_NOTIFY_ARRAY_STATE, &mddev->flags)) + sysfs_notify(&mddev->kobj, NULL, "array_state"); + if (mddev->ro) return; diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 9f2549ac0e2d..c200b9a34aff 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -128,6 +128,7 @@ struct mddev_s #define MD_CHANGE_DEVS 0 /* Some device status has changed */ #define MD_CHANGE_CLEAN 1 /* transition to or from 'clean' */ #define MD_CHANGE_PENDING 2 /* superblock update in progress */ +#define MD_NOTIFY_ARRAY_STATE 3 /* atomic context wants to notify userspace */ int ro; From df10cfbc4d7ab93260d997df754219d390d62a9d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Jul 2008 23:10:39 -0700 Subject: [PATCH 4/6] md: do not progress the resync process if the stripe was blocked handle_stripe will take no action on a stripe when waiting for userspace to unblock the array, so do not report completed sectors. Signed-off-by: Dan Williams --- drivers/md/raid5.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 46132fca3469..b428e15d59af 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2507,7 +2507,7 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh, * */ -static void handle_stripe5(struct stripe_head *sh) +static bool handle_stripe5(struct stripe_head *sh) { raid5_conf_t *conf = sh->raid_conf; int disks = sh->disks, i; @@ -2755,9 +2755,11 @@ static void handle_stripe5(struct stripe_head *sh) ops_run_io(sh, &s); return_io(return_bi); + + return blocked_rdev == NULL; } -static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) +static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) { raid6_conf_t *conf = sh->raid_conf; int disks = sh->disks; @@ -2968,14 +2970,17 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) ops_run_io(sh, &s); return_io(return_bi); + + return blocked_rdev == NULL; } -static void handle_stripe(struct stripe_head *sh, struct page *tmp_page) +/* returns true if the stripe was handled */ +static bool handle_stripe(struct stripe_head *sh, struct page *tmp_page) { if (sh->raid_conf->level == 6) - handle_stripe6(sh, tmp_page); + return handle_stripe6(sh, tmp_page); else - handle_stripe5(sh); + return handle_stripe5(sh); } @@ -3691,7 +3696,9 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *ski clear_bit(STRIPE_INSYNC, &sh->state); spin_unlock(&sh->lock); - handle_stripe(sh, NULL); + /* wait for any blocked device to be handled */ + while(unlikely(!handle_stripe(sh, NULL))) + ; release_stripe(sh); return STRIPE_SECTORS; From e542713529e323ff09d7aeb5806cf29f6f160f53 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 28 Jul 2008 23:28:06 -0700 Subject: [PATCH 5/6] md: do not count blocked devices as spares remove_and_add_spares() assumes that failed devices have been hot-removed from the array. Removal is skipped in the 'blocked' case so do not count a device in this state as 'spare'. Signed-off-by: Dan Williams --- drivers/md/md.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 0f1b83096425..c7aae66c6f9b 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5996,7 +5996,8 @@ static int remove_and_add_spares(mddev_t *mddev) if (mddev->degraded) { rdev_for_each(rdev, rtmp, mddev) { if (rdev->raid_disk >= 0 && - !test_bit(In_sync, &rdev->flags)) + !test_bit(In_sync, &rdev->flags) && + !test_bit(Blocked, &rdev->flags)) spares++; if (rdev->raid_disk < 0 && !test_bit(Faulty, &rdev->flags)) { From 388667bed591b2359713bb17d5de0cf56e961447 Mon Sep 17 00:00:00 2001 From: Arthur Jones Date: Fri, 25 Jul 2008 12:03:38 -0700 Subject: [PATCH 6/6] md: raid10: wake up frozen array When rescheduling a bio in raid10, we wake up the md thread, but if the array is frozen, this will have no effect. This causes the array to remain frozen for eternity. We add a wake_up to allow the array to de-freeze. This code is nearly identical to the raid1 code, which has this fix already. Signed-off-by: Arthur Jones Signed-off-by: NeilBrown --- drivers/md/raid10.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 2acea4025243..8674a5f7e706 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -215,6 +215,9 @@ static void reschedule_retry(r10bio_t *r10_bio) conf->nr_queued ++; spin_unlock_irqrestore(&conf->device_lock, flags); + /* wake up frozen array... */ + wake_up(&conf->wait_barrier); + md_wakeup_thread(mddev->thread); }