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.petazz...@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.ko...@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 <w...@chromium.org>
+Co-developed-by: Kees Cook <keesc...@chromium.org>
+Co-developed-by: Enric Balletbo i Serra <enric.balle...@collabora.com>
+Signed-off-by: Helen Koike <helen.ko...@collabora.com>
+Reviewed-by: Kees Cook <keesc...@chromium.org>
+Signed-off-by: Mike Snitzer <snit...@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-...@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.ko...@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: sta...@vger.kernel.org
+Signed-off-by: Helen Koike <helen.ko...@collabora.com>
+Signed-off-by: Mike Snitzer <snit...@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.ko...@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: sta...@vger.kernel.org
+Signed-off-by: Helen Koike <helen.ko...@collabora.com>
+Signed-off-by: Mike Snitzer <snit...@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.ko...@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.ko...@collabora.com>
+Reviewed-by: Stephen Boyd <swb...@chromium.org>
+Signed-off-by: Jonathan Corbet <cor...@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.ko...@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 <w...@chromium.org>
+Co-developed-by: Kees Cook <keesc...@chromium.org>
+Co-developed-by: Enric Balletbo i Serra <enric.balle...@collabora.com>
+Signed-off-by: Helen Koike <helen.ko...@collabora.com>
+Reviewed-by: Kees Cook <keesc...@chromium.org>
+Signed-off-by: Mike Snitzer <snit...@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-...@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.ko...@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: sta...@vger.kernel.org
+Signed-off-by: Helen Koike <helen.ko...@collabora.com>
+Signed-off-by: Mike Snitzer <snit...@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.ko...@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: sta...@vger.kernel.org
+Signed-off-by: Helen Koike <helen.ko...@collabora.com>
+Signed-off-by: Mike Snitzer <snit...@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.ko...@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.ko...@collabora.com>
+Reviewed-by: Stephen Boyd <swb...@chromium.org>
+Signed-off-by: Jonathan Corbet <cor...@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@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to