md/raid10: set dev_sectors properly when resizing devices in array.
[linux-flexiantxendom0-3.2.10.git] / drivers / md / raid10.c
index e4a66ab..3e7b154 100644 (file)
@@ -1788,6 +1788,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
        struct r10conf *conf = mddev->private;
        int i, first;
        struct bio *tbio, *fbio;
+       int vcnt;
 
        atomic_set(&r10_bio->remaining, 1);
 
@@ -1802,10 +1803,10 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
        first = i;
        fbio = r10_bio->devs[i].bio;
 
+       vcnt = (r10_bio->sectors + (PAGE_SIZE >> 9) - 1) >> (PAGE_SHIFT - 9);
        /* now find blocks with errors */
        for (i=0 ; i < conf->copies ; i++) {
                int  j, d;
-               int vcnt = r10_bio->sectors >> (PAGE_SHIFT-9);
 
                tbio = r10_bio->devs[i].bio;
 
@@ -1821,7 +1822,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
                        for (j = 0; j < vcnt; j++)
                                if (memcmp(page_address(fbio->bi_io_vec[j].bv_page),
                                           page_address(tbio->bi_io_vec[j].bv_page),
-                                          PAGE_SIZE))
+                                          fbio->bi_io_vec[j].bv_len))
                                        break;
                        if (j == vcnt)
                                continue;
@@ -1871,7 +1872,6 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio)
         */
        for (i = 0; i < conf->copies; i++) {
                int j, d;
-               int vcnt = r10_bio->sectors >> (PAGE_SHIFT-9);
 
                tbio = r10_bio->devs[i].repl_bio;
                if (!tbio || !tbio->bi_end_io)
@@ -3164,12 +3164,40 @@ raid10_size(struct mddev *mddev, sector_t sectors, int raid_disks)
        return size << conf->chunk_shift;
 }
 
+static void calc_sectors(struct r10conf *conf, sector_t size)
+{
+       /* Calculate the number of sectors-per-device that will
+        * actually be used, and set conf->dev_sectors and
+        * conf->stride
+        */
+
+       size = size >> conf->chunk_shift;
+       sector_div(size, conf->far_copies);
+       size = size * conf->raid_disks;
+       sector_div(size, conf->near_copies);
+       /* 'size' is now the number of chunks in the array */
+       /* calculate "used chunks per device" */
+       size = size * conf->copies;
+
+       /* We need to round up when dividing by raid_disks to
+        * get the stride size.
+        */
+       size = DIV_ROUND_UP_SECTOR_T(size, conf->raid_disks);
+
+       conf->dev_sectors = size << conf->chunk_shift;
+
+       if (conf->far_offset)
+               conf->stride = 1 << conf->chunk_shift;
+       else {
+               sector_div(size, conf->near_copies);
+               conf->stride = size << conf->chunk_shift;
+       }
+}
 
 static struct r10conf *setup_conf(struct mddev *mddev)
 {
        struct r10conf *conf = NULL;
        int nc, fc, fo;
-       sector_t stride, size;
        int err = -EINVAL;
 
        if (mddev->new_chunk_sectors < (PAGE_SIZE >> 9) ||
@@ -3219,28 +3247,7 @@ static struct r10conf *setup_conf(struct mddev *mddev)
        if (!conf->r10bio_pool)
                goto out;
 
-       size = mddev->dev_sectors >> conf->chunk_shift;
-       sector_div(size, fc);
-       size = size * conf->raid_disks;
-       sector_div(size, nc);
-       /* 'size' is now the number of chunks in the array */
-       /* calculate "used chunks per device" in 'stride' */
-       stride = size * conf->copies;
-
-       /* We need to round up when dividing by raid_disks to
-        * get the stride size.
-        */
-       stride += conf->raid_disks - 1;
-       sector_div(stride, conf->raid_disks);
-
-       conf->dev_sectors = stride << conf->chunk_shift;
-
-       if (fo)
-               stride = 1;
-       else
-               sector_div(stride, fc);
-       conf->stride = stride << conf->chunk_shift;
-
+       calc_sectors(conf, mddev->dev_sectors);
 
        spin_lock_init(&conf->device_lock);
        INIT_LIST_HEAD(&conf->retry_list);
@@ -3436,6 +3443,44 @@ static void raid10_quiesce(struct mddev *mddev, int state)
        }
 }
 
+static int raid10_resize(struct mddev *mddev, sector_t sectors)
+{
+       /* Resize of 'far' arrays is not supported.
+        * For 'near' and 'offset' arrays we can set the
+        * number of sectors used to be an appropriate multiple
+        * of the chunk size.
+        * For 'offset', this is far_copies*chunksize.
+        * For 'near' the multiplier is the LCM of
+        * near_copies and raid_disks.
+        * So if far_copies > 1 && !far_offset, fail.
+        * Else find LCM(raid_disks, near_copy)*far_copies and
+        * multiply by chunk_size.  Then round to this number.
+        * This is mostly done by raid10_size()
+        */
+       struct r10conf *conf = mddev->private;
+       sector_t oldsize, size;
+
+       if (conf->far_copies > 1 && !conf->far_offset)
+               return -EINVAL;
+
+       oldsize = raid10_size(mddev, 0, 0);
+       size = raid10_size(mddev, sectors, 0);
+       md_set_array_sectors(mddev, size);
+       if (mddev->array_sectors > size)
+               return -EINVAL;
+       set_capacity(mddev->gendisk, mddev->array_sectors);
+       revalidate_disk(mddev->gendisk);
+       if (sectors > mddev->dev_sectors &&
+           mddev->recovery_cp > oldsize) {
+               mddev->recovery_cp = oldsize;
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       }
+       calc_sectors(conf, sectors);
+       mddev->dev_sectors = conf->dev_sectors;
+       mddev->resync_max_sectors = size;
+       return 0;
+}
+
 static void *raid10_takeover_raid0(struct mddev *mddev)
 {
        struct md_rdev *rdev;
@@ -3505,6 +3550,7 @@ static struct md_personality raid10_personality =
        .sync_request   = sync_request,
        .quiesce        = raid10_quiesce,
        .size           = raid10_size,
+       .resize         = raid10_resize,
        .takeover       = raid10_takeover,
 };