Merge branch 'for-linus' of git://neil.brown.name/md
[pandora-kernel.git] / drivers / md / md.c
index 0a0d7c2..cf8594c 100644 (file)
@@ -765,7 +765,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
         */
        struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, mddev);
 
-       bio->bi_bdev = rdev->bdev;
+       bio->bi_bdev = rdev->meta_bdev ? rdev->meta_bdev : rdev->bdev;
        bio->bi_sector = sector;
        bio_add_page(bio, page, size, 0);
        bio->bi_private = rdev;
@@ -795,7 +795,7 @@ static void bi_complete(struct bio *bio, int error)
 }
 
 int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
-                struct page *page, int rw)
+                struct page *page, int rw, bool metadata_op)
 {
        struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, rdev->mddev);
        struct completion event;
@@ -803,8 +803,12 @@ int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
 
        rw |= REQ_SYNC | REQ_UNPLUG;
 
-       bio->bi_bdev = rdev->bdev;
-       bio->bi_sector = sector;
+       bio->bi_bdev = (metadata_op && rdev->meta_bdev) ?
+               rdev->meta_bdev : rdev->bdev;
+       if (metadata_op)
+               bio->bi_sector = sector + rdev->sb_start;
+       else
+               bio->bi_sector = sector + rdev->data_offset;
        bio_add_page(bio, page, size, 0);
        init_completion(&event);
        bio->bi_private = &event;
@@ -829,7 +833,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size)
                return 0;
 
 
-       if (!sync_page_io(rdev, rdev->sb_start, size, rdev->sb_page, READ))
+       if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, true))
                goto fail;
        rdev->sb_loaded = 1;
        return 0;
@@ -1881,7 +1885,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
        rdev->sysfs_state = sysfs_get_dirent_safe(rdev->kobj.sd, "state");
 
        list_add_rcu(&rdev->same_set, &mddev->disks);
-       bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk);
+       bd_link_disk_holder(rdev->bdev, mddev->gendisk);
 
        /* May as well allow recovery to be retried once */
        mddev->recovery_disabled = 0;
@@ -1908,7 +1912,6 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
                MD_BUG();
                return;
        }
-       bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk);
        list_del_rcu(&rdev->same_set);
        printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b));
        rdev->mddev = NULL;
@@ -1936,19 +1939,13 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared)
        struct block_device *bdev;
        char b[BDEVNAME_SIZE];
 
-       bdev = open_by_devnum(dev, FMODE_READ|FMODE_WRITE);
+       bdev = blkdev_get_by_dev(dev, FMODE_READ|FMODE_WRITE|FMODE_EXCL,
+                                shared ? (mdk_rdev_t *)lock_rdev : rdev);
        if (IS_ERR(bdev)) {
                printk(KERN_ERR "md: could not open %s.\n",
                        __bdevname(dev, b));
                return PTR_ERR(bdev);
        }
-       err = bd_claim(bdev, shared ? (mdk_rdev_t *)lock_rdev : rdev);
-       if (err) {
-               printk(KERN_ERR "md: could not bd_claim %s.\n",
-                       bdevname(bdev, b));
-               blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
-               return err;
-       }
        if (!shared)
                set_bit(AllReserved, &rdev->flags);
        rdev->bdev = bdev;
@@ -1961,8 +1958,7 @@ static void unlock_rdev(mdk_rdev_t *rdev)
        rdev->bdev = NULL;
        if (!bdev)
                MD_BUG();
-       bd_release(bdev);
-       blkdev_put(bdev, FMODE_READ|FMODE_WRITE);
+       blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL);
 }
 
 void md_autodetect_dev(dev_t dev);
@@ -2475,6 +2471,10 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                        if (rdev2->raid_disk == slot)
                                return -EEXIST;
 
+               if (slot >= rdev->mddev->raid_disks &&
+                   slot >= rdev->mddev->raid_disks + rdev->mddev->delta_disks)
+                       return -ENOSPC;
+
                rdev->raid_disk = slot;
                if (test_bit(In_sync, &rdev->flags))
                        rdev->saved_raid_disk = slot;
@@ -2492,7 +2492,8 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                        /* failure here is OK */;
                /* don't wakeup anyone, leave that to userspace. */
        } else {
-               if (slot >= rdev->mddev->raid_disks)
+               if (slot >= rdev->mddev->raid_disks &&
+                   slot >= rdev->mddev->raid_disks + rdev->mddev->delta_disks)
                        return -ENOSPC;
                rdev->raid_disk = slot;
                /* assume it is working */
@@ -3117,7 +3118,7 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
                char nm[20];
                if (rdev->raid_disk < 0)
                        continue;
-               if (rdev->new_raid_disk > mddev->raid_disks)
+               if (rdev->new_raid_disk >= mddev->raid_disks)
                        rdev->new_raid_disk = -1;
                if (rdev->new_raid_disk == rdev->raid_disk)
                        continue;
@@ -3914,7 +3915,7 @@ static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed);
 static ssize_t
 sync_completed_show(mddev_t *mddev, char *page)
 {
-       unsigned long max_sectors, resync;
+       unsigned long long max_sectors, resync;
 
        if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
                return sprintf(page, "none\n");
@@ -3925,7 +3926,7 @@ sync_completed_show(mddev_t *mddev, char *page)
                max_sectors = mddev->dev_sectors;
 
        resync = mddev->curr_resync_completed;
-       return sprintf(page, "%lu / %lu\n", resync, max_sectors);
+       return sprintf(page, "%llu / %llu\n", resync, max_sectors);
 }
 
 static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
@@ -4012,19 +4013,24 @@ suspend_lo_store(mddev_t *mddev, const char *buf, size_t len)
 {
        char *e;
        unsigned long long new = simple_strtoull(buf, &e, 10);
+       unsigned long long old = mddev->suspend_lo;
 
        if (mddev->pers == NULL || 
            mddev->pers->quiesce == NULL)
                return -EINVAL;
        if (buf == e || (*e && *e != '\n'))
                return -EINVAL;
-       if (new >= mddev->suspend_hi ||
-           (new > mddev->suspend_lo && new < mddev->suspend_hi)) {
-               mddev->suspend_lo = new;
+
+       mddev->suspend_lo = new;
+       if (new >= old)
+               /* Shrinking suspended region */
                mddev->pers->quiesce(mddev, 2);
-               return len;
-       } else
-               return -EINVAL;
+       else {
+               /* Expanding suspended region - need to wait */
+               mddev->pers->quiesce(mddev, 1);
+               mddev->pers->quiesce(mddev, 0);
+       }
+       return len;
 }
 static struct md_sysfs_entry md_suspend_lo =
 __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
@@ -4041,20 +4047,24 @@ suspend_hi_store(mddev_t *mddev, const char *buf, size_t len)
 {
        char *e;
        unsigned long long new = simple_strtoull(buf, &e, 10);
+       unsigned long long old = mddev->suspend_hi;
 
        if (mddev->pers == NULL ||
            mddev->pers->quiesce == NULL)
                return -EINVAL;
        if (buf == e || (*e && *e != '\n'))
                return -EINVAL;
-       if ((new <= mddev->suspend_lo && mddev->suspend_lo >= mddev->suspend_hi) ||
-           (new > mddev->suspend_lo && new > mddev->suspend_hi)) {
-               mddev->suspend_hi = new;
+
+       mddev->suspend_hi = new;
+       if (new <= old)
+               /* Shrinking suspended region */
+               mddev->pers->quiesce(mddev, 2);
+       else {
+               /* Expanding suspended region - need to wait */
                mddev->pers->quiesce(mddev, 1);
                mddev->pers->quiesce(mddev, 0);
-               return len;
-       } else
-               return -EINVAL;
+       }
+       return len;
 }
 static struct md_sysfs_entry md_suspend_hi =
 __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
@@ -4432,7 +4442,9 @@ int md_run(mddev_t *mddev)
                 * We don't want the data to overlap the metadata,
                 * Internal Bitmap issues have been handled elsewhere.
                 */
-               if (rdev->data_offset < rdev->sb_start) {
+               if (rdev->meta_bdev) {
+                       /* Nothing to check */;
+               } else if (rdev->data_offset < rdev->sb_start) {
                        if (mddev->dev_sectors &&
                            rdev->data_offset + mddev->dev_sectors
                            > rdev->sb_start) {
@@ -5529,7 +5541,6 @@ static int update_size(mddev_t *mddev, sector_t num_sectors)
         * sb_start or, if that is <data_offset, it must fit before the size
         * of each device.  If num_sectors is zero, we find the largest size
         * that fits.
-
         */
        if (mddev->sync_thread)
                return -EBUSY;
@@ -6819,7 +6830,7 @@ void md_do_sync(mddev_t *mddev)
                       desc, mdname(mddev));
                mddev->curr_resync = j;
        }
-       mddev->curr_resync_completed = mddev->curr_resync;
+       mddev->curr_resync_completed = j;
 
        while (j < max_sectors) {
                sector_t sectors;
@@ -6837,8 +6848,7 @@ void md_do_sync(mddev_t *mddev)
                        md_unplug(mddev);
                        wait_event(mddev->recovery_wait,
                                   atomic_read(&mddev->recovery_active) == 0);
-                       mddev->curr_resync_completed =
-                               mddev->curr_resync;
+                       mddev->curr_resync_completed = j;
                        set_bit(MD_CHANGE_CLEAN, &mddev->flags);
                        sysfs_notify(&mddev->kobj, NULL, "sync_completed");
                }