diff options
author | David Zeuthen <zeuthen@google.com> | 2016-10-03 13:46:15 -0400 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2016-10-03 13:49:10 -0400 |
commit | 4ecd03a43349a6dab58bc7f964de13698a5cef0e (patch) | |
tree | ada5edab2736179b2c54de37115e29151d404f06 | |
parent | d8c5fca5da707e5caf43ce71384328a77d5a81a6 (diff) | |
download | v4.4-4ecd03a43349a6dab58bc7f964de13698a5cef0e.tar.gz |
Bug: 31796270
Test: Manually tested on x86_64.
Signed-off-by: David Zeuthen <zeuthen@google.com>
Patchset: init-dm
Change-Id: I3688001cf69a2c82bc417482e2a538c76abd3618
-rw-r--r-- | include/linux/device-mapper.h | 4 | ||||
-rw-r--r-- | init/do_mounts_dm.c | 323 |
2 files changed, 184 insertions, 143 deletions
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 7c8a0c6e9f37..9ea85ecf3ab6 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -420,6 +420,10 @@ struct queue_limits *dm_get_queue_limits(struct mapped_device *md); */ void dm_lock_md_type(struct mapped_device *md); void dm_unlock_md_type(struct mapped_device *md); +void dm_set_md_type(struct mapped_device *md, unsigned type); +unsigned dm_get_md_type(struct mapped_device *md); +int dm_setup_md_queue(struct mapped_device *md); +unsigned dm_table_get_type(struct dm_table *t); /* * Geometry functions. diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index 7c201a51aa26..bce1c2fbb915 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -1,9 +1,9 @@ -/* - * do_mounts_dm.c +/* do_mounts_dm.c * Copyright (C) 2010 The Chromium OS Authors <chromium-os-dev@chromium.org> + * All Rights Reserved. * Based on do_mounts_md.c * - * This file is released under the GPLv2. + * This file is released under the GPL. */ #include <linux/async.h> #include <linux/ctype.h> @@ -21,23 +21,58 @@ #define DM_NO_UUID "none" #define DM_MSG_PREFIX "init" -#define DMERR_PARSE(fmt, args...) \ - DMERR("failed to parse " fmt " for device %s<%lu>", args) /* Separators used for parsing the dm= argument. */ #define DM_FIELD_SEP " " #define DM_LINE_SEP "," #define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP -/* See Documentation/device-mapper/boot.txt for dm="..." format details. */ +/* + * When the device-mapper and any targets are compiled into the kernel + * (not a module), one or more device-mappers may be created and used + * as the root device at boot time with the parameters given with the + * boot line dm=... + * + * Multiple device-mappers can be stacked specifing the number of + * devices. A device can have multiple targets if the the number of + * targets is specified. + * + * TODO(taysom:defect 32847) + * In the future, the <num> field will be mandatory. + * + * <device> ::= [<num>] <device-mapper>+ + * <device-mapper> ::= <head> "," <target>+ + * <head> ::= <name> <uuid> <mode> [<num>] + * <target> ::= <start> <length> <type> <options> "," + * <mode> ::= "ro" | "rw" + * <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none" + * <type> ::= "verity" | "bootcache" | ... + * + * Example: + * 2 vboot none ro 1, + * 0 1768000 bootcache + * device=aa55b119-2a47-8c45-946a-5ac57765011f+1 + * signature=76e9be054b15884a9fa85973e9cb274c93afadb6 + * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000, + * vroot none ro 1, + * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1 + * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6 + * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe + * + * Notes: + * 1. uuid is a label for the device and we set it to "none". + * 2. The <num> field will be optional initially and assumed to be 1. + * Once all the scripts that set these fields have been set, it will + * be made mandatory. + */ -struct dm_setup_table { +struct dm_setup_target { sector_t begin; sector_t length; char *type; char *params; /* simple singly linked list */ - struct dm_setup_table *next; + struct dm_setup_target *next; }; struct dm_device { @@ -45,9 +80,9 @@ struct dm_device { int ro; char name[DM_MAX_NAME]; char uuid[DM_MAX_UUID]; - unsigned long num_tables; - struct dm_setup_table *table; - int table_count; + unsigned long num_targets; + struct dm_setup_target *target; + int target_count; struct dm_device *next; }; @@ -63,7 +98,7 @@ static struct { char *str; } dm_setup_args __initdata; -static int dm_early_setup __initdata; +static __initdata int dm_early_setup; static int __init get_dm_option(struct dm_option *opt, const char *accept) { @@ -92,52 +127,37 @@ static int __init get_dm_option(struct dm_option *opt, const char *accept) return opt->len != 0; } -static int __init get_dm_option_u64(struct dm_option *opt, const char *sep, - unsigned long long *result) -{ - char buf[32]; - - if (!get_dm_option(opt, sep)) - return -EINVAL; - - strlcpy(buf, opt->start, min(sizeof(buf), opt->len + 1)); - return kstrtoull(buf, 0, result); -} - -static void __init dm_setup_cleanup(struct dm_device *devices) +static int __init dm_setup_cleanup(struct dm_device *devices) { struct dm_device *dev = devices; while (dev) { struct dm_device *old_dev = dev; - struct dm_setup_table *table = dev->table; - - while (table) { - struct dm_setup_table *old_table = table; - - kfree(table->type); - kfree(table->params); - table = table->next; - kfree(old_table); - dev->table_count--; + struct dm_setup_target *target = dev->target; + while (target) { + struct dm_setup_target *old_target = target; + kfree(target->type); + kfree(target->params); + target = target->next; + kfree(old_target); + dev->target_count--; } - WARN_ON(dev->table_count); + BUG_ON(dev->target_count); dev = dev->next; kfree(old_dev); } + return 0; } -static char * __init dm_parse_device(struct dm_device *dev, char *str, - unsigned long idx) +static char * __init dm_parse_device(struct dm_device *dev, char *str) { struct dm_option opt; size_t len; - unsigned long long num_tables; /* Grab the logical name of the device to be exported to udev */ opt.next = str; if (!get_dm_option(&opt, DM_FIELD_SEP)) { - DMERR_PARSE("name", "", idx); + DMERR("failed to parse device name"); goto parse_fail; } len = min(opt.len + 1, sizeof(dev->name)); @@ -145,7 +165,7 @@ static char * __init dm_parse_device(struct dm_device *dev, char *str, /* Grab the UUID value or "none" */ if (!get_dm_option(&opt, DM_FIELD_SEP)) { - DMERR_PARSE("uuid", dev->name, idx); + DMERR("failed to parse device uuid"); goto parse_fail; } len = min(opt.len + 1, sizeof(dev->uuid)); @@ -158,84 +178,81 @@ static char * __init dm_parse_device(struct dm_device *dev, char *str, } else if (!strncmp("rw", opt.start, opt.len)) { dev->ro = 0; } else { - DMERR_PARSE("table mode", dev->name, idx); + DMERR("failed to parse table mode"); goto parse_fail; } /* Optional number field */ + /* XXX: The <num> field will be mandatory in the next round */ if (opt.delim == DM_FIELD_SEP[0]) { - if (get_dm_option_u64(&opt, DM_LINE_SEP, &num_tables)) { - DMERR_PARSE("number of tables", dev->name, idx); - goto parse_fail; - } + if (!get_dm_option(&opt, DM_LINE_SEP)) + return NULL; + dev->num_targets = simple_strtoul(opt.start, NULL, 10); } else { - num_tables = 1; + dev->num_targets = 1; } - if (num_tables > DM_MAX_TARGETS) { - DMERR_PARSE("too many tables (%llu > %d)", num_tables, - DM_MAX_TARGETS, dev->name, idx); + if (dev->num_targets > DM_MAX_TARGETS) { + DMERR("too many targets %lu > %d", + dev->num_targets, DM_MAX_TARGETS); } - dev->num_tables = num_tables; - return opt.next; parse_fail: return NULL; } -static char * __init dm_parse_tables(struct dm_device *dev, char *str, - unsigned long idx) +static char * __init dm_parse_targets(struct dm_device *dev, char *str) { struct dm_option opt; - struct dm_setup_table **table = &dev->table; - unsigned long num_tables = dev->num_tables; + struct dm_setup_target **target = &dev->target; + unsigned long num_targets = dev->num_targets; unsigned long i; - unsigned long long value; - /* - * Tables are defined as per the normal table format but with a - * comma as a newline separator. - */ + /* Targets are defined as per the table format but with a + * comma as a newline separator. */ opt.next = str; - for (i = 0; i < num_tables; i++) { - *table = kzalloc(sizeof(struct dm_setup_table), GFP_KERNEL); - if (!*table) { - DMERR_PARSE("table %lu (out of memory)", i, dev->name, - idx); + for (i = 0; i < num_targets; i++) { + *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); + if (!*target) { + DMERR("failed to allocate memory for target %s<%ld>", + dev->name, i); goto parse_fail; } - dev->table_count++; + dev->target_count++; - if (get_dm_option_u64(&opt, DM_FIELD_SEP, &value)) { - DMERR_PARSE("starting sector for table %lu", i, - dev->name, idx); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse starting sector" + " for target %s<%ld>", dev->name, i); goto parse_fail; } - (*table)->begin = value; + (*target)->begin = simple_strtoull(opt.start, NULL, 10); - if (get_dm_option_u64(&opt, DM_FIELD_SEP, &value)) { - DMERR_PARSE("length for table %lu", i, dev->name, idx); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse length for target %s<%ld>", + dev->name, i); goto parse_fail; } - (*table)->length = value; + (*target)->length = simple_strtoull(opt.start, NULL, 10); if (get_dm_option(&opt, DM_FIELD_SEP)) - (*table)->type = kstrndup(opt.start, opt.len, + (*target)->type = kstrndup(opt.start, opt.len, GFP_KERNEL); - if (!((*table)->type)) { - DMERR_PARSE("type for table %lu", i, dev->name, idx); + if (!((*target)->type)) { + DMERR("failed to parse type for target %s<%ld>", + dev->name, i); goto parse_fail; } if (get_dm_option(&opt, DM_LINE_SEP)) - (*table)->params = kstrndup(opt.start, opt.len, - GFP_KERNEL); - if (!((*table)->params)) { - DMERR_PARSE("params for table %lu", i, dev->name, idx); + (*target)->params = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->params)) { + DMERR("failed to parse params for target %s<%ld>", + dev->name, i); goto parse_fail; } - table = &((*table)->next); + target = &((*target)->next); } - DMDEBUG("tables parsed: %d", dev->table_count); + DMDEBUG("parsed %d targets", dev->target_count); return opt.next; @@ -257,7 +274,7 @@ static struct dm_device * __init dm_parse_args(void) for (i = 0; i < num_devices; i++) { dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { - DMERR("failed to allocated memory for device %lu", i); + DMERR("failed to allocated memory for dev"); goto error; } *tail = dev; @@ -268,11 +285,11 @@ static struct dm_device * __init dm_parse_args(void) * string. */ dev->minor = i; - str = dm_parse_device(dev, str, i); + str = dm_parse_device(dev, str); if (!str) /* NULL indicates error in parsing, bail */ goto error; - str = dm_parse_tables(dev, str, i); + str = dm_parse_targets(dev, str); if (!str) goto error; } @@ -295,27 +312,29 @@ error: static int __init dm_setup(char *str) { struct dm_option opt; - unsigned long long num_devices; + unsigned long num_devices; if (!str) { - DMERR("setup str is NULL"); + DMDEBUG("str is NULL"); goto parse_fail; } - - DMDEBUG("Want to parse \"%s\"", str); opt.next = str; - if (get_dm_option_u64(&opt, DM_FIELD_SEP, &num_devices)) + if (!get_dm_option(&opt, DM_FIELD_SEP)) goto parse_fail; - str = opt.next; + if (isdigit(opt.start[0])) { /* XXX: Optional number field */ + num_devices = simple_strtoul(opt.start, NULL, 10); + str = opt.next; + } else { + num_devices = 1; + /* Don't advance str */ + } if (num_devices > DM_MAX_DEVICES) { - DMERR("too many devices %llu > %d", num_devices, - DM_MAX_DEVICES); + DMDEBUG("too many devices %lu > %d", + num_devices, DM_MAX_DEVICES); } - dm_setup_args.num_devices = num_devices; dm_setup_args.str = str; - - DMINFO("will configure %lu device%s", dm_setup_args.num_devices, - dm_setup_args.num_devices == 1 ? "" : "s"); + dm_setup_args.num_devices = num_devices; + DMINFO("will configure %lu devices", num_devices); dm_early_setup = 1; return 1; @@ -327,8 +346,8 @@ parse_fail: static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; - struct dm_table *tables = NULL; - struct dm_setup_table *table; + struct dm_table *table = NULL; + struct dm_setup_target *target; struct dm_device *dev; char *uuid; fmode_t fmode = FMODE_READ; @@ -338,8 +357,8 @@ static void __init dm_setup_drives(void) for (dev = devices; dev; dev = dev->next) { if (dm_create(dev->minor, &md)) { - DMERR("failed to create device %s", dev->name); - goto fail; + DMDEBUG("failed to create the device"); + goto dm_create_fail; } DMDEBUG("created device '%s'", dm_device_name(md)); @@ -351,74 +370,92 @@ static void __init dm_setup_drives(void) if (!dev->ro) fmode |= FMODE_WRITE; - if (dm_table_create(&tables, fmode, dev->table_count, md)) { - DMERR("failed to create device %s tables", dev->name); - goto fail_put; + if (dm_table_create(&table, fmode, dev->target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; } - for (table = dev->table; table; table = table->next) { - DMINFO("device %s adding table '%llu %llu %s %s'", - dev->name, - (unsigned long long) table->begin, - (unsigned long long) table->length, - table->type, table->params); - if (dm_table_add_target(tables, table->type, - table->begin, - table->length, - table->params)) { - DMERR("failed to add table to device %s", - dev->name); - goto fail_put; + + dm_lock_md_type(md); + + for (target = dev->target; target; target = target->next) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, + target->type, target->params); + if (dm_table_add_target(table, target->type, + target->begin, + target->length, + target->params)) { + DMDEBUG("failed to add the target" + " to the table"); + goto add_target_fail; } } - dm_lock_md_type(md); - if (dm_table_complete(tables)) { - DMERR("failed to complete device %s tables", - dev->name); - dm_unlock_md_type(md); - goto fail_put; + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; } - dm_unlock_md_type(md); - /* Suspend the device so that we can bind it to the tables. */ + /* Suspend the device so that we can bind it to the table. */ if (dm_suspend(md, 0)) { - DMERR("failed to suspend device %s pre-bind", - dev->name); - goto fail_put; + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } + + /* Initial table load: acquire type of table. */ + dm_set_md_type(md, dm_table_get_type(table)); + + /* Setup md->queue to reflect md's type. */ + if (dm_setup_md_queue(md)) { + DMWARN("unable to set up device queue for new table."); + goto setup_md_queue_fail; } /* - * Bind the tables to the device. This is the only way - * to associate md->map with the tables and set the disk + * Bind the table to the device. This is the only way + * to associate md->map with the table and set the disk * capacity directly. */ - if (dm_swap_table(md, tables)) { /* should return NULL. */ - DMERR("failed to bind device %s to tables", - dev->name); - goto fail_put; + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; } /* Finally, resume and the device should be ready. */ if (dm_resume(md)) { - DMERR("failed to resume device %s", dev->name); - goto fail_put; + DMDEBUG("failed to resume the device"); + goto resume_fail; } /* Export the dm device via the ioctl interface */ if (!strcmp(DM_NO_UUID, dev->uuid)) uuid = NULL; if (dm_ioctl_export(md, dev->name, uuid)) { - DMERR("failed to export device %s", dev->name); - goto fail_put; + DMDEBUG("failed to export device with given" + " name and uuid"); + goto export_fail; } - DMINFO("dm-%d (%s) is ready", dev->minor, dev->name); + + dm_unlock_md_type(md); + + DMINFO("dm-%d is ready", dev->minor); } dm_setup_cleanup(devices); return; -fail_put: +export_fail: +resume_fail: +table_bind_fail: +setup_md_queue_fail: +suspend_fail: +table_complete_fail: +add_target_fail: + dm_unlock_md_type(md); +dm_table_create_fail: dm_put(md); -fail: - DMERR("starting dm-%d (%s) failed", dev->minor, dev->name); +dm_create_fail: + DMWARN("starting dm-%d (%s) failed", + dev->minor, dev->name); dm_setup_cleanup(devices); } |