|
@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk,
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
-static int linear_run (mddev_t *mddev)
|
|
|
|
|
|
+static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
|
|
{
|
|
{
|
|
linear_conf_t *conf;
|
|
linear_conf_t *conf;
|
|
dev_info_t **table;
|
|
dev_info_t **table;
|
|
@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev)
|
|
sector_t curr_offset;
|
|
sector_t curr_offset;
|
|
struct list_head *tmp;
|
|
struct list_head *tmp;
|
|
|
|
|
|
- conf = kzalloc (sizeof (*conf) + mddev->raid_disks*sizeof(dev_info_t),
|
|
|
|
|
|
+ conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t),
|
|
GFP_KERNEL);
|
|
GFP_KERNEL);
|
|
if (!conf)
|
|
if (!conf)
|
|
- goto out;
|
|
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
mddev->private = conf;
|
|
mddev->private = conf;
|
|
|
|
|
|
cnt = 0;
|
|
cnt = 0;
|
|
- mddev->array_size = 0;
|
|
|
|
|
|
+ conf->array_size = 0;
|
|
|
|
|
|
ITERATE_RDEV(mddev,rdev,tmp) {
|
|
ITERATE_RDEV(mddev,rdev,tmp) {
|
|
int j = rdev->raid_disk;
|
|
int j = rdev->raid_disk;
|
|
dev_info_t *disk = conf->disks + j;
|
|
dev_info_t *disk = conf->disks + j;
|
|
|
|
|
|
- if (j < 0 || j > mddev->raid_disks || disk->rdev) {
|
|
|
|
|
|
+ if (j < 0 || j > raid_disks || disk->rdev) {
|
|
printk("linear: disk numbering problem. Aborting!\n");
|
|
printk("linear: disk numbering problem. Aborting!\n");
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev)
|
|
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
|
|
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
|
|
|
|
|
|
disk->size = rdev->size;
|
|
disk->size = rdev->size;
|
|
- mddev->array_size += rdev->size;
|
|
|
|
|
|
+ conf->array_size += rdev->size;
|
|
|
|
|
|
cnt++;
|
|
cnt++;
|
|
}
|
|
}
|
|
- if (cnt != mddev->raid_disks) {
|
|
|
|
|
|
+ if (cnt != raid_disks) {
|
|
printk("linear: not enough drives present. Aborting!\n");
|
|
printk("linear: not enough drives present. Aborting!\n");
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev)
|
|
unsigned round;
|
|
unsigned round;
|
|
unsigned long base;
|
|
unsigned long base;
|
|
|
|
|
|
- sz = mddev->array_size >> conf->preshift;
|
|
|
|
|
|
+ sz = conf->array_size >> conf->preshift;
|
|
sz += 1; /* force round-up */
|
|
sz += 1; /* force round-up */
|
|
base = conf->hash_spacing >> conf->preshift;
|
|
base = conf->hash_spacing >> conf->preshift;
|
|
round = sector_div(sz, base);
|
|
round = sector_div(sz, base);
|
|
@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev)
|
|
|
|
|
|
BUG_ON(table - conf->hash_table > nb_zone);
|
|
BUG_ON(table - conf->hash_table > nb_zone);
|
|
|
|
|
|
|
|
+ return conf;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ kfree(conf);
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int linear_run (mddev_t *mddev)
|
|
|
|
+{
|
|
|
|
+ linear_conf_t *conf;
|
|
|
|
+
|
|
|
|
+ conf = linear_conf(mddev, mddev->raid_disks);
|
|
|
|
+
|
|
|
|
+ if (!conf)
|
|
|
|
+ return 1;
|
|
|
|
+ mddev->private = conf;
|
|
|
|
+ mddev->array_size = conf->array_size;
|
|
|
|
+
|
|
blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
|
|
blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
|
|
mddev->queue->unplug_fn = linear_unplug;
|
|
mddev->queue->unplug_fn = linear_unplug;
|
|
mddev->queue->issue_flush_fn = linear_issue_flush;
|
|
mddev->queue->issue_flush_fn = linear_issue_flush;
|
|
return 0;
|
|
return 0;
|
|
|
|
+}
|
|
|
|
|
|
-out:
|
|
|
|
- kfree(conf);
|
|
|
|
- return 1;
|
|
|
|
|
|
+static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
|
|
|
|
+{
|
|
|
|
+ /* Adding a drive to a linear array allows the array to grow.
|
|
|
|
+ * It is permitted if the new drive has a matching superblock
|
|
|
|
+ * already on it, with raid_disk equal to raid_disks.
|
|
|
|
+ * It is achieved by creating a new linear_private_data structure
|
|
|
|
+ * and swapping it in in-place of the current one.
|
|
|
|
+ * The current one is never freed until the array is stopped.
|
|
|
|
+ * This avoids races.
|
|
|
|
+ */
|
|
|
|
+ linear_conf_t *newconf;
|
|
|
|
+
|
|
|
|
+ if (rdev->raid_disk != mddev->raid_disks)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ newconf = linear_conf(mddev,mddev->raid_disks+1);
|
|
|
|
+
|
|
|
|
+ if (!newconf)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ newconf->prev = mddev_to_conf(mddev);
|
|
|
|
+ mddev->private = newconf;
|
|
|
|
+ mddev->raid_disks++;
|
|
|
|
+ mddev->array_size = newconf->array_size;
|
|
|
|
+ set_capacity(mddev->gendisk, mddev->array_size << 1);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int linear_stop (mddev_t *mddev)
|
|
static int linear_stop (mddev_t *mddev)
|
|
@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev)
|
|
linear_conf_t *conf = mddev_to_conf(mddev);
|
|
linear_conf_t *conf = mddev_to_conf(mddev);
|
|
|
|
|
|
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
|
|
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
|
|
- kfree(conf->hash_table);
|
|
|
|
- kfree(conf);
|
|
|
|
|
|
+ do {
|
|
|
|
+ linear_conf_t *t = conf->prev;
|
|
|
|
+ kfree(conf->hash_table);
|
|
|
|
+ kfree(conf);
|
|
|
|
+ conf = t;
|
|
|
|
+ } while (conf);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality =
|
|
.run = linear_run,
|
|
.run = linear_run,
|
|
.stop = linear_stop,
|
|
.stop = linear_stop,
|
|
.status = linear_status,
|
|
.status = linear_status,
|
|
|
|
+ .hot_add_disk = linear_add,
|
|
};
|
|
};
|
|
|
|
|
|
static int __init linear_init (void)
|
|
static int __init linear_init (void)
|