aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-09-21 01:30:20 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-10-16 17:14:53 -0400
commit7f3b5ef8184a929f56293d6c7a88f426c7b74558 (patch)
tree817b10c76033ad291f9e5d5d56259855016ed190 /drivers/md
parent57fb87f0a640cd7b2e7cde21fd7c13138f8ef75b (diff)
md: Avoid waking up a thread after it has been freed.
commit 01f96c0a9922cd9919baf9d16febdf7016177a12 upstream. Two related problems: 1/ some error paths call "md_unregister_thread(mddev->thread)" without subsequently clearing ->thread. A subsequent call to mddev_unlock will try to wake the thread, and crash. 2/ Most calls to md_wakeup_thread are protected against the thread disappeared either by: - holding the ->mutex - having an active request, so something else must be keeping the array active. However mddev_unlock calls md_wakeup_thread after dropping the mutex and without any certainty of an active request, so the ->thread could theoretically disappear. So we need a spinlock to provide some protections. So change md_unregister_thread to take a pointer to the thread pointer, and ensure that it always does the required locking, and clears the pointer properly. Reported-by: "Moshe Melnikov" <moshe@zadarastorage.com> Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/md.c22
-rw-r--r--drivers/md/md.h2
-rw-r--r--drivers/md/multipath.c3
-rw-r--r--drivers/md/raid1.c3
-rw-r--r--drivers/md/raid10.c5
-rw-r--r--drivers/md/raid5.c6
6 files changed, 26 insertions, 15 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 85540829d64..bc8342812d0 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -61,6 +61,11 @@
61static void autostart_arrays(int part); 61static void autostart_arrays(int part);
62#endif 62#endif
63 63
64/* pers_list is a list of registered personalities protected
65 * by pers_lock.
66 * pers_lock does extra service to protect accesses to
67 * mddev->thread when the mutex cannot be held.
68 */
64static LIST_HEAD(pers_list); 69static LIST_HEAD(pers_list);
65static DEFINE_SPINLOCK(pers_lock); 70static DEFINE_SPINLOCK(pers_lock);
66 71
@@ -690,7 +695,12 @@ static void mddev_unlock(mddev_t * mddev)
690 } else 695 } else
691 mutex_unlock(&mddev->reconfig_mutex); 696 mutex_unlock(&mddev->reconfig_mutex);
692 697
698 /* was we've dropped the mutex we need a spinlock to
699 * make sur the thread doesn't disappear
700 */
701 spin_lock(&pers_lock);
693 md_wakeup_thread(mddev->thread); 702 md_wakeup_thread(mddev->thread);
703 spin_unlock(&pers_lock);
694} 704}
695 705
696static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr) 706static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr)
@@ -6186,11 +6196,18 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
6186 return thread; 6196 return thread;
6187} 6197}
6188 6198
6189void md_unregister_thread(mdk_thread_t *thread) 6199void md_unregister_thread(mdk_thread_t **threadp)
6190{ 6200{
6201 mdk_thread_t *thread = *threadp;
6191 if (!thread) 6202 if (!thread)
6192 return; 6203 return;
6193 dprintk("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk)); 6204 dprintk("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));
6205 /* Locking ensures that mddev_unlock does not wake_up a
6206 * non-existent thread
6207 */
6208 spin_lock(&pers_lock);
6209 *threadp = NULL;
6210 spin_unlock(&pers_lock);
6194 6211
6195 kthread_stop(thread->tsk); 6212 kthread_stop(thread->tsk);
6196 kfree(thread); 6213 kfree(thread);
@@ -7125,8 +7142,7 @@ static void reap_sync_thread(mddev_t *mddev)
7125 mdk_rdev_t *rdev; 7142 mdk_rdev_t *rdev;
7126 7143
7127 /* resync has finished, collect result */ 7144 /* resync has finished, collect result */
7128 md_unregister_thread(mddev->sync_thread); 7145 md_unregister_thread(&mddev->sync_thread);
7129 mddev->sync_thread = NULL;
7130 if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) && 7146 if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
7131 !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) { 7147 !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
7132 /* success...*/ 7148 /* success...*/
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 1c26c7a08ae..ce4e328049d 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -475,7 +475,7 @@ extern int register_md_personality(struct mdk_personality *p);
475extern int unregister_md_personality(struct mdk_personality *p); 475extern int unregister_md_personality(struct mdk_personality *p);
476extern mdk_thread_t * md_register_thread(void (*run) (mddev_t *mddev), 476extern mdk_thread_t * md_register_thread(void (*run) (mddev_t *mddev),
477 mddev_t *mddev, const char *name); 477 mddev_t *mddev, const char *name);
478extern void md_unregister_thread(mdk_thread_t *thread); 478extern void md_unregister_thread(mdk_thread_t **threadp);
479extern void md_wakeup_thread(mdk_thread_t *thread); 479extern void md_wakeup_thread(mdk_thread_t *thread);
480extern void md_check_recovery(mddev_t *mddev); 480extern void md_check_recovery(mddev_t *mddev);
481extern void md_write_start(mddev_t *mddev, struct bio *bi); 481extern void md_write_start(mddev_t *mddev, struct bio *bi);
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 3535c23af28..d5b5fb30017 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -514,8 +514,7 @@ static int multipath_stop (mddev_t *mddev)
514{ 514{
515 multipath_conf_t *conf = mddev->private; 515 multipath_conf_t *conf = mddev->private;
516 516
517 md_unregister_thread(mddev->thread); 517 md_unregister_thread(&mddev->thread);
518 mddev->thread = NULL;
519 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ 518 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
520 mempool_destroy(conf->pool); 519 mempool_destroy(conf->pool);
521 kfree(conf->multipaths); 520 kfree(conf->multipaths);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index f7431b6d844..3a9e59fe7ad 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -2045,8 +2045,7 @@ static int stop(mddev_t *mddev)
2045 raise_barrier(conf); 2045 raise_barrier(conf);
2046 lower_barrier(conf); 2046 lower_barrier(conf);
2047 2047
2048 md_unregister_thread(mddev->thread); 2048 md_unregister_thread(&mddev->thread);
2049 mddev->thread = NULL;
2050 if (conf->r1bio_pool) 2049 if (conf->r1bio_pool)
2051 mempool_destroy(conf->r1bio_pool); 2050 mempool_destroy(conf->r1bio_pool);
2052 kfree(conf->mirrors); 2051 kfree(conf->mirrors);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 6e846688962..17cb6ab6230 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -2331,7 +2331,7 @@ static int run(mddev_t *mddev)
2331 return 0; 2331 return 0;
2332 2332
2333out_free_conf: 2333out_free_conf:
2334 md_unregister_thread(mddev->thread); 2334 md_unregister_thread(&mddev->thread);
2335 if (conf->r10bio_pool) 2335 if (conf->r10bio_pool)
2336 mempool_destroy(conf->r10bio_pool); 2336 mempool_destroy(conf->r10bio_pool);
2337 safe_put_page(conf->tmppage); 2337 safe_put_page(conf->tmppage);
@@ -2349,8 +2349,7 @@ static int stop(mddev_t *mddev)
2349 raise_barrier(conf, 0); 2349 raise_barrier(conf, 0);
2350 lower_barrier(conf); 2350 lower_barrier(conf);
2351 2351
2352 md_unregister_thread(mddev->thread); 2352 md_unregister_thread(&mddev->thread);
2353 mddev->thread = NULL;
2354 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ 2353 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
2355 if (conf->r10bio_pool) 2354 if (conf->r10bio_pool)
2356 mempool_destroy(conf->r10bio_pool); 2355 mempool_destroy(conf->r10bio_pool);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b72edf35ec5..2581ba12735 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5162,8 +5162,7 @@ static int run(mddev_t *mddev)
5162 5162
5163 return 0; 5163 return 0;
5164abort: 5164abort:
5165 md_unregister_thread(mddev->thread); 5165 md_unregister_thread(&mddev->thread);
5166 mddev->thread = NULL;
5167 if (conf) { 5166 if (conf) {
5168 print_raid5_conf(conf); 5167 print_raid5_conf(conf);
5169 free_conf(conf); 5168 free_conf(conf);
@@ -5177,8 +5176,7 @@ static int stop(mddev_t *mddev)
5177{ 5176{
5178 raid5_conf_t *conf = mddev->private; 5177 raid5_conf_t *conf = mddev->private;
5179 5178
5180 md_unregister_thread(mddev->thread); 5179 md_unregister_thread(&mddev->thread);
5181 mddev->thread = NULL;
5182 if (mddev->queue) 5180 if (mddev->queue)
5183 mddev->queue->backing_dev_info.congested_fn = NULL; 5181 mddev->queue->backing_dev_info.congested_fn = NULL;
5184 free_conf(conf); 5182 free_conf(conf);