vserver 1.9.5.x5
[linux-2.6.git] / drivers / md / raid1.c
index 1ed82ea..060c6a2 100644 (file)
@@ -30,8 +30,6 @@
 #define        NR_RAID1_BIOS 256
 
 static mdk_personality_t raid1_personality;
-static spinlock_t retry_list_lock = SPIN_LOCK_UNLOCKED;
-static LIST_HEAD(retry_list_head);
 
 static void unplug_slaves(mddev_t *mddev);
 
@@ -188,10 +186,11 @@ static void reschedule_retry(r1bio_t *r1_bio)
 {
        unsigned long flags;
        mddev_t *mddev = r1_bio->mddev;
+       conf_t *conf = mddev_to_conf(mddev);
 
-       spin_lock_irqsave(&retry_list_lock, flags);
-       list_add(&r1_bio->retry_list, &retry_list_head);
-       spin_unlock_irqrestore(&retry_list_lock, flags);
+       spin_lock_irqsave(&conf->device_lock, flags);
+       list_add(&r1_bio->retry_list, &conf->retry_list);
+       spin_unlock_irqrestore(&conf->device_lock, flags);
 
        md_wakeup_thread(mddev->thread);
 }
@@ -340,7 +339,7 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio)
        const int sectors = r1_bio->sectors;
        sector_t new_distance, current_distance;
 
-       spin_lock_irq(&conf->device_lock);
+       rcu_read_lock();
        /*
         * Check if it if we can balance. We can balance on the whole
         * device if no resync is going on, or below the resync window.
@@ -417,7 +416,7 @@ rb_out:
                conf->last_used = new_disk;
                atomic_inc(&conf->mirrors[new_disk].rdev->nr_pending);
        }
-       spin_unlock_irq(&conf->device_lock);
+       rcu_read_unlock();
 
        return new_disk;
 }
@@ -426,26 +425,26 @@ static void unplug_slaves(mddev_t *mddev)
 {
        conf_t *conf = mddev_to_conf(mddev);
        int i;
-       unsigned long flags;
 
-       spin_lock_irqsave(&conf->device_lock, flags);
+       rcu_read_lock();
        for (i=0; i<mddev->raid_disks; i++) {
                mdk_rdev_t *rdev = conf->mirrors[i].rdev;
-               if (rdev && atomic_read(&rdev->nr_pending)) {
+               if (rdev && !rdev->faulty && atomic_read(&rdev->nr_pending)) {
                        request_queue_t *r_queue = bdev_get_queue(rdev->bdev);
 
                        atomic_inc(&rdev->nr_pending);
-                       spin_unlock_irqrestore(&conf->device_lock, flags);
+                       rcu_read_unlock();
 
                        if (r_queue->unplug_fn)
                                r_queue->unplug_fn(r_queue);
 
-                       spin_lock_irqsave(&conf->device_lock, flags);
-                       atomic_dec(&rdev->nr_pending);
+                       rdev_dec_pending(rdev, mddev);
+                       rcu_read_lock();
                }
        }
-       spin_unlock_irqrestore(&conf->device_lock, flags);
+       rcu_read_unlock();
 }
+
 static void raid1_unplug(request_queue_t *q)
 {
        unplug_slaves(q->queuedata);
@@ -456,24 +455,28 @@ static int raid1_issue_flush(request_queue_t *q, struct gendisk *disk,
 {
        mddev_t *mddev = q->queuedata;
        conf_t *conf = mddev_to_conf(mddev);
-       unsigned long flags;
        int i, ret = 0;
 
-       spin_lock_irqsave(&conf->device_lock, flags);
-       for (i=0; i<mddev->raid_disks; i++) {
+       rcu_read_lock();
+       for (i=0; i<mddev->raid_disks && ret == 0; i++) {
                mdk_rdev_t *rdev = conf->mirrors[i].rdev;
                if (rdev && !rdev->faulty) {
                        struct block_device *bdev = rdev->bdev;
                        request_queue_t *r_queue = bdev_get_queue(bdev);
 
-                       if (r_queue->issue_flush_fn) {
-                               ret = r_queue->issue_flush_fn(r_queue, bdev->bd_disk, error_sector);
-                               if (ret)
-                                       break;
+                       if (!r_queue->issue_flush_fn)
+                               ret = -EOPNOTSUPP;
+                       else {
+                               atomic_inc(&rdev->nr_pending);
+                               rcu_read_unlock();
+                               ret = r_queue->issue_flush_fn(r_queue, bdev->bd_disk,
+                                                             error_sector);
+                               rdev_dec_pending(rdev, mddev);
+                               rcu_read_lock();
                        }
                }
        }
-       spin_unlock_irqrestore(&conf->device_lock, flags);
+       rcu_read_unlock();
        return ret;
 }
 
@@ -580,7 +583,7 @@ static int make_request(request_queue_t *q, struct bio * bio)
         * bios[x] to bio
         */
        disks = conf->raid_disks;
-       spin_lock_irq(&conf->device_lock);
+       rcu_read_lock();
        for (i = 0;  i < disks; i++) {
                if (conf->mirrors[i].rdev &&
                    !conf->mirrors[i].rdev->faulty) {
@@ -589,7 +592,7 @@ static int make_request(request_queue_t *q, struct bio * bio)
                } else
                        r1_bio->bios[i] = NULL;
        }
-       spin_unlock_irq(&conf->device_lock);
+       rcu_read_unlock();
 
        atomic_set(&r1_bio->remaining, 1);
        md_write_start(mddev);
@@ -711,7 +714,6 @@ static int raid1_spare_active(mddev_t *mddev)
        conf_t *conf = mddev->private;
        mirror_info_t *tmp;
 
-       spin_lock_irq(&conf->device_lock);
        /*
         * Find all failed disks within the RAID1 configuration 
         * and mark them readable
@@ -726,7 +728,6 @@ static int raid1_spare_active(mddev_t *mddev)
                        tmp->rdev->in_sync = 1;
                }
        }
-       spin_unlock_irq(&conf->device_lock);
 
        print_conf(conf);
        return 0;
@@ -740,10 +741,8 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
        int mirror;
        mirror_info_t *p;
 
-       spin_lock_irq(&conf->device_lock);
        for (mirror=0; mirror < mddev->raid_disks; mirror++)
                if ( !(p=conf->mirrors+mirror)->rdev) {
-                       p->rdev = rdev;
 
                        blk_queue_stack_limits(mddev->queue,
                                               rdev->bdev->bd_disk->queue);
@@ -758,9 +757,9 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
                        p->head_position = 0;
                        rdev->raid_disk = mirror;
                        found = 1;
+                       p->rdev = rdev;
                        break;
                }
-       spin_unlock_irq(&conf->device_lock);
 
        print_conf(conf);
        return found;
@@ -769,24 +768,27 @@ static int raid1_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
 static int raid1_remove_disk(mddev_t *mddev, int number)
 {
        conf_t *conf = mddev->private;
-       int err = 1;
+       int err = 0;
+       mdk_rdev_t *rdev;
        mirror_info_t *p = conf->mirrors+ number;
 
        print_conf(conf);
-       spin_lock_irq(&conf->device_lock);
-       if (p->rdev) {
-               if (p->rdev->in_sync ||
-                   atomic_read(&p->rdev->nr_pending)) {
+       rdev = p->rdev;
+       if (rdev) {
+               if (rdev->in_sync ||
+                   atomic_read(&rdev->nr_pending)) {
                        err = -EBUSY;
                        goto abort;
                }
                p->rdev = NULL;
-               err = 0;
+               synchronize_kernel();
+               if (atomic_read(&rdev->nr_pending)) {
+                       /* lost the race, try later */
+                       err = -EBUSY;
+                       p->rdev = rdev;
+               }
        }
-       if (err)
-               MD_BUG();
 abort:
-       spin_unlock_irq(&conf->device_lock);
 
        print_conf(conf);
        return err;
@@ -904,11 +906,11 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
 
 static void raid1d(mddev_t *mddev)
 {
-       struct list_head *head = &retry_list_head;
        r1bio_t *r1_bio;
        struct bio *bio;
        unsigned long flags;
        conf_t *conf = mddev_to_conf(mddev);
+       struct list_head *head = &conf->retry_list;
        int unplug=0;
        mdk_rdev_t *rdev;
 
@@ -917,12 +919,12 @@ static void raid1d(mddev_t *mddev)
        
        for (;;) {
                char b[BDEVNAME_SIZE];
-               spin_lock_irqsave(&retry_list_lock, flags);
+               spin_lock_irqsave(&conf->device_lock, flags);
                if (list_empty(head))
                        break;
                r1_bio = list_entry(head->prev, r1bio_t, retry_list);
                list_del(head->prev);
-               spin_unlock_irqrestore(&retry_list_lock, flags);
+               spin_unlock_irqrestore(&conf->device_lock, flags);
 
                mddev = r1_bio->mddev;
                conf = mddev_to_conf(mddev);
@@ -941,6 +943,8 @@ static void raid1d(mddev_t *mddev)
                        } else {
                                r1_bio->bios[r1_bio->read_disk] = NULL;
                                r1_bio->read_disk = disk;
+                               bio_put(bio);
+                               bio = bio_clone(r1_bio->master_bio, GFP_NOIO);
                                r1_bio->bios[r1_bio->read_disk] = bio;
                                rdev = conf->mirrors[disk].rdev;
                                if (printk_ratelimit())
@@ -948,15 +952,17 @@ static void raid1d(mddev_t *mddev)
                                               " another mirror\n",
                                               bdevname(rdev->bdev,b),
                                               (unsigned long long)r1_bio->sector);
-                               bio->bi_bdev = rdev->bdev;
                                bio->bi_sector = r1_bio->sector + rdev->data_offset;
+                               bio->bi_bdev = rdev->bdev;
+                               bio->bi_end_io = raid1_end_read_request;
                                bio->bi_rw = READ;
+                               bio->bi_private = r1_bio;
                                unplug = 1;
                                generic_make_request(bio);
                        }
                }
        }
-       spin_unlock_irqrestore(&retry_list_lock, flags);
+       spin_unlock_irqrestore(&conf->device_lock, flags);
        if (unplug)
                unplug_slaves(mddev);
 }
@@ -1013,7 +1019,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster)
         * put in a delay to throttle resync.
         */
        if (!go_faster && waitqueue_active(&conf->wait_resume))
-               schedule_timeout(HZ);
+               msleep_interruptible(1000);
        device_barrier(conf, sector_nr + RESYNC_SECTORS);
 
        /*
@@ -1023,7 +1029,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster)
         */
        disk = conf->last_used;
        /* make sure disk is operational */
-       spin_lock_irq(&conf->device_lock);
+
        while (conf->mirrors[disk].rdev == NULL ||
               !conf->mirrors[disk].rdev->in_sync) {
                if (disk <= 0)
@@ -1034,7 +1040,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster)
        }
        conf->last_used = disk;
        atomic_inc(&conf->mirrors[disk].rdev->nr_pending);
-       spin_unlock_irq(&conf->device_lock);
+
 
        mirror = conf->mirrors + disk;
 
@@ -1087,7 +1093,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster)
                int rv = max_sector - sector_nr;
                md_done_sync(mddev, rv, 1);
                put_buf(r1_bio);
-               atomic_dec(&conf->mirrors[disk].rdev->nr_pending);
+               rdev_dec_pending(conf->mirrors[disk].rdev, mddev);
                return rv;
        }
 
@@ -1204,11 +1210,12 @@ static int run(mddev_t *mddev)
        }
        conf->raid_disks = mddev->raid_disks;
        conf->mddev = mddev;
-       conf->device_lock = SPIN_LOCK_UNLOCKED;
+       spin_lock_init(&conf->device_lock);
+       INIT_LIST_HEAD(&conf->retry_list);
        if (conf->working_disks == 1)
                mddev->recovery_cp = MaxSector;
 
-       conf->resync_lock = SPIN_LOCK_UNLOCKED;
+       spin_lock_init(&conf->resync_lock);
        init_waitqueue_head(&conf->wait_idle);
        init_waitqueue_head(&conf->wait_resume);
 
@@ -1286,6 +1293,7 @@ static int stop(mddev_t *mddev)
 
        md_unregister_thread(mddev->thread);
        mddev->thread = NULL;
+       blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
        if (conf->r1bio_pool)
                mempool_destroy(conf->r1bio_pool);
        if (conf->mirrors)