]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/md/md.c
md: allow devices to be shared between md arrays
[linux-2.6-omap-h63xx.git] / drivers / md / md.c
index 78fe3e97ff993293c1d018431c8f5bf1e2531f16..7c9a87b02e7784259d751ed9a57ce02f4a7409a1 100644 (file)
@@ -774,7 +774,11 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
        __u64 ev1 = md_event(sb);
 
        rdev->raid_disk = -1;
-       rdev->flags = 0;
+       clear_bit(Faulty, &rdev->flags);
+       clear_bit(In_sync, &rdev->flags);
+       clear_bit(WriteMostly, &rdev->flags);
+       clear_bit(BarriersNotsupp, &rdev->flags);
+
        if (mddev->raid_disks == 0) {
                mddev->major_version = 0;
                mddev->minor_version = sb->minor_version;
@@ -1154,7 +1158,11 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
        __u64 ev1 = le64_to_cpu(sb->events);
 
        rdev->raid_disk = -1;
-       rdev->flags = 0;
+       clear_bit(Faulty, &rdev->flags);
+       clear_bit(In_sync, &rdev->flags);
+       clear_bit(WriteMostly, &rdev->flags);
+       clear_bit(BarriersNotsupp, &rdev->flags);
+
        if (mddev->raid_disks == 0) {
                mddev->major_version = 1;
                mddev->patch_version = 0;
@@ -1402,7 +1410,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
                goto fail;
        }
        list_add(&rdev->same_set, &mddev->disks);
-       bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk);
+       bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk);
        return 0;
 
  fail:
@@ -1442,7 +1450,7 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev)
  * otherwise reused by a RAID array (or any other kernel
  * subsystem), by bd_claiming the device.
  */
-static int lock_rdev(mdk_rdev_t *rdev, dev_t dev)
+static int lock_rdev(mdk_rdev_t *rdev, dev_t dev, int shared)
 {
        int err = 0;
        struct block_device *bdev;
@@ -1454,13 +1462,15 @@ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev)
                        __bdevname(dev, b));
                return PTR_ERR(bdev);
        }
-       err = bd_claim(bdev, rdev);
+       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);
                return err;
        }
+       if (!shared)
+               set_bit(AllReserved, &rdev->flags);
        rdev->bdev = bdev;
        return err;
 }
@@ -1925,7 +1935,8 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                        return -ENOSPC;
                rdev->raid_disk = slot;
                /* assume it is working */
-               rdev->flags = 0;
+               clear_bit(Faulty, &rdev->flags);
+               clear_bit(WriteMostly, &rdev->flags);
                set_bit(In_sync, &rdev->flags);
        }
        return len;
@@ -1950,6 +1961,10 @@ offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                return -EINVAL;
        if (rdev->mddev->pers)
                return -EBUSY;
+       if (rdev->size && rdev->mddev->external)
+               /* Must set offset before size, so overlap checks
+                * can be sane */
+               return -EBUSY;
        rdev->data_offset = offset;
        return len;
 }
@@ -1963,16 +1978,69 @@ rdev_size_show(mdk_rdev_t *rdev, char *page)
        return sprintf(page, "%llu\n", (unsigned long long)rdev->size);
 }
 
+static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2)
+{
+       /* check if two start/length pairs overlap */
+       if (s1+l1 <= s2)
+               return 0;
+       if (s2+l2 <= s1)
+               return 0;
+       return 1;
+}
+
 static ssize_t
 rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 {
        char *e;
        unsigned long long size = simple_strtoull(buf, &e, 10);
+       unsigned long long oldsize = rdev->size;
        if (e==buf || (*e && *e != '\n'))
                return -EINVAL;
        if (rdev->mddev->pers)
                return -EBUSY;
        rdev->size = size;
+       if (size > oldsize && rdev->mddev->external) {
+               /* need to check that all other rdevs with the same ->bdev
+                * do not overlap.  We need to unlock the mddev to avoid
+                * a deadlock.  We have already changed rdev->size, and if
+                * we have to change it back, we will have the lock again.
+                */
+               mddev_t *mddev;
+               int overlap = 0;
+               struct list_head *tmp, *tmp2;
+
+               mddev_unlock(rdev->mddev);
+               ITERATE_MDDEV(mddev, tmp) {
+                       mdk_rdev_t *rdev2;
+
+                       mddev_lock(mddev);
+                       ITERATE_RDEV(mddev, rdev2, tmp2)
+                               if (test_bit(AllReserved, &rdev2->flags) ||
+                                   (rdev->bdev == rdev2->bdev &&
+                                    rdev != rdev2 &&
+                                    overlaps(rdev->data_offset, rdev->size,
+                                           rdev2->data_offset, rdev2->size))) {
+                                       overlap = 1;
+                                       break;
+                               }
+                       mddev_unlock(mddev);
+                       if (overlap) {
+                               mddev_put(mddev);
+                               break;
+                       }
+               }
+               mddev_lock(rdev->mddev);
+               if (overlap) {
+                       /* Someone else could have slipped in a size
+                        * change here, but doing so is just silly.
+                        * We put oldsize back because we *know* it is
+                        * safe, and trust userspace not to race with
+                        * itself
+                        */
+                       rdev->size = oldsize;
+                       return -EBUSY;
+               }
+       }
        if (size < rdev->mddev->size || rdev->mddev->size == 0)
                rdev->mddev->size = size;
        return len;
@@ -2056,7 +2124,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
        if ((err = alloc_disk_sb(rdev)))
                goto abort_free;
 
-       err = lock_rdev(rdev, newdev);
+       err = lock_rdev(rdev, newdev, super_format == -2);
        if (err)
                goto abort_free;
 
@@ -2609,7 +2677,9 @@ new_dev_store(mddev_t *mddev, const char *buf, size_t len)
                        if (err < 0)
                                goto out;
                }
-       } else
+       } else if (mddev->external)
+               rdev = md_import_device(dev, -2, -1);
+       else
                rdev = md_import_device(dev, -1, -1);
 
        if (IS_ERR(rdev))
@@ -4019,8 +4089,6 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
                else
                        rdev->raid_disk = -1;
 
-               rdev->flags = 0;
-
                if (rdev->raid_disk < mddev->raid_disks)
                        if (info->state & (1<<MD_DISK_SYNC))
                                set_bit(In_sync, &rdev->flags);