diff --git a/Documentation/admin-guide/md.rst b/Documentation/admin-guide/md.rst index 4ff2cc291d18..277bf0cdd84a 100644 --- a/Documentation/admin-guide/md.rst +++ b/Documentation/admin-guide/md.rst @@ -80,8 +80,7 @@ Boot time autodetection of RAID arrays When md is compiled into the kernel (not as module), partitions of type 0xfd are scanned and automatically assembled into RAID arrays. This autodetection may be suppressed with the kernel parameter -``raid=noautodetect``. As of kernel 2.6.9, only drives with a type 0 -superblock can be autodetected and run at boot time. +``raid=noautodetect``. The kernel parameter ``raid=partitionable`` (or ``raid=part``) means that all auto-detected arrays are assembled as partitionable. diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index 4b80165afd23..c677e90a6c62 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -198,22 +198,48 @@ static void __init md_setup_drive(struct md_setup_args *args) ainfo.raid_disks++; } - err = md_set_array_info(mddev, &ainfo); - - for (i = 0; i <= MD_SB_DISKS && devices[i]; i++) { - struct mdu_disk_info_s dinfo = { - .major = MAJOR(devices[i]), - .minor = MINOR(devices[i]), - }; - - if (args->level != LEVEL_NONE) { - dinfo.number = i; - dinfo.raid_disk = i; - dinfo.state = - (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC); - } + if (args->level != LEVEL_NONE) { + err = md_set_array_info(mddev, &ainfo); + for (i = 0; i <= MD_SB_DISKS && devices[i]; i++) { + struct mdu_disk_info_s dinfo = { + .major = MAJOR(devices[i]), + .minor = MINOR(devices[i]), + }; + + if (args->level != LEVEL_NONE) { + dinfo.number = i; + dinfo.raid_disk = i; + dinfo.state = + (1 << MD_DISK_ACTIVE) | (1 << MD_DISK_SYNC); + } - md_add_new_disk(mddev, &dinfo); + md_add_new_disk(mddev, &dinfo); + } + } else { + ainfo.major_version = -1; + ainfo.minor_version = -1; + for (i = 0; i <= MD_SB_DISKS && devices[i]; i++) { + struct md_rdev *rdev = NULL; + + if (ainfo.major_version < 0) { + rdev = md_guess_super_import_device(devices[i]); + if (rdev == NULL) + continue; + ainfo.major_version = rdev->sb_major_version; + ainfo.minor_version = rdev->sb_minor_version; + err = md_set_array_info(mddev, &ainfo); + if (err) { + pr_warn("md: couldn't update array info. %d\n", err); + break; + } + } else { + rdev = md_import_device(devices[i], + ainfo.major_version, ainfo.minor_version); + if (rdev == NULL) + continue; + } + md_autodetect_bind_export_rdev(rdev, mddev); + } } if (!err) diff --git a/drivers/md/md.c b/drivers/md/md.c index 09042b060086..f16a5466195d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2521,6 +2521,15 @@ static void export_rdev(struct md_rdev *rdev, struct mddev *mddev) kobject_put(&rdev->kobj); } +int md_autodetect_bind_export_rdev(struct md_rdev *rdev, struct mddev *mddev) +{ + int err = bind_rdev_to_array(rdev, mddev); + + if (err) + export_rdev(rdev, mddev); + return err; +} + static void md_kick_rdev_from_array(struct md_rdev *rdev) { struct mddev *mddev = rdev->mddev; @@ -3686,7 +3695,7 @@ EXPORT_SYMBOL_GPL(md_rdev_init); * * a faulty rdev _never_ has rdev->sb set. */ -static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor) +struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor) { struct md_rdev *rdev; sector_t size; @@ -3740,10 +3749,13 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe } } + rdev->sb_major_version = super_format; + rdev->sb_minor_version = super_minor; + return rdev; out_blkdev_put: - fput(rdev->bdev_file); + bdev_fput(rdev->bdev_file); out_clear_rdev: md_rdev_clear(rdev); out_free_rdev: @@ -6695,6 +6707,11 @@ static void autorun_devices(int part) { struct md_rdev *rdev0, *rdev, *tmp; struct mddev *mddev; + /* + * Version 1 superblocks don't store a preferred minor number, + * assign a high one here so we get no conflicts + */ + int preferred_minor_1 = 0xffff; pr_info("md: autorun ...\n"); while (!list_empty(&pending_raid_disks)) { @@ -6706,28 +6723,50 @@ static void autorun_devices(int part) pr_debug("md: considering %pg ...\n", rdev0->bdev); INIT_LIST_HEAD(&candidates); - rdev_for_each_list(rdev, tmp, &pending_raid_disks) - if (super_90_load(rdev, rdev0, 0) >= 0) { + rdev_for_each_list(rdev, tmp, &pending_raid_disks) { + if (rdev0->sb_major_version != rdev->sb_major_version || + rdev0->sb_minor_version != rdev->sb_minor_version) { + pr_debug("md: Versions don't match with %pg ...\n", + rdev->bdev); + continue; + } + if (super_types[rdev->sb_major_version].load_super(rdev, + rdev0, + rdev0->sb_minor_version + ) >= 0) { pr_debug("md: adding %pg ...\n", rdev->bdev); list_move(&rdev->same_set, &candidates); } + } /* * now we have a set of devices, with all of them having * mostly sane superblocks. It's time to allocate the * mddev. */ + + int minor = rdev0->preferred_minor; + + if (rdev0->sb_major_version == 1) { + if (preferred_minor_1 < 0) { + pr_warn("md: no free minor number left for v1 superblock\n"); + break; + } + minor = preferred_minor_1; + preferred_minor_1--; + } + if (part) { dev = MKDEV(mdp_major, - rdev0->preferred_minor << MdpMinorShift); + minor << MdpMinorShift); unit = MINOR(dev) >> MdpMinorShift; } else { - dev = MKDEV(MD_MAJOR, rdev0->preferred_minor); + dev = MKDEV(MD_MAJOR, minor); unit = MINOR(dev); } - if (rdev0->preferred_minor != unit) { + if (minor != unit) { pr_warn("md: unit number in %pg is bad: %d\n", - rdev0->bdev, rdev0->preferred_minor); + rdev0->bdev, minor); break; } @@ -6745,6 +6784,10 @@ static void autorun_devices(int part) } else { pr_debug("md: created %s\n", mdname(mddev)); mddev->persistent = 1; + + mddev->major_version = rdev0->sb_major_version; + mddev->minor_version = rdev0->sb_minor_version; + rdev_for_each_list(rdev, tmp, &candidates) { list_del_init(&rdev->same_set); if (bind_rdev_to_array(rdev, mddev)) @@ -10261,6 +10304,32 @@ void md_autodetect_dev(dev_t dev) } } +struct md_sb_type { + int major; + int minor; +}; + +static const struct md_sb_type super_versions[4] = { + {0, 90}, + {1, 2}, + {1, 1}, + {1, 0} +}; + +struct md_rdev *md_guess_super_import_device(dev_t dev) +{ + const struct md_sb_type *super; + struct md_rdev *rdev; + + for (int i = 0; i < ARRAY_SIZE(super_versions); i++) { + super = &super_versions[i]; + rdev = md_import_device(dev, super->major, super->minor); + if (!IS_ERR_OR_NULL(rdev)) + return rdev; + } + return NULL; /* No valid superblock found */ +} + void md_autostart_arrays(int part) { struct md_rdev *rdev; @@ -10282,9 +10351,9 @@ void md_autostart_arrays(int part) dev = node_detected_dev->dev; kfree(node_detected_dev); mutex_unlock(&detected_devices_mutex); - rdev = md_import_device(dev,0, 90); + rdev = md_guess_super_import_device(dev); mutex_lock(&detected_devices_mutex); - if (IS_ERR(rdev)) + if (rdev == NULL) continue; if (test_bit(Faulty, &rdev->flags)) diff --git a/drivers/md/md.h b/drivers/md/md.h index d45a9e6ead80..424286f06a66 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -145,6 +145,7 @@ struct md_rdev { struct page *sb_page, *bb_page; int sb_loaded; + int sb_major_version, sb_minor_version; __u64 sb_events; sector_t data_offset; /* start of data in array */ sector_t new_data_offset;/* only relevant while reshaping */ @@ -899,6 +900,7 @@ extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev); extern void md_set_array_sectors(struct mddev *mddev, sector_t array_sectors); extern int md_check_no_bitmap(struct mddev *mddev); extern int md_integrity_register(struct mddev *mddev); +int md_autodetect_bind_export_rdev(struct md_rdev *rdev, struct mddev *mddev); extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale); extern int mddev_init(struct mddev *mddev); @@ -911,6 +913,7 @@ extern int md_start(struct mddev *mddev); extern void md_stop(struct mddev *mddev); extern void md_stop_writes(struct mddev *mddev); extern int md_rdev_init(struct md_rdev *rdev); +struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor); extern void md_rdev_clear(struct md_rdev *rdev); extern bool md_handle_request(struct mddev *mddev, struct bio *bio); @@ -992,6 +995,7 @@ struct mdu_disk_info_s; extern int mdp_major; extern struct workqueue_struct *md_bitmap_wq; +struct md_rdev *md_guess_super_import_device(dev_t dev); void md_autostart_arrays(int part); int md_set_array_info(struct mddev *mddev, struct mdu_array_info_s *info); int md_add_new_disk(struct mddev *mddev, struct mdu_disk_info_s *info);