[OpenWrt-Devel] [PATCH v2 10/12] target/linux/generic: backport patches adding DM_INIT functionality
Thomas Petazzoni
thomas.petazzoni at bootlin.com
Thu Nov 21 11:23:20 EST 2019
The new DM_INIT functionality, merged in upstream Linux 5.1, allows to
setup a device mapper target at boot time. It avoids the need to use
an initramfs to setup a device mapper target. This is useful in the
context of supporting dm-verity in OpenWRT.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni at bootlin.com>
---
...-to-directly-boot-to-a-mapped-device.patch | 668 ++++++++++++++++++
...-init-fix-max-devices-targets-checks.patch | 48 ++
...hang-in-early-create-error-condition.patch | 49 ++
...ion-dm-init-fix-multi-device-example.patch | 45 ++
...-to-directly-boot-to-a-mapped-device.patch | 668 ++++++++++++++++++
...-init-fix-max-devices-targets-checks.patch | 48 ++
...hang-in-early-create-error-condition.patch | 49 ++
...ion-dm-init-fix-multi-device-example.patch | 45 ++
8 files changed, 1620 insertions(+)
create mode 100644 target/linux/generic/backport-4.14/390-dm-add-support-to-directly-boot-to-a-mapped-device.patch
create mode 100644 target/linux/generic/backport-4.14/391-dm-init-fix-max-devices-targets-checks.patch
create mode 100644 target/linux/generic/backport-4.14/392-dm-ioctl-fix-hang-in-early-create-error-condition.patch
create mode 100644 target/linux/generic/backport-4.14/393-Documentation-dm-init-fix-multi-device-example.patch
create mode 100644 target/linux/generic/backport-4.19/400-dm-add-support-to-directly-boot-to-a-mapped-device.patch
create mode 100644 target/linux/generic/backport-4.19/401-dm-init-fix-max-devices-targets-checks.patch
create mode 100644 target/linux/generic/backport-4.19/402-dm-ioctl-fix-hang-in-early-create-error-condition.patch
create mode 100644 target/linux/generic/backport-4.19/403-Documentation-dm-init-fix-multi-device-example.patch
diff --git a/target/linux/generic/backport-4.14/390-dm-add-support-to-directly-boot-to-a-mapped-device.patch b/target/linux/generic/backport-4.14/390-dm-add-support-to-directly-boot-to-a-mapped-device.patch
new file mode 100644
index 0000000000..291dbd783d
--- /dev/null
+++ b/target/linux/generic/backport-4.14/390-dm-add-support-to-directly-boot-to-a-mapped-device.patch
@@ -0,0 +1,668 @@
+From d2f5bf5f2df9c9993564e4a03187f6aa79b58cc4 Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Thu, 21 Feb 2019 17:33:34 -0300
+Subject: [PATCH 1/4] dm: add support to directly boot to a mapped device
+
+Add a "create" module parameter, which allows device-mapper targets to
+be configured at boot time. This enables early use of DM targets in the
+boot process (as the root device or otherwise) without the need of an
+initramfs.
+
+The syntax used in the boot param is based on the concise format from
+the dmsetup tool to follow the rule of least surprise:
+
+ dmsetup table --concise /dev/mapper/lroot
+
+Which is:
+ dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
+
+Where,
+ <name> ::= The device name.
+ <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
+ <minor> ::= The device minor number | ""
+ <flags> ::= "ro" | "rw"
+ <table> ::= <start_sector> <num_sectors> <target_type> <target_args>
+ <target_type> ::= "verity" | "linear" | ...
+
+For example, the following could be added in the boot parameters:
+dm-mod.create="lroot,,,rw, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" root=/dev/dm-0
+
+Only the targets that were tested are allowed and the ones that don't
+change any block device when the device is create as read-only. For
+example, mirror and cache targets are not allowed. The rationale behind
+this is that if the user makes a mistake, choosing the wrong device to
+be the mirror or the cache can corrupt data.
+
+The only targets initially allowed are:
+* crypt
+* delay
+* linear
+* snapshot-origin
+* striped
+* verity
+
+Co-developed-by: Will Drewry <wad at chromium.org>
+Co-developed-by: Kees Cook <keescook at chromium.org>
+Co-developed-by: Enric Balletbo i Serra <enric.balletbo at collabora.com>
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Reviewed-by: Kees Cook <keescook at chromium.org>
+Signed-off-by: Mike Snitzer <snitzer at redhat.com>
+---
+ Documentation/device-mapper/dm-init.txt | 114 +++++++++
+ drivers/md/Kconfig | 12 +
+ drivers/md/Makefile | 4 +
+ drivers/md/dm-init.c | 303 ++++++++++++++++++++++++
+ drivers/md/dm-ioctl.c | 103 ++++++++
+ include/linux/device-mapper.h | 9 +
+ 6 files changed, 545 insertions(+)
+ create mode 100644 Documentation/device-mapper/dm-init.txt
+ create mode 100644 drivers/md/dm-init.c
+
+diff --git a/Documentation/device-mapper/dm-init.txt b/Documentation/device-mapper/dm-init.txt
+new file mode 100644
+index 000000000000..8464ee7c01b8
+--- /dev/null
++++ b/Documentation/device-mapper/dm-init.txt
+@@ -0,0 +1,114 @@
++Early creation of mapped devices
++====================================
++
++It is possible to configure a device-mapper device to act as the root device for
++your system in two ways.
++
++The first is to build an initial ramdisk which boots to a minimal userspace
++which configures the device, then pivot_root(8) in to it.
++
++The second is to create one or more device-mappers using the module parameter
++"dm-mod.create=" through the kernel boot command line argument.
++
++The format is specified as a string of data separated by commas and optionally
++semi-colons, where:
++ - a comma is used to separate fields like name, uuid, flags and table
++ (specifies one device)
++ - a semi-colon is used to separate devices.
++
++So the format will look like this:
++
++ dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
++
++Where,
++ <name> ::= The device name.
++ <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
++ <minor> ::= The device minor number | ""
++ <flags> ::= "ro" | "rw"
++ <table> ::= <start_sector> <num_sectors> <target_type> <target_args>
++ <target_type> ::= "verity" | "linear" | ... (see list below)
++
++The dm line should be equivalent to the one used by the dmsetup tool with the
++--concise argument.
++
++Target types
++============
++
++Not all target types are available as there are serious risks in allowing
++activation of certain DM targets without first using userspace tools to check
++the validity of associated metadata.
++
++ "cache": constrained, userspace should verify cache device
++ "crypt": allowed
++ "delay": allowed
++ "era": constrained, userspace should verify metadata device
++ "flakey": constrained, meant for test
++ "linear": allowed
++ "log-writes": constrained, userspace should verify metadata device
++ "mirror": constrained, userspace should verify main/mirror device
++ "raid": constrained, userspace should verify metadata device
++ "snapshot": constrained, userspace should verify src/dst device
++ "snapshot-origin": allowed
++ "snapshot-merge": constrained, userspace should verify src/dst device
++ "striped": allowed
++ "switch": constrained, userspace should verify dev path
++ "thin": constrained, requires dm target message from userspace
++ "thin-pool": constrained, requires dm target message from userspace
++ "verity": allowed
++ "writecache": constrained, userspace should verify cache device
++ "zero": constrained, not meant for rootfs
++
++If the target is not listed above, it is constrained by default (not tested).
++
++Examples
++========
++An example of booting to a linear array made up of user-mode linux block
++devices:
++
++ dm-mod.create="lroot,,,rw, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" root=/dev/dm-0
++
++This will boot to a rw dm-linear target of 8192 sectors split across two block
++devices identified by their major:minor numbers. After boot, udev will rename
++this target to /dev/mapper/lroot (depending on the rules). No uuid was assigned.
++
++An example of multiple device-mappers, with the dm-mod.create="..." contents is shown here
++split on multiple lines for readability:
++
++ vroot,,,ro,
++ 0 1740800 verity 254:0 254:0 1740800 sha1
++ 76e9be054b15884a9fa85973e9cb274c93afadb6
++ 5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe;
++ vram,,,rw,
++ 0 32768 linear 1:0 0,
++ 32768 32768 linear 1:1 0
++
++Other examples (per target):
++
++"crypt":
++ dm-crypt,,8,ro,
++ 0 1048576 crypt aes-xts-plain64
++ babebabebabebabebabebabebabebabebabebabebabebabebabebabebabebabe 0
++ /dev/sda 0 1 allow_discards
++
++"delay":
++ dm-delay,,4,ro,0 409600 delay /dev/sda1 0 500
++
++"linear":
++ dm-linear,,,rw,
++ 0 32768 linear /dev/sda1 0,
++ 32768 1024000 linear /dev/sda2 0,
++ 1056768 204800 linear /dev/sda3 0,
++ 1261568 512000 linear /dev/sda4 0
++
++"snapshot-origin":
++ dm-snap-orig,,4,ro,0 409600 snapshot-origin 8:2
++
++"striped":
++ dm-striped,,4,ro,0 1638400 striped 4 4096
++ /dev/sda1 0 /dev/sda2 0 /dev/sda3 0 /dev/sda4 0
++
++"verity":
++ dm-verity,,4,ro,
++ 0 1638400 verity 1 8:1 8:2 4096 4096 204800 1 sha256
++ fb1a5a0f00deb908d8b53cb270858975e76cf64105d412ce764225d53b8f3cfd
++ 51934789604d1b92399c52e7cb149d1b3a1b74bbbcb103b2a0aaacbed5c08584
+diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
+index 4a249ee86364..4ea706f7790b 100644
+--- a/drivers/md/Kconfig
++++ b/drivers/md/Kconfig
+@@ -428,6 +428,18 @@ config DM_DELAY
+
+ If unsure, say N.
+
++config DM_INIT
++ bool "DM \"dm-mod.create=\" parameter support"
++ depends on BLK_DEV_DM=y
++ ---help---
++ Enable "dm-mod.create=" parameter to create mapped devices at init time.
++ This option is useful to allow mounting rootfs without requiring an
++ initramfs.
++ See Documentation/device-mapper/dm-init.txt for dm-mod.create="..."
++ format.
++
++ If unsure, say N.
++
+ config DM_UEVENT
+ bool "DM uevents"
+ depends on BLK_DEV_DM
+diff --git a/drivers/md/Makefile b/drivers/md/Makefile
+index e94b6f9be941..d56331fbd895 100644
+--- a/drivers/md/Makefile
++++ b/drivers/md/Makefile
+@@ -64,6 +64,10 @@ obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
+ obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
+ obj-$(CONFIG_DM_ZONED) += dm-zoned.o
+
++ifeq ($(CONFIG_DM_INIT),y)
++dm-mod-objs += dm-init.o
++endif
++
+ ifeq ($(CONFIG_DM_UEVENT),y)
+ dm-mod-objs += dm-uevent.o
+ endif
+diff --git a/drivers/md/dm-init.c b/drivers/md/dm-init.c
+new file mode 100644
+index 000000000000..b53f30f16b4d
+--- /dev/null
++++ b/drivers/md/dm-init.c
+@@ -0,0 +1,303 @@
++// SPDX-License-Identifier: GPL-2.0
++
++/*
++ * dm-init.c
++ * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev at chromium.org>
++ *
++ * This file is released under the GPLv2.
++ */
++
++#include <linux/ctype.h>
++#include <linux/device.h>
++#include <linux/device-mapper.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/moduleparam.h>
++
++#define DM_MSG_PREFIX "init"
++#define DM_MAX_DEVICES 256
++#define DM_MAX_TARGETS 256
++#define DM_MAX_STR_SIZE 4096
++
++static char *create;
++
++/*
++ * Format: dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
++ * Table format: <start_sector> <num_sectors> <target_type> <target_args>
++ *
++ * See Documentation/device-mapper/dm-init.txt for dm-mod.create="..." format
++ * details.
++ */
++
++struct dm_device {
++ struct dm_ioctl dmi;
++ struct dm_target_spec *table[DM_MAX_TARGETS];
++ char *target_args_array[DM_MAX_TARGETS];
++ struct list_head list;
++};
++
++const char *dm_allowed_targets[] __initconst = {
++ "crypt",
++ "delay",
++ "linear",
++ "snapshot-origin",
++ "striped",
++ "verity",
++};
++
++static int __init dm_verify_target_type(const char *target)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(dm_allowed_targets); i++) {
++ if (!strcmp(dm_allowed_targets[i], target))
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static void __init dm_setup_cleanup(struct list_head *devices)
++{
++ struct dm_device *dev, *tmp;
++ unsigned int i;
++
++ list_for_each_entry_safe(dev, tmp, devices, list) {
++ list_del(&dev->list);
++ for (i = 0; i < dev->dmi.target_count; i++) {
++ kfree(dev->table[i]);
++ kfree(dev->target_args_array[i]);
++ }
++ kfree(dev);
++ }
++}
++
++/**
++ * str_field_delimit - delimit a string based on a separator char.
++ * @str: the pointer to the string to delimit.
++ * @separator: char that delimits the field
++ *
++ * Find a @separator and replace it by '\0'.
++ * Remove leading and trailing spaces.
++ * Return the remainder string after the @separator.
++ */
++static char __init *str_field_delimit(char **str, char separator)
++{
++ char *s;
++
++ /* TODO: add support for escaped characters */
++ *str = skip_spaces(*str);
++ s = strchr(*str, separator);
++ /* Delimit the field and remove trailing spaces */
++ if (s)
++ *s = '\0';
++ *str = strim(*str);
++ return s ? ++s : NULL;
++}
++
++/**
++ * dm_parse_table_entry - parse a table entry
++ * @dev: device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * <start_sector> <num_sectors> <target_type> <target_args>[, ...]
++ *
++ * Return the remainder string after the table entry, i.e, after the comma which
++ * delimits the entry or NULL if reached the end of the string.
++ */
++static char __init *dm_parse_table_entry(struct dm_device *dev, char *str)
++{
++ const unsigned int n = dev->dmi.target_count - 1;
++ struct dm_target_spec *sp;
++ unsigned int i;
++ /* fields: */
++ char *field[4];
++ char *next;
++
++ field[0] = str;
++ /* Delimit first 3 fields that are separated by space */
++ for (i = 0; i < ARRAY_SIZE(field) - 1; i++) {
++ field[i + 1] = str_field_delimit(&field[i], ' ');
++ if (!field[i + 1])
++ return ERR_PTR(-EINVAL);
++ }
++ /* Delimit last field that can be terminated by comma */
++ next = str_field_delimit(&field[i], ',');
++
++ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
++ if (!sp)
++ return ERR_PTR(-ENOMEM);
++ dev->table[n] = sp;
++
++ /* start_sector */
++ if (kstrtoull(field[0], 0, &sp->sector_start))
++ return ERR_PTR(-EINVAL);
++ /* num_sector */
++ if (kstrtoull(field[1], 0, &sp->length))
++ return ERR_PTR(-EINVAL);
++ /* target_type */
++ strscpy(sp->target_type, field[2], sizeof(sp->target_type));
++ if (dm_verify_target_type(sp->target_type)) {
++ DMERR("invalid type \"%s\"", sp->target_type);
++ return ERR_PTR(-EINVAL);
++ }
++ /* target_args */
++ dev->target_args_array[n] = kstrndup(field[3], GFP_KERNEL,
++ DM_MAX_STR_SIZE);
++ if (!dev->target_args_array[n])
++ return ERR_PTR(-ENOMEM);
++
++ return next;
++}
++
++/**
++ * dm_parse_table - parse "dm-mod.create=" table field
++ * @dev: device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * <table>[,<table>+]
++ */
++static int __init dm_parse_table(struct dm_device *dev, char *str)
++{
++ char *table_entry = str;
++
++ while (table_entry) {
++ DMDEBUG("parsing table \"%s\"", str);
++ if (++dev->dmi.target_count >= DM_MAX_TARGETS) {
++ DMERR("too many targets %u > %d",
++ dev->dmi.target_count, DM_MAX_TARGETS);
++ return -EINVAL;
++ }
++ table_entry = dm_parse_table_entry(dev, table_entry);
++ if (IS_ERR(table_entry)) {
++ DMERR("couldn't parse table");
++ return PTR_ERR(table_entry);
++ }
++ }
++
++ return 0;
++}
++
++/**
++ * dm_parse_device_entry - parse a device entry
++ * @dev: device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * name,uuid,minor,flags,table[; ...]
++ *
++ * Return the remainder string after the table entry, i.e, after the semi-colon
++ * which delimits the entry or NULL if reached the end of the string.
++ */
++static char __init *dm_parse_device_entry(struct dm_device *dev, char *str)
++{
++ /* There are 5 fields: name,uuid,minor,flags,table; */
++ char *field[5];
++ unsigned int i;
++ char *next;
++
++ field[0] = str;
++ /* Delimit first 4 fields that are separated by comma */
++ for (i = 0; i < ARRAY_SIZE(field) - 1; i++) {
++ field[i+1] = str_field_delimit(&field[i], ',');
++ if (!field[i+1])
++ return ERR_PTR(-EINVAL);
++ }
++ /* Delimit last field that can be delimited by semi-colon */
++ next = str_field_delimit(&field[i], ';');
++
++ /* name */
++ strscpy(dev->dmi.name, field[0], sizeof(dev->dmi.name));
++ /* uuid */
++ strscpy(dev->dmi.uuid, field[1], sizeof(dev->dmi.uuid));
++ /* minor */
++ if (strlen(field[2])) {
++ if (kstrtoull(field[2], 0, &dev->dmi.dev))
++ return ERR_PTR(-EINVAL);
++ dev->dmi.flags |= DM_PERSISTENT_DEV_FLAG;
++ }
++ /* flags */
++ if (!strcmp(field[3], "ro"))
++ dev->dmi.flags |= DM_READONLY_FLAG;
++ else if (strcmp(field[3], "rw"))
++ return ERR_PTR(-EINVAL);
++ /* table */
++ if (dm_parse_table(dev, field[4]))
++ return ERR_PTR(-EINVAL);
++
++ return next;
++}
++
++/**
++ * dm_parse_devices - parse "dm-mod.create=" argument
++ * @devices: list of struct dm_device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * <device>[;<device>+]
++ */
++static int __init dm_parse_devices(struct list_head *devices, char *str)
++{
++ unsigned long ndev = 0;
++ struct dm_device *dev;
++ char *device = str;
++
++ DMDEBUG("parsing \"%s\"", str);
++ while (device) {
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++ list_add_tail(&dev->list, devices);
++
++ if (++ndev >= DM_MAX_DEVICES) {
++ DMERR("too many targets %u > %d",
++ dev->dmi.target_count, DM_MAX_TARGETS);
++ return -EINVAL;
++ }
++
++ device = dm_parse_device_entry(dev, device);
++ if (IS_ERR(device)) {
++ DMERR("couldn't parse device");
++ return PTR_ERR(device);
++ }
++ }
++
++ return 0;
++}
++
++/**
++ * dm_init_init - parse "dm-mod.create=" argument and configure drivers
++ */
++static int __init dm_init_init(void)
++{
++ struct dm_device *dev;
++ LIST_HEAD(devices);
++ char *str;
++ int r;
++
++ if (!create)
++ return 0;
++
++ if (strlen(create) >= DM_MAX_STR_SIZE) {
++ DMERR("Argument is too big. Limit is %d\n", DM_MAX_STR_SIZE);
++ return -EINVAL;
++ }
++ str = kstrndup(create, GFP_KERNEL, DM_MAX_STR_SIZE);
++ if (!str)
++ return -ENOMEM;
++
++ r = dm_parse_devices(&devices, str);
++ if (r)
++ goto out;
++
++ DMINFO("waiting for all devices to be available before creating mapped devices\n");
++ wait_for_device_probe();
++
++ list_for_each_entry(dev, &devices, list) {
++ if (dm_early_create(&dev->dmi, dev->table,
++ dev->target_args_array))
++ break;
++ }
++out:
++ kfree(str);
++ dm_setup_cleanup(&devices);
++ return r;
++}
++
++late_initcall(dm_init_init);
++
++module_param(create, charp, 0);
++MODULE_PARM_DESC(create, "Create a mapped device in early boot");
+diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
+index ca948155191a..b7e6c7311a93 100644
+--- a/drivers/md/dm-ioctl.c
++++ b/drivers/md/dm-ioctl.c
+@@ -2017,3 +2017,106 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
+
+ return r;
+ }
++
++
++/**
++ * dm_early_create - create a mapped device in early boot.
++ *
++ * @dmi: Contains main information of the device mapping to be created.
++ * @spec_array: array of pointers to struct dm_target_spec. Describes the
++ * mapping table of the device.
++ * @target_params_array: array of strings with the parameters to a specific
++ * target.
++ *
++ * Instead of having the struct dm_target_spec and the parameters for every
++ * target embedded at the end of struct dm_ioctl (as performed in a normal
++ * ioctl), pass them as arguments, so the caller doesn't need to serialize them.
++ * The size of the spec_array and target_params_array is given by
++ * @dmi->target_count.
++ * This function is supposed to be called in early boot, so locking mechanisms
++ * to protect against concurrent loads are not required.
++ */
++int __init dm_early_create(struct dm_ioctl *dmi,
++ struct dm_target_spec **spec_array,
++ char **target_params_array)
++{
++ int r, m = DM_ANY_MINOR;
++ struct dm_table *t, *old_map;
++ struct mapped_device *md;
++ unsigned int i;
++
++ if (!dmi->target_count)
++ return -EINVAL;
++
++ r = check_name(dmi->name);
++ if (r)
++ return r;
++
++ if (dmi->flags & DM_PERSISTENT_DEV_FLAG)
++ m = MINOR(huge_decode_dev(dmi->dev));
++
++ /* alloc dm device */
++ r = dm_create(m, &md);
++ if (r)
++ return r;
++
++ /* hash insert */
++ r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md);
++ if (r)
++ goto err_destroy_dm;
++
++ /* alloc table */
++ r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
++ if (r)
++ goto err_destroy_dm;
++
++ /* add targets */
++ for (i = 0; i < dmi->target_count; i++) {
++ r = dm_table_add_target(t, spec_array[i]->target_type,
++ (sector_t) spec_array[i]->sector_start,
++ (sector_t) spec_array[i]->length,
++ target_params_array[i]);
++ if (r) {
++ DMWARN("error adding target to table");
++ goto err_destroy_table;
++ }
++ }
++
++ /* finish table */
++ r = dm_table_complete(t);
++ if (r)
++ goto err_destroy_table;
++
++ md->type = dm_table_get_type(t);
++ /* setup md->queue to reflect md's type (may block) */
++ r = dm_setup_md_queue(md, t);
++ if (r) {
++ DMWARN("unable to set up device queue for new table.");
++ goto err_destroy_table;
++ }
++
++ /* Set new map */
++ dm_suspend(md, 0);
++ old_map = dm_swap_table(md, t);
++ if (IS_ERR(old_map)) {
++ r = PTR_ERR(old_map);
++ goto err_destroy_table;
++ }
++ set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG));
++
++ /* resume device */
++ r = dm_resume(md);
++ if (r)
++ goto err_destroy_table;
++
++ DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
++ dm_put(md);
++ return 0;
++
++err_destroy_table:
++ dm_table_destroy(t);
++err_destroy_dm:
++ dm_put(md);
++ dm_destroy(md);
++ return r;
++}
+diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
+index a5538433c927..990e7c2f84b1 100644
+--- a/include/linux/device-mapper.h
++++ b/include/linux/device-mapper.h
+@@ -10,6 +10,7 @@
+
+ #include <linux/bio.h>
+ #include <linux/blkdev.h>
++#include <linux/dm-ioctl.h>
+ #include <linux/math64.h>
+ #include <linux/ratelimit.h>
+
+@@ -457,6 +458,14 @@ void dm_remap_zone_report(struct dm_target *ti, struct bio *bio,
+ sector_t start);
+ union map_info *dm_get_rq_mapinfo(struct request *rq);
+
++/*
++ * Device mapper functions to parse and create devices specified by the
++ * parameter "dm-mod.create="
++ */
++int __init dm_early_create(struct dm_ioctl *dmi,
++ struct dm_target_spec **spec_array,
++ char **target_params_array);
++
+ struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+
+ /*
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.14/391-dm-init-fix-max-devices-targets-checks.patch b/target/linux/generic/backport-4.14/391-dm-init-fix-max-devices-targets-checks.patch
new file mode 100644
index 0000000000..59eb168b66
--- /dev/null
+++ b/target/linux/generic/backport-4.14/391-dm-init-fix-max-devices-targets-checks.patch
@@ -0,0 +1,48 @@
+From 4318792c96a76cf6ea2ae62afb1b045301d96a5c Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Fri, 26 Apr 2019 17:09:55 -0300
+Subject: [PATCH 2/4] dm init: fix max devices/targets checks
+
+dm-init should allow up to DM_MAX_{DEVICES,TARGETS} for devices/targets,
+and not DM_MAX_{DEVICES,TARGETS} - 1.
+
+Fix the checks and also fix the error message when the number of devices
+is surpassed.
+
+Fixes: 6bbc923dfcf57d ("dm: add support to directly boot to a mapped device")
+Cc: stable at vger.kernel.org
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Signed-off-by: Mike Snitzer <snitzer at redhat.com>
+---
+ drivers/md/dm-init.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/md/dm-init.c b/drivers/md/dm-init.c
+index b53f30f16b4d..2e791ee6779f 100644
+--- a/drivers/md/dm-init.c
++++ b/drivers/md/dm-init.c
+@@ -160,7 +160,7 @@ static int __init dm_parse_table(struct dm_device *dev, char *str)
+
+ while (table_entry) {
+ DMDEBUG("parsing table \"%s\"", str);
+- if (++dev->dmi.target_count >= DM_MAX_TARGETS) {
++ if (++dev->dmi.target_count > DM_MAX_TARGETS) {
+ DMERR("too many targets %u > %d",
+ dev->dmi.target_count, DM_MAX_TARGETS);
+ return -EINVAL;
+@@ -242,9 +242,9 @@ static int __init dm_parse_devices(struct list_head *devices, char *str)
+ return -ENOMEM;
+ list_add_tail(&dev->list, devices);
+
+- if (++ndev >= DM_MAX_DEVICES) {
+- DMERR("too many targets %u > %d",
+- dev->dmi.target_count, DM_MAX_TARGETS);
++ if (++ndev > DM_MAX_DEVICES) {
++ DMERR("too many devices %lu > %d",
++ ndev, DM_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.14/392-dm-ioctl-fix-hang-in-early-create-error-condition.patch b/target/linux/generic/backport-4.14/392-dm-ioctl-fix-hang-in-early-create-error-condition.patch
new file mode 100644
index 0000000000..1a80b6d97d
--- /dev/null
+++ b/target/linux/generic/backport-4.14/392-dm-ioctl-fix-hang-in-early-create-error-condition.patch
@@ -0,0 +1,49 @@
+From c78ff5467e0f5c4d001b7ac80209441c6c107b26 Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Wed, 15 May 2019 13:50:54 -0300
+Subject: [PATCH 3/4] dm ioctl: fix hang in early create error condition
+
+The dm_early_create() function (which deals with "dm-mod.create=" kernel
+command line option) calls dm_hash_insert() who gets an extra reference
+to the md object.
+
+In case of failure, this reference wasn't being released, causing
+dm_destroy() to hang, thus hanging the whole boot process.
+
+Fix this by calling __hash_remove() in the error path.
+
+Fixes: 6bbc923dfcf57d ("dm: add support to directly boot to a mapped device")
+Cc: stable at vger.kernel.org
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Signed-off-by: Mike Snitzer <snitzer at redhat.com>
+---
+ drivers/md/dm-ioctl.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
+index b7e6c7311a93..d7801688d7be 100644
+--- a/drivers/md/dm-ioctl.c
++++ b/drivers/md/dm-ioctl.c
+@@ -2068,7 +2068,7 @@ int __init dm_early_create(struct dm_ioctl *dmi,
+ /* alloc table */
+ r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
+ if (r)
+- goto err_destroy_dm;
++ goto err_hash_remove;
+
+ /* add targets */
+ for (i = 0; i < dmi->target_count; i++) {
+@@ -2115,6 +2115,10 @@ int __init dm_early_create(struct dm_ioctl *dmi,
+
+ err_destroy_table:
+ dm_table_destroy(t);
++err_hash_remove:
++ (void) __hash_remove(__get_name_cell(dmi->name));
++ /* release reference from __get_name_cell */
++ dm_put(md);
+ err_destroy_dm:
+ dm_put(md);
+ dm_destroy(md);
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.14/393-Documentation-dm-init-fix-multi-device-example.patch b/target/linux/generic/backport-4.14/393-Documentation-dm-init-fix-multi-device-example.patch
new file mode 100644
index 0000000000..7d0d1af13f
--- /dev/null
+++ b/target/linux/generic/backport-4.14/393-Documentation-dm-init-fix-multi-device-example.patch
@@ -0,0 +1,45 @@
+From 072ea8a94e81e9744d18034b3245ab530f8365fc Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Tue, 4 Jun 2019 15:27:19 -0300
+Subject: [PATCH 4/4] Documentation/dm-init: fix multi device example
+
+The example in the docs regarding multiple device-mappers is invalid (it
+has a wrong number of arguments), it's a left over from previous
+versions of the patch.
+Replace the example with an valid and tested one.
+
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Reviewed-by: Stephen Boyd <swboyd at chromium.org>
+Signed-off-by: Jonathan Corbet <corbet at lwn.net>
+---
+ Documentation/device-mapper/dm-init.txt | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/Documentation/device-mapper/dm-init.txt b/Documentation/device-mapper/dm-init.txt
+index 8464ee7c01b8..130b3c3679c5 100644
+--- a/Documentation/device-mapper/dm-init.txt
++++ b/Documentation/device-mapper/dm-init.txt
+@@ -74,13 +74,13 @@ this target to /dev/mapper/lroot (depending on the rules). No uuid was assigned.
+ An example of multiple device-mappers, with the dm-mod.create="..." contents is shown here
+ split on multiple lines for readability:
+
+- vroot,,,ro,
+- 0 1740800 verity 254:0 254:0 1740800 sha1
+- 76e9be054b15884a9fa85973e9cb274c93afadb6
+- 5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe;
+- vram,,,rw,
+- 0 32768 linear 1:0 0,
+- 32768 32768 linear 1:1 0
++ dm-linear,,1,rw,
++ 0 32768 linear 8:1 0,
++ 32768 1024000 linear 8:2 0;
++ dm-verity,,3,ro,
++ 0 1638400 verity 1 /dev/sdc1 /dev/sdc2 4096 4096 204800 1 sha256
++ ac87db56303c9c1da433d7209b5a6ef3e4779df141200cbd7c157dcb8dd89c42
++ 5ebfe87f7df3235b80a117ebc4078e44f55045487ad4a96581d1adb564615b51
+
+ Other examples (per target):
+
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.19/400-dm-add-support-to-directly-boot-to-a-mapped-device.patch b/target/linux/generic/backport-4.19/400-dm-add-support-to-directly-boot-to-a-mapped-device.patch
new file mode 100644
index 0000000000..291dbd783d
--- /dev/null
+++ b/target/linux/generic/backport-4.19/400-dm-add-support-to-directly-boot-to-a-mapped-device.patch
@@ -0,0 +1,668 @@
+From d2f5bf5f2df9c9993564e4a03187f6aa79b58cc4 Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Thu, 21 Feb 2019 17:33:34 -0300
+Subject: [PATCH 1/4] dm: add support to directly boot to a mapped device
+
+Add a "create" module parameter, which allows device-mapper targets to
+be configured at boot time. This enables early use of DM targets in the
+boot process (as the root device or otherwise) without the need of an
+initramfs.
+
+The syntax used in the boot param is based on the concise format from
+the dmsetup tool to follow the rule of least surprise:
+
+ dmsetup table --concise /dev/mapper/lroot
+
+Which is:
+ dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
+
+Where,
+ <name> ::= The device name.
+ <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
+ <minor> ::= The device minor number | ""
+ <flags> ::= "ro" | "rw"
+ <table> ::= <start_sector> <num_sectors> <target_type> <target_args>
+ <target_type> ::= "verity" | "linear" | ...
+
+For example, the following could be added in the boot parameters:
+dm-mod.create="lroot,,,rw, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" root=/dev/dm-0
+
+Only the targets that were tested are allowed and the ones that don't
+change any block device when the device is create as read-only. For
+example, mirror and cache targets are not allowed. The rationale behind
+this is that if the user makes a mistake, choosing the wrong device to
+be the mirror or the cache can corrupt data.
+
+The only targets initially allowed are:
+* crypt
+* delay
+* linear
+* snapshot-origin
+* striped
+* verity
+
+Co-developed-by: Will Drewry <wad at chromium.org>
+Co-developed-by: Kees Cook <keescook at chromium.org>
+Co-developed-by: Enric Balletbo i Serra <enric.balletbo at collabora.com>
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Reviewed-by: Kees Cook <keescook at chromium.org>
+Signed-off-by: Mike Snitzer <snitzer at redhat.com>
+---
+ Documentation/device-mapper/dm-init.txt | 114 +++++++++
+ drivers/md/Kconfig | 12 +
+ drivers/md/Makefile | 4 +
+ drivers/md/dm-init.c | 303 ++++++++++++++++++++++++
+ drivers/md/dm-ioctl.c | 103 ++++++++
+ include/linux/device-mapper.h | 9 +
+ 6 files changed, 545 insertions(+)
+ create mode 100644 Documentation/device-mapper/dm-init.txt
+ create mode 100644 drivers/md/dm-init.c
+
+diff --git a/Documentation/device-mapper/dm-init.txt b/Documentation/device-mapper/dm-init.txt
+new file mode 100644
+index 000000000000..8464ee7c01b8
+--- /dev/null
++++ b/Documentation/device-mapper/dm-init.txt
+@@ -0,0 +1,114 @@
++Early creation of mapped devices
++====================================
++
++It is possible to configure a device-mapper device to act as the root device for
++your system in two ways.
++
++The first is to build an initial ramdisk which boots to a minimal userspace
++which configures the device, then pivot_root(8) in to it.
++
++The second is to create one or more device-mappers using the module parameter
++"dm-mod.create=" through the kernel boot command line argument.
++
++The format is specified as a string of data separated by commas and optionally
++semi-colons, where:
++ - a comma is used to separate fields like name, uuid, flags and table
++ (specifies one device)
++ - a semi-colon is used to separate devices.
++
++So the format will look like this:
++
++ dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
++
++Where,
++ <name> ::= The device name.
++ <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
++ <minor> ::= The device minor number | ""
++ <flags> ::= "ro" | "rw"
++ <table> ::= <start_sector> <num_sectors> <target_type> <target_args>
++ <target_type> ::= "verity" | "linear" | ... (see list below)
++
++The dm line should be equivalent to the one used by the dmsetup tool with the
++--concise argument.
++
++Target types
++============
++
++Not all target types are available as there are serious risks in allowing
++activation of certain DM targets without first using userspace tools to check
++the validity of associated metadata.
++
++ "cache": constrained, userspace should verify cache device
++ "crypt": allowed
++ "delay": allowed
++ "era": constrained, userspace should verify metadata device
++ "flakey": constrained, meant for test
++ "linear": allowed
++ "log-writes": constrained, userspace should verify metadata device
++ "mirror": constrained, userspace should verify main/mirror device
++ "raid": constrained, userspace should verify metadata device
++ "snapshot": constrained, userspace should verify src/dst device
++ "snapshot-origin": allowed
++ "snapshot-merge": constrained, userspace should verify src/dst device
++ "striped": allowed
++ "switch": constrained, userspace should verify dev path
++ "thin": constrained, requires dm target message from userspace
++ "thin-pool": constrained, requires dm target message from userspace
++ "verity": allowed
++ "writecache": constrained, userspace should verify cache device
++ "zero": constrained, not meant for rootfs
++
++If the target is not listed above, it is constrained by default (not tested).
++
++Examples
++========
++An example of booting to a linear array made up of user-mode linux block
++devices:
++
++ dm-mod.create="lroot,,,rw, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" root=/dev/dm-0
++
++This will boot to a rw dm-linear target of 8192 sectors split across two block
++devices identified by their major:minor numbers. After boot, udev will rename
++this target to /dev/mapper/lroot (depending on the rules). No uuid was assigned.
++
++An example of multiple device-mappers, with the dm-mod.create="..." contents is shown here
++split on multiple lines for readability:
++
++ vroot,,,ro,
++ 0 1740800 verity 254:0 254:0 1740800 sha1
++ 76e9be054b15884a9fa85973e9cb274c93afadb6
++ 5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe;
++ vram,,,rw,
++ 0 32768 linear 1:0 0,
++ 32768 32768 linear 1:1 0
++
++Other examples (per target):
++
++"crypt":
++ dm-crypt,,8,ro,
++ 0 1048576 crypt aes-xts-plain64
++ babebabebabebabebabebabebabebabebabebabebabebabebabebabebabebabe 0
++ /dev/sda 0 1 allow_discards
++
++"delay":
++ dm-delay,,4,ro,0 409600 delay /dev/sda1 0 500
++
++"linear":
++ dm-linear,,,rw,
++ 0 32768 linear /dev/sda1 0,
++ 32768 1024000 linear /dev/sda2 0,
++ 1056768 204800 linear /dev/sda3 0,
++ 1261568 512000 linear /dev/sda4 0
++
++"snapshot-origin":
++ dm-snap-orig,,4,ro,0 409600 snapshot-origin 8:2
++
++"striped":
++ dm-striped,,4,ro,0 1638400 striped 4 4096
++ /dev/sda1 0 /dev/sda2 0 /dev/sda3 0 /dev/sda4 0
++
++"verity":
++ dm-verity,,4,ro,
++ 0 1638400 verity 1 8:1 8:2 4096 4096 204800 1 sha256
++ fb1a5a0f00deb908d8b53cb270858975e76cf64105d412ce764225d53b8f3cfd
++ 51934789604d1b92399c52e7cb149d1b3a1b74bbbcb103b2a0aaacbed5c08584
+diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
+index 4a249ee86364..4ea706f7790b 100644
+--- a/drivers/md/Kconfig
++++ b/drivers/md/Kconfig
+@@ -428,6 +428,18 @@ config DM_DELAY
+
+ If unsure, say N.
+
++config DM_INIT
++ bool "DM \"dm-mod.create=\" parameter support"
++ depends on BLK_DEV_DM=y
++ ---help---
++ Enable "dm-mod.create=" parameter to create mapped devices at init time.
++ This option is useful to allow mounting rootfs without requiring an
++ initramfs.
++ See Documentation/device-mapper/dm-init.txt for dm-mod.create="..."
++ format.
++
++ If unsure, say N.
++
+ config DM_UEVENT
+ bool "DM uevents"
+ depends on BLK_DEV_DM
+diff --git a/drivers/md/Makefile b/drivers/md/Makefile
+index e94b6f9be941..d56331fbd895 100644
+--- a/drivers/md/Makefile
++++ b/drivers/md/Makefile
+@@ -64,6 +64,10 @@ obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
+ obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
+ obj-$(CONFIG_DM_ZONED) += dm-zoned.o
+
++ifeq ($(CONFIG_DM_INIT),y)
++dm-mod-objs += dm-init.o
++endif
++
+ ifeq ($(CONFIG_DM_UEVENT),y)
+ dm-mod-objs += dm-uevent.o
+ endif
+diff --git a/drivers/md/dm-init.c b/drivers/md/dm-init.c
+new file mode 100644
+index 000000000000..b53f30f16b4d
+--- /dev/null
++++ b/drivers/md/dm-init.c
+@@ -0,0 +1,303 @@
++// SPDX-License-Identifier: GPL-2.0
++
++/*
++ * dm-init.c
++ * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev at chromium.org>
++ *
++ * This file is released under the GPLv2.
++ */
++
++#include <linux/ctype.h>
++#include <linux/device.h>
++#include <linux/device-mapper.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/moduleparam.h>
++
++#define DM_MSG_PREFIX "init"
++#define DM_MAX_DEVICES 256
++#define DM_MAX_TARGETS 256
++#define DM_MAX_STR_SIZE 4096
++
++static char *create;
++
++/*
++ * Format: dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]
++ * Table format: <start_sector> <num_sectors> <target_type> <target_args>
++ *
++ * See Documentation/device-mapper/dm-init.txt for dm-mod.create="..." format
++ * details.
++ */
++
++struct dm_device {
++ struct dm_ioctl dmi;
++ struct dm_target_spec *table[DM_MAX_TARGETS];
++ char *target_args_array[DM_MAX_TARGETS];
++ struct list_head list;
++};
++
++const char *dm_allowed_targets[] __initconst = {
++ "crypt",
++ "delay",
++ "linear",
++ "snapshot-origin",
++ "striped",
++ "verity",
++};
++
++static int __init dm_verify_target_type(const char *target)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(dm_allowed_targets); i++) {
++ if (!strcmp(dm_allowed_targets[i], target))
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static void __init dm_setup_cleanup(struct list_head *devices)
++{
++ struct dm_device *dev, *tmp;
++ unsigned int i;
++
++ list_for_each_entry_safe(dev, tmp, devices, list) {
++ list_del(&dev->list);
++ for (i = 0; i < dev->dmi.target_count; i++) {
++ kfree(dev->table[i]);
++ kfree(dev->target_args_array[i]);
++ }
++ kfree(dev);
++ }
++}
++
++/**
++ * str_field_delimit - delimit a string based on a separator char.
++ * @str: the pointer to the string to delimit.
++ * @separator: char that delimits the field
++ *
++ * Find a @separator and replace it by '\0'.
++ * Remove leading and trailing spaces.
++ * Return the remainder string after the @separator.
++ */
++static char __init *str_field_delimit(char **str, char separator)
++{
++ char *s;
++
++ /* TODO: add support for escaped characters */
++ *str = skip_spaces(*str);
++ s = strchr(*str, separator);
++ /* Delimit the field and remove trailing spaces */
++ if (s)
++ *s = '\0';
++ *str = strim(*str);
++ return s ? ++s : NULL;
++}
++
++/**
++ * dm_parse_table_entry - parse a table entry
++ * @dev: device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * <start_sector> <num_sectors> <target_type> <target_args>[, ...]
++ *
++ * Return the remainder string after the table entry, i.e, after the comma which
++ * delimits the entry or NULL if reached the end of the string.
++ */
++static char __init *dm_parse_table_entry(struct dm_device *dev, char *str)
++{
++ const unsigned int n = dev->dmi.target_count - 1;
++ struct dm_target_spec *sp;
++ unsigned int i;
++ /* fields: */
++ char *field[4];
++ char *next;
++
++ field[0] = str;
++ /* Delimit first 3 fields that are separated by space */
++ for (i = 0; i < ARRAY_SIZE(field) - 1; i++) {
++ field[i + 1] = str_field_delimit(&field[i], ' ');
++ if (!field[i + 1])
++ return ERR_PTR(-EINVAL);
++ }
++ /* Delimit last field that can be terminated by comma */
++ next = str_field_delimit(&field[i], ',');
++
++ sp = kzalloc(sizeof(*sp), GFP_KERNEL);
++ if (!sp)
++ return ERR_PTR(-ENOMEM);
++ dev->table[n] = sp;
++
++ /* start_sector */
++ if (kstrtoull(field[0], 0, &sp->sector_start))
++ return ERR_PTR(-EINVAL);
++ /* num_sector */
++ if (kstrtoull(field[1], 0, &sp->length))
++ return ERR_PTR(-EINVAL);
++ /* target_type */
++ strscpy(sp->target_type, field[2], sizeof(sp->target_type));
++ if (dm_verify_target_type(sp->target_type)) {
++ DMERR("invalid type \"%s\"", sp->target_type);
++ return ERR_PTR(-EINVAL);
++ }
++ /* target_args */
++ dev->target_args_array[n] = kstrndup(field[3], GFP_KERNEL,
++ DM_MAX_STR_SIZE);
++ if (!dev->target_args_array[n])
++ return ERR_PTR(-ENOMEM);
++
++ return next;
++}
++
++/**
++ * dm_parse_table - parse "dm-mod.create=" table field
++ * @dev: device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * <table>[,<table>+]
++ */
++static int __init dm_parse_table(struct dm_device *dev, char *str)
++{
++ char *table_entry = str;
++
++ while (table_entry) {
++ DMDEBUG("parsing table \"%s\"", str);
++ if (++dev->dmi.target_count >= DM_MAX_TARGETS) {
++ DMERR("too many targets %u > %d",
++ dev->dmi.target_count, DM_MAX_TARGETS);
++ return -EINVAL;
++ }
++ table_entry = dm_parse_table_entry(dev, table_entry);
++ if (IS_ERR(table_entry)) {
++ DMERR("couldn't parse table");
++ return PTR_ERR(table_entry);
++ }
++ }
++
++ return 0;
++}
++
++/**
++ * dm_parse_device_entry - parse a device entry
++ * @dev: device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * name,uuid,minor,flags,table[; ...]
++ *
++ * Return the remainder string after the table entry, i.e, after the semi-colon
++ * which delimits the entry or NULL if reached the end of the string.
++ */
++static char __init *dm_parse_device_entry(struct dm_device *dev, char *str)
++{
++ /* There are 5 fields: name,uuid,minor,flags,table; */
++ char *field[5];
++ unsigned int i;
++ char *next;
++
++ field[0] = str;
++ /* Delimit first 4 fields that are separated by comma */
++ for (i = 0; i < ARRAY_SIZE(field) - 1; i++) {
++ field[i+1] = str_field_delimit(&field[i], ',');
++ if (!field[i+1])
++ return ERR_PTR(-EINVAL);
++ }
++ /* Delimit last field that can be delimited by semi-colon */
++ next = str_field_delimit(&field[i], ';');
++
++ /* name */
++ strscpy(dev->dmi.name, field[0], sizeof(dev->dmi.name));
++ /* uuid */
++ strscpy(dev->dmi.uuid, field[1], sizeof(dev->dmi.uuid));
++ /* minor */
++ if (strlen(field[2])) {
++ if (kstrtoull(field[2], 0, &dev->dmi.dev))
++ return ERR_PTR(-EINVAL);
++ dev->dmi.flags |= DM_PERSISTENT_DEV_FLAG;
++ }
++ /* flags */
++ if (!strcmp(field[3], "ro"))
++ dev->dmi.flags |= DM_READONLY_FLAG;
++ else if (strcmp(field[3], "rw"))
++ return ERR_PTR(-EINVAL);
++ /* table */
++ if (dm_parse_table(dev, field[4]))
++ return ERR_PTR(-EINVAL);
++
++ return next;
++}
++
++/**
++ * dm_parse_devices - parse "dm-mod.create=" argument
++ * @devices: list of struct dm_device to store the parsed information.
++ * @str: the pointer to a string with the format:
++ * <device>[;<device>+]
++ */
++static int __init dm_parse_devices(struct list_head *devices, char *str)
++{
++ unsigned long ndev = 0;
++ struct dm_device *dev;
++ char *device = str;
++
++ DMDEBUG("parsing \"%s\"", str);
++ while (device) {
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
++ list_add_tail(&dev->list, devices);
++
++ if (++ndev >= DM_MAX_DEVICES) {
++ DMERR("too many targets %u > %d",
++ dev->dmi.target_count, DM_MAX_TARGETS);
++ return -EINVAL;
++ }
++
++ device = dm_parse_device_entry(dev, device);
++ if (IS_ERR(device)) {
++ DMERR("couldn't parse device");
++ return PTR_ERR(device);
++ }
++ }
++
++ return 0;
++}
++
++/**
++ * dm_init_init - parse "dm-mod.create=" argument and configure drivers
++ */
++static int __init dm_init_init(void)
++{
++ struct dm_device *dev;
++ LIST_HEAD(devices);
++ char *str;
++ int r;
++
++ if (!create)
++ return 0;
++
++ if (strlen(create) >= DM_MAX_STR_SIZE) {
++ DMERR("Argument is too big. Limit is %d\n", DM_MAX_STR_SIZE);
++ return -EINVAL;
++ }
++ str = kstrndup(create, GFP_KERNEL, DM_MAX_STR_SIZE);
++ if (!str)
++ return -ENOMEM;
++
++ r = dm_parse_devices(&devices, str);
++ if (r)
++ goto out;
++
++ DMINFO("waiting for all devices to be available before creating mapped devices\n");
++ wait_for_device_probe();
++
++ list_for_each_entry(dev, &devices, list) {
++ if (dm_early_create(&dev->dmi, dev->table,
++ dev->target_args_array))
++ break;
++ }
++out:
++ kfree(str);
++ dm_setup_cleanup(&devices);
++ return r;
++}
++
++late_initcall(dm_init_init);
++
++module_param(create, charp, 0);
++MODULE_PARM_DESC(create, "Create a mapped device in early boot");
+diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
+index ca948155191a..b7e6c7311a93 100644
+--- a/drivers/md/dm-ioctl.c
++++ b/drivers/md/dm-ioctl.c
+@@ -2017,3 +2017,106 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
+
+ return r;
+ }
++
++
++/**
++ * dm_early_create - create a mapped device in early boot.
++ *
++ * @dmi: Contains main information of the device mapping to be created.
++ * @spec_array: array of pointers to struct dm_target_spec. Describes the
++ * mapping table of the device.
++ * @target_params_array: array of strings with the parameters to a specific
++ * target.
++ *
++ * Instead of having the struct dm_target_spec and the parameters for every
++ * target embedded at the end of struct dm_ioctl (as performed in a normal
++ * ioctl), pass them as arguments, so the caller doesn't need to serialize them.
++ * The size of the spec_array and target_params_array is given by
++ * @dmi->target_count.
++ * This function is supposed to be called in early boot, so locking mechanisms
++ * to protect against concurrent loads are not required.
++ */
++int __init dm_early_create(struct dm_ioctl *dmi,
++ struct dm_target_spec **spec_array,
++ char **target_params_array)
++{
++ int r, m = DM_ANY_MINOR;
++ struct dm_table *t, *old_map;
++ struct mapped_device *md;
++ unsigned int i;
++
++ if (!dmi->target_count)
++ return -EINVAL;
++
++ r = check_name(dmi->name);
++ if (r)
++ return r;
++
++ if (dmi->flags & DM_PERSISTENT_DEV_FLAG)
++ m = MINOR(huge_decode_dev(dmi->dev));
++
++ /* alloc dm device */
++ r = dm_create(m, &md);
++ if (r)
++ return r;
++
++ /* hash insert */
++ r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md);
++ if (r)
++ goto err_destroy_dm;
++
++ /* alloc table */
++ r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
++ if (r)
++ goto err_destroy_dm;
++
++ /* add targets */
++ for (i = 0; i < dmi->target_count; i++) {
++ r = dm_table_add_target(t, spec_array[i]->target_type,
++ (sector_t) spec_array[i]->sector_start,
++ (sector_t) spec_array[i]->length,
++ target_params_array[i]);
++ if (r) {
++ DMWARN("error adding target to table");
++ goto err_destroy_table;
++ }
++ }
++
++ /* finish table */
++ r = dm_table_complete(t);
++ if (r)
++ goto err_destroy_table;
++
++ md->type = dm_table_get_type(t);
++ /* setup md->queue to reflect md's type (may block) */
++ r = dm_setup_md_queue(md, t);
++ if (r) {
++ DMWARN("unable to set up device queue for new table.");
++ goto err_destroy_table;
++ }
++
++ /* Set new map */
++ dm_suspend(md, 0);
++ old_map = dm_swap_table(md, t);
++ if (IS_ERR(old_map)) {
++ r = PTR_ERR(old_map);
++ goto err_destroy_table;
++ }
++ set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG));
++
++ /* resume device */
++ r = dm_resume(md);
++ if (r)
++ goto err_destroy_table;
++
++ DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
++ dm_put(md);
++ return 0;
++
++err_destroy_table:
++ dm_table_destroy(t);
++err_destroy_dm:
++ dm_put(md);
++ dm_destroy(md);
++ return r;
++}
+diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
+index a5538433c927..990e7c2f84b1 100644
+--- a/include/linux/device-mapper.h
++++ b/include/linux/device-mapper.h
+@@ -10,6 +10,7 @@
+
+ #include <linux/bio.h>
+ #include <linux/blkdev.h>
++#include <linux/dm-ioctl.h>
+ #include <linux/math64.h>
+ #include <linux/ratelimit.h>
+
+@@ -457,6 +458,14 @@ void dm_remap_zone_report(struct dm_target *ti, struct bio *bio,
+ sector_t start);
+ union map_info *dm_get_rq_mapinfo(struct request *rq);
+
++/*
++ * Device mapper functions to parse and create devices specified by the
++ * parameter "dm-mod.create="
++ */
++int __init dm_early_create(struct dm_ioctl *dmi,
++ struct dm_target_spec **spec_array,
++ char **target_params_array);
++
+ struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
+
+ /*
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.19/401-dm-init-fix-max-devices-targets-checks.patch b/target/linux/generic/backport-4.19/401-dm-init-fix-max-devices-targets-checks.patch
new file mode 100644
index 0000000000..59eb168b66
--- /dev/null
+++ b/target/linux/generic/backport-4.19/401-dm-init-fix-max-devices-targets-checks.patch
@@ -0,0 +1,48 @@
+From 4318792c96a76cf6ea2ae62afb1b045301d96a5c Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Fri, 26 Apr 2019 17:09:55 -0300
+Subject: [PATCH 2/4] dm init: fix max devices/targets checks
+
+dm-init should allow up to DM_MAX_{DEVICES,TARGETS} for devices/targets,
+and not DM_MAX_{DEVICES,TARGETS} - 1.
+
+Fix the checks and also fix the error message when the number of devices
+is surpassed.
+
+Fixes: 6bbc923dfcf57d ("dm: add support to directly boot to a mapped device")
+Cc: stable at vger.kernel.org
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Signed-off-by: Mike Snitzer <snitzer at redhat.com>
+---
+ drivers/md/dm-init.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/md/dm-init.c b/drivers/md/dm-init.c
+index b53f30f16b4d..2e791ee6779f 100644
+--- a/drivers/md/dm-init.c
++++ b/drivers/md/dm-init.c
+@@ -160,7 +160,7 @@ static int __init dm_parse_table(struct dm_device *dev, char *str)
+
+ while (table_entry) {
+ DMDEBUG("parsing table \"%s\"", str);
+- if (++dev->dmi.target_count >= DM_MAX_TARGETS) {
++ if (++dev->dmi.target_count > DM_MAX_TARGETS) {
+ DMERR("too many targets %u > %d",
+ dev->dmi.target_count, DM_MAX_TARGETS);
+ return -EINVAL;
+@@ -242,9 +242,9 @@ static int __init dm_parse_devices(struct list_head *devices, char *str)
+ return -ENOMEM;
+ list_add_tail(&dev->list, devices);
+
+- if (++ndev >= DM_MAX_DEVICES) {
+- DMERR("too many targets %u > %d",
+- dev->dmi.target_count, DM_MAX_TARGETS);
++ if (++ndev > DM_MAX_DEVICES) {
++ DMERR("too many devices %lu > %d",
++ ndev, DM_MAX_DEVICES);
+ return -EINVAL;
+ }
+
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.19/402-dm-ioctl-fix-hang-in-early-create-error-condition.patch b/target/linux/generic/backport-4.19/402-dm-ioctl-fix-hang-in-early-create-error-condition.patch
new file mode 100644
index 0000000000..1a80b6d97d
--- /dev/null
+++ b/target/linux/generic/backport-4.19/402-dm-ioctl-fix-hang-in-early-create-error-condition.patch
@@ -0,0 +1,49 @@
+From c78ff5467e0f5c4d001b7ac80209441c6c107b26 Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Wed, 15 May 2019 13:50:54 -0300
+Subject: [PATCH 3/4] dm ioctl: fix hang in early create error condition
+
+The dm_early_create() function (which deals with "dm-mod.create=" kernel
+command line option) calls dm_hash_insert() who gets an extra reference
+to the md object.
+
+In case of failure, this reference wasn't being released, causing
+dm_destroy() to hang, thus hanging the whole boot process.
+
+Fix this by calling __hash_remove() in the error path.
+
+Fixes: 6bbc923dfcf57d ("dm: add support to directly boot to a mapped device")
+Cc: stable at vger.kernel.org
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Signed-off-by: Mike Snitzer <snitzer at redhat.com>
+---
+ drivers/md/dm-ioctl.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
+index b7e6c7311a93..d7801688d7be 100644
+--- a/drivers/md/dm-ioctl.c
++++ b/drivers/md/dm-ioctl.c
+@@ -2068,7 +2068,7 @@ int __init dm_early_create(struct dm_ioctl *dmi,
+ /* alloc table */
+ r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
+ if (r)
+- goto err_destroy_dm;
++ goto err_hash_remove;
+
+ /* add targets */
+ for (i = 0; i < dmi->target_count; i++) {
+@@ -2115,6 +2115,10 @@ int __init dm_early_create(struct dm_ioctl *dmi,
+
+ err_destroy_table:
+ dm_table_destroy(t);
++err_hash_remove:
++ (void) __hash_remove(__get_name_cell(dmi->name));
++ /* release reference from __get_name_cell */
++ dm_put(md);
+ err_destroy_dm:
+ dm_put(md);
+ dm_destroy(md);
+--
+2.21.0
+
diff --git a/target/linux/generic/backport-4.19/403-Documentation-dm-init-fix-multi-device-example.patch b/target/linux/generic/backport-4.19/403-Documentation-dm-init-fix-multi-device-example.patch
new file mode 100644
index 0000000000..7d0d1af13f
--- /dev/null
+++ b/target/linux/generic/backport-4.19/403-Documentation-dm-init-fix-multi-device-example.patch
@@ -0,0 +1,45 @@
+From 072ea8a94e81e9744d18034b3245ab530f8365fc Mon Sep 17 00:00:00 2001
+From: Helen Koike <helen.koike at collabora.com>
+Date: Tue, 4 Jun 2019 15:27:19 -0300
+Subject: [PATCH 4/4] Documentation/dm-init: fix multi device example
+
+The example in the docs regarding multiple device-mappers is invalid (it
+has a wrong number of arguments), it's a left over from previous
+versions of the patch.
+Replace the example with an valid and tested one.
+
+Signed-off-by: Helen Koike <helen.koike at collabora.com>
+Reviewed-by: Stephen Boyd <swboyd at chromium.org>
+Signed-off-by: Jonathan Corbet <corbet at lwn.net>
+---
+ Documentation/device-mapper/dm-init.txt | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/Documentation/device-mapper/dm-init.txt b/Documentation/device-mapper/dm-init.txt
+index 8464ee7c01b8..130b3c3679c5 100644
+--- a/Documentation/device-mapper/dm-init.txt
++++ b/Documentation/device-mapper/dm-init.txt
+@@ -74,13 +74,13 @@ this target to /dev/mapper/lroot (depending on the rules). No uuid was assigned.
+ An example of multiple device-mappers, with the dm-mod.create="..." contents is shown here
+ split on multiple lines for readability:
+
+- vroot,,,ro,
+- 0 1740800 verity 254:0 254:0 1740800 sha1
+- 76e9be054b15884a9fa85973e9cb274c93afadb6
+- 5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe;
+- vram,,,rw,
+- 0 32768 linear 1:0 0,
+- 32768 32768 linear 1:1 0
++ dm-linear,,1,rw,
++ 0 32768 linear 8:1 0,
++ 32768 1024000 linear 8:2 0;
++ dm-verity,,3,ro,
++ 0 1638400 verity 1 /dev/sdc1 /dev/sdc2 4096 4096 204800 1 sha256
++ ac87db56303c9c1da433d7209b5a6ef3e4779df141200cbd7c157dcb8dd89c42
++ 5ebfe87f7df3235b80a117ebc4078e44f55045487ad4a96581d1adb564615b51
+
+ Other examples (per target):
+
+--
+2.21.0
+
--
2.23.0
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list