diff options
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r-- | drivers/md/md.c | 79 |
1 files changed, 64 insertions, 15 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index f30f09cb08e8..6cb31f8da14c 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -201,12 +201,68 @@ static DEFINE_SPINLOCK(all_mddevs_lock); | |||
201 | ) | 201 | ) |
202 | 202 | ||
203 | 203 | ||
204 | static int md_fail_request(struct request_queue *q, struct bio *bio) | 204 | /* Rather than calling directly into the personality make_request function, |
205 | * IO requests come here first so that we can check if the device is | ||
206 | * being suspended pending a reconfiguration. | ||
207 | * We hold a refcount over the call to ->make_request. By the time that | ||
208 | * call has finished, the bio has been linked into some internal structure | ||
209 | * and so is visible to ->quiesce(), so we don't need the refcount any more. | ||
210 | */ | ||
211 | static int md_make_request(struct request_queue *q, struct bio *bio) | ||
205 | { | 212 | { |
206 | bio_io_error(bio); | 213 | mddev_t *mddev = q->queuedata; |
207 | return 0; | 214 | int rv; |
215 | if (mddev == NULL || mddev->pers == NULL) { | ||
216 | bio_io_error(bio); | ||
217 | return 0; | ||
218 | } | ||
219 | rcu_read_lock(); | ||
220 | if (mddev->suspended) { | ||
221 | DEFINE_WAIT(__wait); | ||
222 | for (;;) { | ||
223 | prepare_to_wait(&mddev->sb_wait, &__wait, | ||
224 | TASK_UNINTERRUPTIBLE); | ||
225 | if (!mddev->suspended) | ||
226 | break; | ||
227 | rcu_read_unlock(); | ||
228 | schedule(); | ||
229 | rcu_read_lock(); | ||
230 | } | ||
231 | finish_wait(&mddev->sb_wait, &__wait); | ||
232 | } | ||
233 | atomic_inc(&mddev->active_io); | ||
234 | rcu_read_unlock(); | ||
235 | rv = mddev->pers->make_request(q, bio); | ||
236 | if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended) | ||
237 | wake_up(&mddev->sb_wait); | ||
238 | |||
239 | return rv; | ||
208 | } | 240 | } |
209 | 241 | ||
242 | static void mddev_suspend(mddev_t *mddev) | ||
243 | { | ||
244 | BUG_ON(mddev->suspended); | ||
245 | mddev->suspended = 1; | ||
246 | synchronize_rcu(); | ||
247 | wait_event(mddev->sb_wait, atomic_read(&mddev->active_io) == 0); | ||
248 | mddev->pers->quiesce(mddev, 1); | ||
249 | md_unregister_thread(mddev->thread); | ||
250 | mddev->thread = NULL; | ||
251 | /* we now know that no code is executing in the personality module, | ||
252 | * except possibly the tail end of a ->bi_end_io function, but that | ||
253 | * is certain to complete before the module has a chance to get | ||
254 | * unloaded | ||
255 | */ | ||
256 | } | ||
257 | |||
258 | static void mddev_resume(mddev_t *mddev) | ||
259 | { | ||
260 | mddev->suspended = 0; | ||
261 | wake_up(&mddev->sb_wait); | ||
262 | mddev->pers->quiesce(mddev, 0); | ||
263 | } | ||
264 | |||
265 | |||
210 | static inline mddev_t *mddev_get(mddev_t *mddev) | 266 | static inline mddev_t *mddev_get(mddev_t *mddev) |
211 | { | 267 | { |
212 | atomic_inc(&mddev->active); | 268 | atomic_inc(&mddev->active); |
@@ -314,6 +370,7 @@ static mddev_t * mddev_find(dev_t unit) | |||
314 | init_timer(&new->safemode_timer); | 370 | init_timer(&new->safemode_timer); |
315 | atomic_set(&new->active, 1); | 371 | atomic_set(&new->active, 1); |
316 | atomic_set(&new->openers, 0); | 372 | atomic_set(&new->openers, 0); |
373 | atomic_set(&new->active_io, 0); | ||
317 | spin_lock_init(&new->write_lock); | 374 | spin_lock_init(&new->write_lock); |
318 | init_waitqueue_head(&new->sb_wait); | 375 | init_waitqueue_head(&new->sb_wait); |
319 | init_waitqueue_head(&new->recovery_wait); | 376 | init_waitqueue_head(&new->recovery_wait); |
@@ -3632,10 +3689,12 @@ static int md_alloc(dev_t dev, char *name) | |||
3632 | mddev_put(mddev); | 3689 | mddev_put(mddev); |
3633 | return -ENOMEM; | 3690 | return -ENOMEM; |
3634 | } | 3691 | } |
3692 | mddev->queue->queuedata = mddev; | ||
3693 | |||
3635 | /* Can be unlocked because the queue is new: no concurrency */ | 3694 | /* Can be unlocked because the queue is new: no concurrency */ |
3636 | queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue); | 3695 | queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, mddev->queue); |
3637 | 3696 | ||
3638 | blk_queue_make_request(mddev->queue, md_fail_request); | 3697 | blk_queue_make_request(mddev->queue, md_make_request); |
3639 | 3698 | ||
3640 | disk = alloc_disk(1 << shift); | 3699 | disk = alloc_disk(1 << shift); |
3641 | if (!disk) { | 3700 | if (!disk) { |
@@ -3938,16 +3997,6 @@ static int do_md_run(mddev_t * mddev) | |||
3938 | 3997 | ||
3939 | set_capacity(disk, mddev->array_sectors); | 3998 | set_capacity(disk, mddev->array_sectors); |
3940 | 3999 | ||
3941 | /* If we call blk_queue_make_request here, it will | ||
3942 | * re-initialise max_sectors etc which may have been | ||
3943 | * refined inside -> run. So just set the bits we need to set. | ||
3944 | * Most initialisation happended when we called | ||
3945 | * blk_queue_make_request(..., md_fail_request) | ||
3946 | * earlier. | ||
3947 | */ | ||
3948 | mddev->queue->queuedata = mddev; | ||
3949 | mddev->queue->make_request_fn = mddev->pers->make_request; | ||
3950 | |||
3951 | /* If there is a partially-recovered drive we need to | 4000 | /* If there is a partially-recovered drive we need to |
3952 | * start recovery here. If we leave it to md_check_recovery, | 4001 | * start recovery here. If we leave it to md_check_recovery, |
3953 | * it will remove the drives and not do the right thing | 4002 | * it will remove the drives and not do the right thing |
@@ -4077,7 +4126,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) | |||
4077 | md_super_wait(mddev); | 4126 | md_super_wait(mddev); |
4078 | if (mddev->ro) | 4127 | if (mddev->ro) |
4079 | set_disk_ro(disk, 0); | 4128 | set_disk_ro(disk, 0); |
4080 | blk_queue_make_request(mddev->queue, md_fail_request); | 4129 | |
4081 | mddev->pers->stop(mddev); | 4130 | mddev->pers->stop(mddev); |
4082 | mddev->queue->merge_bvec_fn = NULL; | 4131 | mddev->queue->merge_bvec_fn = NULL; |
4083 | mddev->queue->unplug_fn = NULL; | 4132 | mddev->queue->unplug_fn = NULL; |