linux-lvm.redhat.com archive mirror
 help / color / mirror / Atom feed
* [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs
@ 2018-11-03  3:53 Helen Koike
  2018-11-03  3:53 ` [linux-lvm] [PATCH v10 1/2] dm ioctl: add a device mapper ioctl function Helen Koike
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Helen Koike @ 2018-11-03  3:53 UTC (permalink / raw)
  To: dm-devel
  Cc: wad, keescook, snitzer, linux-doc, richard.weinberger,
	linux-kernel, linux-lvm, enric.balletbo, kernel, agk

As mentioned in the discussion from the previous version of this patch, Android
and Chrome OS do not use initramfs mostly due to boot time and size liability.
A practical example as mentioned by Kees is that Chrome OS has a limited amount
of storage available for the boot image as it is covered by the static root of
trust signature.

So instead of bringing up userspace to perform the required configuration for
mapped devices, this patchset allows them to be configured in the kernel command
line parameter for use early in the boot process, allowing booting from a mapped
device without an initramfs.

The syntax used in the boot param is based on the concise format from the dmsetup
tool as described in its man page http://man7.org/linux/man-pages/man8/dmsetup.8.html#CONCISE_FORMAT

Which is:
	dm=<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" | ...

Example, the following could be added in the boot parameters.
dm="lroot,,,rw, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" root=/dev/dm-0

Please check patch 2/2 with the documentation on the format.

The idea to make it compatible with the dmsetup concise format is to make it
easier for users, allowing just copy & paste from the output of the command:

	sudo dmsetup table --concise /dev/mapper/lroot

The implementation consists basically in parsing the command line argument and
performing the ioctls that would be performed by userspace otherwise,
i.e. DM_DEV_CREATE, followed by DM_TABLE_LOAD then DM_DEV_SUSPEND.

Instead of performing the ioctls, we could by-pass it, calling the corresponding
functions directly, but the ioctls calls perform some checks and also the
implementation stays less invasive. Please let me know if you would prefer a
directly call instead of going thought the ioctls.

Changes since v9:
 - https://www.redhat.com/archives/linux-lvm/2018-September/msg00016.html
 - new file: drivers/md/dm-boot.c
 - most of the parsing code was moved from init/do_mounts_dm.c to drivers/md/dm-boot.c
 - parsing code was in essence replaced by the concise parser from dmsetup
 _create_concise function:
https://sourceware.org/git/?p=lvm2.git;a=blob;f=libdm/dm-tools/dmsetup.c;h=835fdcdc75e8f0f0f7c4ed46cc9788a6616f58b8;hb=7498f8383397a93db95655ca227257836cbcac82#l1265
 the main reason is that this code is already being used/tested by dmsetup, so
 we can have some level of confidence that it works as expected. Besides this,
 it also looks more efficient.
 - Not all targets are allowed to be used by dm=, as pointed previously, there
 are some risks in creating a mapped device without some validation from
 userspace (see documentation from the patch listing which targets are allowed).
 - Instead of using a simple singly linked list (for devices and tables), use
 the struct list_head. This occupies unnecessary space in the code, but it makes
 the code cleaner and easier to read and less prone to silly errors.
 - Documentation and comments were reviewed and refactored, e.g.:
	* "is to possible" was removed
	* s/specified as a simple string/specified as a string/
 - Added docs above __align function, make it clear that the second parameter @a
 must be a power of two.
 - Clean ups: removal of unnecessary includes, macros, variables, some redundant
 checks and warnings.
 - when calling ioctls, the code was allocating and freeing the same structure
 a couple of times. So instead of executing kzalloc/kfree 3 times, execute
 kmalloc once and reuse the structure after a memset, then finally kfree it once.
 - update commit message

Changes since v8:
 - https://www.redhat.com/archives/linux-lvm/2017-May/msg00055.html
 - Add minor number to make it compatible with dmsetup concise format

Changes since v7:
 - http://lkml.iu.edu/hypermail/linux/kernel/1705.2/02657.html
 - Fix build error due commit
    e516db4f67 (dm ioctl: add a new DM_DEV_ARM_POLL ioctl)

Changes since v6:
 - https://www.redhat.com/archives/dm-devel/2017-April/msg00316.html
 - Add a new function to issue the equivalent of a DM ioctl programatically.
 - Use the new ioctl interface to create the devices.
 - Use a comma-delimited and semi-colon delimited dmsetup-like commands.

Changes since v5:
 - https://www.redhat.com/archives/dm-devel/2016-February/msg00112.html

Enric Balletbo i Serra (1):
  dm ioctl: add a device mapper ioctl function.

Will Drewry (1):
  init: add support to directly boot to a mapped device

 .../admin-guide/kernel-parameters.rst         |   1 +
 .../admin-guide/kernel-parameters.txt         |   3 +
 Documentation/device-mapper/dm-boot.txt       |  87 ++++
 drivers/md/Makefile                           |   2 +-
 drivers/md/dm-boot.c                          | 433 ++++++++++++++++++
 drivers/md/dm-ioctl.c                         |  49 ++
 include/linux/device-mapper.h                 |  12 +
 init/Makefile                                 |   1 +
 init/do_mounts.c                              |   1 +
 init/do_mounts.h                              |  10 +
 init/do_mounts_dm.c                           |  46 ++
 11 files changed, 644 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/device-mapper/dm-boot.txt
 create mode 100644 drivers/md/dm-boot.c
 create mode 100644 init/do_mounts_dm.c

-- 
2.19.1

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [linux-lvm] [PATCH v10 1/2] dm ioctl: add a device mapper ioctl function.
  2018-11-03  3:53 [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Helen Koike
@ 2018-11-03  3:53 ` Helen Koike
  2018-11-03  3:53 ` [linux-lvm] [PATCH v10 2/2] init: add support to directly boot to a mapped device Helen Koike
  2018-11-03  9:10 ` [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Richard Weinberger
  2 siblings, 0 replies; 5+ messages in thread
From: Helen Koike @ 2018-11-03  3:53 UTC (permalink / raw)
  To: dm-devel
  Cc: wad, keescook, snitzer, linux-doc, richard.weinberger,
	linux-kernel, linux-lvm, enric.balletbo, kernel, agk

From: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Add a dm_ioctl_cmd to issue the equivalent of a DM ioctl call in kernel.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>

---

Changes since v9:
 - https://www.redhat.com/archives/linux-lvm/2018-September/msg00016.html
 - Reorganize variables
---
 drivers/md/dm-ioctl.c         | 49 +++++++++++++++++++++++++++++++++++
 include/linux/device-mapper.h |  6 +++++
 2 files changed, 55 insertions(+)

diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index f666778ad237..ae34c2030a9c 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -2018,3 +2018,52 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
 
 	return r;
 }
+
+/**
+ * dm_ioctl_cmd - Device mapper ioctl's
+ * @command: ioctl command
+ * @param: Pointer to device mapped ioctl struct
+ */
+int dm_ioctl_cmd(uint command, struct dm_ioctl *param)
+{
+	struct file *filp = NULL;
+	size_t input_param_size;
+	int ioctl_flags, r;
+	unsigned int cmd;
+	ioctl_fn fn;
+
+	if (_IOC_TYPE(command) != DM_IOCTL)
+		return -ENOTTY;
+
+	/* DM_DEV_ARM_POLL is not supported */
+	if (command == DM_DEV_ARM_POLL)
+		return -EINVAL;
+
+	cmd = _IOC_NR(command);
+
+	/*
+	 * Nothing more to do for the version command.
+	 */
+	if (cmd == DM_VERSION_CMD)
+		return 0;
+
+	fn = lookup_ioctl(cmd, &ioctl_flags);
+	if (!fn) {
+		DMWARN("dm_ioctl: unknown command 0x%x", command);
+		return -ENOTTY;
+	}
+
+	input_param_size = param->data_size;
+	r = validate_params(cmd, param);
+	if (r)
+		return r;
+
+	param->data_size = sizeof(*param);
+	r = fn(filp, param, input_param_size);
+
+	if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) &&
+	    unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))
+		DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);
+
+	return r;
+}
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index d7bee8669f10..8b2e4ae6a498 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -12,6 +12,7 @@
 #include <linux/blkdev.h>
 #include <linux/math64.h>
 #include <linux/ratelimit.h>
+#include <linux/dm-ioctl.h>
 
 struct dm_dev;
 struct dm_target;
@@ -423,6 +424,11 @@ 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 ioctl function.
+ */
+int dm_ioctl_cmd(unsigned int command, struct dm_ioctl *param);
+
 struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
 
 /*
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [linux-lvm] [PATCH v10 2/2] init: add support to directly boot to a mapped device
  2018-11-03  3:53 [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Helen Koike
  2018-11-03  3:53 ` [linux-lvm] [PATCH v10 1/2] dm ioctl: add a device mapper ioctl function Helen Koike
@ 2018-11-03  3:53 ` Helen Koike
  2018-11-03  9:10 ` [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Richard Weinberger
  2 siblings, 0 replies; 5+ messages in thread
From: Helen Koike @ 2018-11-03  3:53 UTC (permalink / raw)
  To: dm-devel
  Cc: wad, keescook, snitzer, linux-doc, richard.weinberger,
	linux-kernel, linux-lvm, enric.balletbo, kernel, agk

From: Will Drewry <wad@chromium.org>

Add a dm= kernel parameter.
It allows device-mapper targets to be configured at boot time for use early
in the boot process (as the root device or otherwise).

Signed-off-by: Will Drewry <wad@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
[rework to use dm_ioctl calls]
Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
[rework to use concise format | rework for upstream]
Signed-off-by: Helen Koike <helen.koike@collabora.com>

---

Hello,

In this patch, I constrained the targets allowed to be used by dm=, but
I am not entirely familiar with all the targets. I blacklisted the ones
mentioned previously and some other that I think doesn't make much sense, but
please let me know if you think there are others I should blacklist.

Changes since v9:
 - https://www.redhat.com/archives/linux-lvm/2018-September/msg00016.html
 - new file: drivers/md/dm-boot.c
 - most of the parsing code was moved from init/do_mounts_dm.c to drivers/md/dm-boot.c
 - parsing code was in essence replaced by the concise parser from dmsetup
 _create_concise function:
https://sourceware.org/git/?p=lvm2.git;a=blob;f=libdm/dm-tools/dmsetup.c;h=835fdcdc75e8f0f0f7c4ed46cc9788a6616f58b8;hb=7498f8383397a93db95655ca227257836cbcac82#l1265
 the main reason is that this code is already being used/tested by dmsetup, so
 we can have some level of confidence that it works as expected. Besides this,
 it also looks more efficient.
 - Not all targets are allowed to be used by dm=, as pointed previously, there
 are some risks in creating a mapped device without some validation from
 userspace (see documentation from the patch listing which targets are allowed).
 - Instead of using a simple singly linked list (for devices and tables), use
 the struct list_head. This occupies unnecessary space in the code, but it makes
 the code cleaner and easier to read and less prone to silly errors.
 - Documentation and comments were reviewed and refactored, e.g.:
	* "is to possible" was removed
	* s/specified as a simple string/specified as a string/
 - Added docs above __align function, make it clear that the second parameter @a
 must be a power of two.
 - Clean ups: removal of unnecessary includes, macros, variables, some redundant
 checks and warnings.
 - when calling ioctls, the code was allocating and freeing the same structure
 a couple of times. So instead of executing kzalloc/kfree 3 times, execute
 kmalloc once and reuse the structure after a memset, then finally kfree it once.
 - update commit message

Thanks
---
 .../admin-guide/kernel-parameters.rst         |   1 +
 .../admin-guide/kernel-parameters.txt         |   3 +
 Documentation/device-mapper/dm-boot.txt       |  87 ++++
 drivers/md/Makefile                           |   2 +-
 drivers/md/dm-boot.c                          | 433 ++++++++++++++++++
 include/linux/device-mapper.h                 |   6 +
 init/Makefile                                 |   1 +
 init/do_mounts.c                              |   1 +
 init/do_mounts.h                              |  10 +
 init/do_mounts_dm.c                           |  46 ++
 10 files changed, 589 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/device-mapper/dm-boot.txt
 create mode 100644 drivers/md/dm-boot.c
 create mode 100644 init/do_mounts_dm.c

diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst
index b8d0bc07ed0a..bd628865f66f 100644
--- a/Documentation/admin-guide/kernel-parameters.rst
+++ b/Documentation/admin-guide/kernel-parameters.rst
@@ -91,6 +91,7 @@ parameter is applicable::
 	AX25	Appropriate AX.25 support is enabled.
 	CLK	Common clock infrastructure is enabled.
 	CMA	Contiguous Memory Area support is enabled.
+	DM	Device mapper support is enabled.
 	DRM	Direct Rendering Management support is enabled.
 	DYNAMIC_DEBUG Build in debug messages and enable them at runtime
 	EDD	BIOS Enhanced Disk Drive Services (EDD) is enabled
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 92eb1f42240d..a3ff7192b980 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -880,6 +880,9 @@
 
 	dis_ucode_ldr	[X86] Disable the microcode loader.
 
+	dm=		[DM] Allows early creation of a device-mapper device.
+			See Documentation/device-mapper/dm-boot.txt.
+
 	dma_debug=off	If the kernel is compiled with DMA_API_DEBUG support,
 			this option disables the debugging code at boot.
 
diff --git a/Documentation/device-mapper/dm-boot.txt b/Documentation/device-mapper/dm-boot.txt
new file mode 100644
index 000000000000..14f756b34328
--- /dev/null
+++ b/Documentation/device-mapper/dm-boot.txt
@@ -0,0 +1,87 @@
+Boot time 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 when the device-mapper and targets are compiled into the kernel
+(not a module). One or more device-mappers may be created and used as the root
+device at boot time with the parameters given with the boot line dm=...
+
+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=<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, requires userspace validation
+	"crypt":		allowed
+	"delay":		allowed
+	"era":			allowed
+	"error":		allowed
+	"flakey":		allowed
+	"integrity":		allowed
+	"linear":		allowed
+	"log-writes":		allowed
+	"mirror":		allowed
+	"multipath":		allowed
+	"raid":			allowed
+	"snapshot":		allowed
+	"snapshot-origin":	allowed
+	"striped":		allowed
+	"switch":		allowed
+	"thin":			constrained,requires userspace validation
+	"thin-pool":		constrained, requires userspace validation
+	"unstriped":		allowed
+	"verity":		allowed
+	"writecache":		allowed
+	"zero":			constrained, requires userspace validation
+	"zoned":		allowed
+
+Examples
+========
+An example of booting to a linear array made up of user-mode linux block
+devices:
+
+  dm="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="..." 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
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 822f4e8753bc..26ffe4536247 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -5,7 +5,7 @@
 
 dm-mod-y	+= dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
 		   dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-stats.o \
-		   dm-rq.o
+		   dm-rq.o dm-boot.o
 dm-multipath-y	+= dm-path-selector.o dm-mpath.o
 dm-snapshot-y	+= dm-snap.o dm-exception-store.o dm-snap-transient.o \
 		    dm-snap-persistent.o
diff --git a/drivers/md/dm-boot.c b/drivers/md/dm-boot.c
new file mode 100644
index 000000000000..f886174b08fc
--- /dev/null
+++ b/drivers/md/dm-boot.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * dm-boot.c
+ * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/ctype.h>
+#include <linux/device-mapper.h>
+#include <linux/list.h>
+
+#define DM_MSG_PREFIX "dm"
+#define DM_MAX_DEVICES 256
+
+/* See Documentation/device-mapper/dm-boot.txt for dm="..." format details. */
+
+struct target {
+	unsigned long long start;
+	unsigned long long length;
+	char *type;
+	char *params;
+	struct list_head list;
+};
+
+struct dm_device {
+	int minor;
+	int ro;
+	char *name;
+	char *uuid;
+	int table_count;
+	struct list_head table;
+	struct list_head list;
+};
+
+/**
+ * _align - align address with the next block
+ * @ptr: the pointer to be aligned.
+ * @a: the size of the block to align the pointer. Must be a power of two.
+ */
+static void __init *_align(void *ptr, unsigned int a)
+{
+	register unsigned long agn = --a;
+
+	return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
+const char *dm_allowed_types[] __initconst = {
+	/* "cache", constrained, requires userspace validation */
+	"crypt",
+	"delay",
+	"era",
+	"error",
+	"flakey",
+	"integrity",
+	"linear",
+	"log-writes",
+	"mirror",
+	"multipath",
+	"raid",
+	"snapshot",
+	"snapshot-origin",
+	"striped",
+	"switch",
+	/* "thin", constrained, requires userspace validation */
+	/* "thin-pool", constrained, requires userspace validation */
+	"unstriped",
+	"verity",
+	"writecache",
+	/* "zero", constrained, requires userspace validation */
+	"zoned",
+};
+
+static int __init dm_verify_type(const char *type)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(dm_allowed_types); i++) {
+		if (!strcmp(dm_allowed_types[i], type))
+			return 0;
+	}
+	return -EINVAL;
+}
+
+static struct target __init *dm_parse_table_entry(char *str)
+{
+	char type[DM_MAX_TYPE_NAME], *ptr;
+	struct target *table;
+	int n;
+
+	/* trim trailing space */
+	for (ptr = str + strlen(str) - 1;
+	     ptr >= str && isspace(*ptr); ptr--)
+		;
+	*(++ptr) = '\0';
+
+	/* trim leading space */
+	for (ptr = str; *ptr && isspace(*ptr); ptr++)
+		;
+	if (!*ptr)
+		return NULL;
+
+	table = kzalloc(sizeof(struct target), GFP_KERNEL);
+	if (!table)
+		return NULL;
+
+	if (sscanf(ptr, "%llu %llu %s %n", &table->start, &table->length,
+		   type, &n) < 3) {
+		DMERR("invalid format of table \"%s\"", str);
+		goto err_free_table;
+	}
+
+	if (dm_verify_type(type)) {
+		DMERR("invalid type \"%s\"", type);
+		goto err_free_table;
+	}
+
+	table->type = kstrndup(type, strlen(type), GFP_KERNEL);
+	if (!table->type) {
+		DMERR("invalid type of table");
+		goto err_free_table;
+	}
+
+	ptr += n;
+	table->params = kstrndup(ptr, strlen(ptr), GFP_KERNEL);
+	if (!table->params) {
+		DMERR("invalid params for table");
+		goto err_free_type;
+	}
+
+	return table;
+
+err_free_type:
+	kfree(table->type);
+err_free_table:
+	kfree(table);
+	return NULL;
+}
+
+/* Parse multiple lines of table */
+static int __init dm_parse_table(struct dm_device *dev, char *str)
+{
+	char *pos = str, *next_pos;
+	struct target *table;
+
+	do {
+		/* Identify and terminate each line */
+		next_pos = strchr(pos, ',');
+		if (next_pos)
+			*next_pos++ = '\0';
+		table = dm_parse_table_entry(pos);
+		if (!table) {
+			DMERR("Couldn't parse table \"%s\"", pos);
+			return -EINVAL;
+		}
+		dev->table_count++;
+		list_add_tail(&table->list, &dev->table);
+	} while ((pos = next_pos));
+
+	return 0;
+}
+
+static void __init dm_setup_cleanup(struct list_head *devices)
+{
+	struct dm_device *dev, *d_tmp;
+	struct target *target, *t_tmp;
+
+	list_for_each_entry_safe(dev, d_tmp, devices, list) {
+		list_del(&dev->list);
+		list_for_each_entry_safe(target, t_tmp, &dev->table, list) {
+			list_del(&target->list);
+			kfree(target->type);
+			kfree(target->params);
+			kfree(target);
+		}
+		kfree(dev);
+	}
+}
+
+/* code based on _create_concise function from dmsetup.c (lvm2) */
+static int __init dm_parse_args(struct list_head *devices, char *str)
+{
+	/* name,uuid,minor,flags,table */
+	char *fields[5] = { NULL };
+	unsigned long ndev = 0;
+	struct dm_device *dev;
+	char *c, *n;
+	int f = 0;
+
+	/*
+	 * Work through input string c, parsing into sets of 5 fields.
+	 * Strip out any characters quoted by backslashes in-place.
+	 * Read characters from c and prepare them in situ for final processing
+	 * at n
+	 */
+	c = n = fields[f] = str;
+
+	while (*c) {
+		/* Quoted character?  Skip past quote. */
+		if (*c == '\\') {
+			if (!*(++c)) {
+				DMERR("Backslash must be followed by another character at end of string.");
+				*n = '\0';
+				DMERR("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
+				      f + 1, fields[0], fields[1], fields[2],
+				      fields[3], fields[4]);
+				goto out;
+			}
+			/* Don't interpret next character */
+			*n++ = *c++;
+			continue;
+		}
+
+		/* Comma marking end of field? */
+		if (*c == ',' && f < 4) {
+			/* Terminate string */
+			*n++ = '\0', c++;
+			/* Store start of next field */
+			fields[++f] = n;
+			/* Skip any whitespace after field-separating commas */
+			while (isspace(*c))
+				c++;
+			continue;
+		}
+
+		/* Semi-colon marking end of device? */
+		if (*c == ';' || *(c + 1) == '\0') {
+			/* End of input? */
+			if (*c != ';')
+				/* Copy final character */
+				*n++ = *c;
+
+			/* Terminate string */
+			*n++ = '\0', c++;
+
+			if (f != 4) {
+				DMERR("Five comma-separated fields are required for each device");
+				DMERR("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
+				      f + 1, fields[0], fields[1], fields[2],
+				      fields[3], fields[4]);
+				goto out;
+			}
+
+			dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+			if (!dev)
+				goto out;
+			INIT_LIST_HEAD(&dev->table);
+			list_add_tail(&dev->list, devices);
+			if (++ndev > DM_MAX_DEVICES) {
+				DMERR("too many devices %lu > %d",
+				      ndev, DM_MAX_DEVICES);
+				goto out;
+			}
+
+			/* Set up parameters */
+			dev->name = fields[0];
+			dev->uuid = fields[1];
+
+			if (!*fields[2])
+				dev->minor = DM_ANY_MINOR;
+			else if (kstrtoint(fields[2], 0, &dev->minor))
+				goto out;
+
+			if (!strcmp(fields[3], "ro"))
+				dev->ro = 1;
+			else if (*fields[3] && strcmp(fields[3], "rw")) {
+				DMERR("Invalid flags parameter '%s' must be 'ro' or 'rw' or empty.", fields[3]);
+				goto out;
+			}
+
+			if (*fields[4] && dm_parse_table(dev, fields[4]))
+				goto out;
+
+			/* Clear parameters ready for any further devices */
+			f = 0;
+			fields[0] = n;
+			fields[1] = fields[2] = fields[3] = fields[4] = NULL;
+
+			/* Skip any whitespace after semi-colons */
+			while (isspace(*c))
+				c++;
+
+			continue;
+		}
+
+		/* Normal character */
+		*n++ = *c++;
+	}
+
+	if (fields[0] != n) {
+		*n = '\0';
+		DMERR("Incomplete entry: five comma-separated fields are required for each device");
+		DMERR("Parsed %d fields: name: %s uuid: %s minor: %s flags: %s table: %s",
+		      f + 1, fields[0], fields[1], fields[2], fields[3],
+		      fields[4]);
+		goto out;
+	}
+
+	return 0;
+
+out:
+	dm_setup_cleanup(devices);
+	return -EINVAL;
+}
+
+static char __init *dm_add_target(const struct target *const table,
+				  char *const buf, char *const end)
+{
+	struct dm_target_spec sp;
+	char *p = buf;
+	size_t len;
+
+	len = strlen(table->params);
+	if (sizeof(struct dm_target_spec) + len >= end - p) {
+		DMERR("ran out of memory building ioctl parameter");
+		return NULL;
+	}
+
+	p += sizeof(struct dm_target_spec);
+	strcpy(p, table->params);
+	p += len + 1;
+	/* align next block */
+	p = _align(p, 8);
+
+	sp.status = 0;
+	sp.sector_start = table->start;
+	sp.length = table->length;
+	strscpy(sp.target_type, table->type, sizeof(sp.target_type));
+	sp.next = p - buf;
+	memcpy(buf, &sp, sizeof(struct dm_target_spec));
+
+	return p;
+}
+
+static int dm_setup_ioctl(struct dm_ioctl *dmi, size_t len,
+			  struct dm_device *dev, int flags)
+{
+	struct target *table;
+	char *b, *e;
+
+	memset(dmi, 0, len);
+	dmi->version[0] = 4;
+	dmi->version[1] = 0;
+	dmi->version[2] = 0;
+	dmi->data_size = len;
+	dmi->data_start = sizeof(struct dm_ioctl);
+	dmi->flags = flags;
+	dmi->target_count = dev->table_count;
+	dmi->event_nr = 1;
+
+	/* Only one between uuid, name and dev should be filled */
+	if (*dev->uuid)
+		strscpy(dmi->uuid, dev->uuid, sizeof(dmi->uuid));
+	else if (*dev->name)
+		strscpy(dmi->name, dev->name, sizeof(dmi->name));
+	else if (dev->minor > 0)
+		dmi->dev = dev->minor;
+	else {
+		DMERR("device name or uuid or minor number should be provided");
+		return -EINVAL;
+	}
+
+	b = (char *) (dmi + 1);
+	e = (char *) dmi + len;
+
+	list_for_each_entry(table, &dev->table, list) {
+		DMDEBUG("device %s adding table '%llu %llu %s %s'",
+			dev->name,
+			(unsigned long long) table->start,
+			(unsigned long long) table->length,
+			table->type, table->params);
+		b = dm_add_target(table, b, e);
+		if (!b) {
+			kfree(dmi);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+void __init dm_boot_setup_drives(char *boot_param)
+{
+	const size_t min_size = 16 * 1024;
+	const size_t len = sizeof(struct dm_ioctl) > min_size ?
+			   sizeof(struct dm_ioctl) : min_size;
+	LIST_HEAD(devices);
+	struct dm_device *dev;
+	struct dm_ioctl *dmi;
+	int flags;
+
+	if (dm_parse_args(&devices, boot_param))
+		return;
+
+	dmi = kmalloc(len, GFP_KERNEL);
+	if (!dmi)
+		return;
+
+	list_for_each_entry(dev, &devices, list) {
+		flags = dev->minor < 0 ? 0 : DM_PERSISTENT_DEV_FLAG;
+		if (dm_setup_ioctl(dmi, len, dev, flags))
+			goto out_free;
+		dmi->dev = dev->minor;
+		/* create a new device */
+		if (dm_ioctl_cmd(DM_DEV_CREATE, dmi)) {
+			DMERR("failed to create device %s", dev->name);
+			goto out_free;
+		}
+
+		flags = dev->ro ? DM_READONLY_FLAG : 0;
+		if (dm_setup_ioctl(dmi, len, dev, flags))
+			goto out_free;
+		/* load a table into the 'inactive' slot for the device. */
+		if (dm_ioctl_cmd(DM_TABLE_LOAD, dmi)) {
+			DMERR("failed to load device %s tables", dev->name);
+			goto out_free;
+		}
+
+		if (dm_setup_ioctl(dmi, len, dev, 0))
+			goto out_free;
+		/* resume and the device should be ready. */
+		if (dm_ioctl_cmd(DM_DEV_SUSPEND, dmi)) {
+			DMERR("failed to resume device %s", dev->name);
+			goto out_free;
+		}
+
+		DMINFO("dm-%d (%s) is ready", dev->minor, dev->name);
+	}
+
+out_free:
+	kfree(dmi);
+}
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 8b2e4ae6a498..a3ad379fd2b7 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -429,6 +429,12 @@ union map_info *dm_get_rq_mapinfo(struct request *rq);
  */
 int dm_ioctl_cmd(unsigned int command, struct dm_ioctl *param);
 
+/*
+ * Device mapper function to parse and create devices specified in the kernel
+ * command line boot parameter "dm="
+ */
+void __init dm_boot_setup_drives(char *boot_param);
+
 struct queue_limits *dm_get_queue_limits(struct mapped_device *md);
 
 /*
diff --git a/init/Makefile b/init/Makefile
index a3e5ce2bcf08..f814f0ff5974 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -19,6 +19,7 @@ mounts-y			:= do_mounts.o
 mounts-$(CONFIG_BLK_DEV_RAM)	+= do_mounts_rd.o
 mounts-$(CONFIG_BLK_DEV_INITRD)	+= do_mounts_initrd.o
 mounts-$(CONFIG_BLK_DEV_MD)	+= do_mounts_md.o
+mounts-$(CONFIG_BLK_DEV_DM)	+= do_mounts_dm.o
 
 # dependencies on generated files need to be listed explicitly
 $(obj)/version.o: include/generated/compile.h
diff --git a/init/do_mounts.c b/init/do_mounts.c
index e1c9afa9d8c9..d707f12be6e7 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -555,6 +555,7 @@ void __init prepare_namespace(void)
 	wait_for_device_probe();
 
 	md_run_setup();
+	dm_run_setup();
 
 	if (saved_root_name[0]) {
 		root_device_name = saved_root_name;
diff --git a/init/do_mounts.h b/init/do_mounts.h
index 0bb0806de4ce..0f57528ea324 100644
--- a/init/do_mounts.h
+++ b/init/do_mounts.h
@@ -61,3 +61,13 @@ void md_run_setup(void);
 static inline void md_run_setup(void) {}
 
 #endif
+
+#ifdef CONFIG_BLK_DEV_DM
+
+void dm_run_setup(void);
+
+#else
+
+static inline void dm_run_setup(void) {}
+
+#endif
diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c
new file mode 100644
index 000000000000..6657e2993706
--- /dev/null
+++ b/init/do_mounts_dm.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * do_mounts_dm.c
+ * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev@chromium.org>
+ *
+ * This file is released under the GPLv2.
+ */
+#include <linux/device-mapper.h>
+
+#include "do_mounts.h"
+
+#define DM_MSG_PREFIX "init"
+
+static struct {
+	char *str;
+	int early_setup;
+} dm_setup_args __initdata;
+
+/*
+ * Parse the command-line parameters given to our kernel, but do not
+ * actually try to invoke the DM device now; that is handled by
+ * dm_boot_setup_drives after the low-level disk drivers have initialised.
+ */
+static int __init dm_setup(char *str)
+{
+	if (!str) {
+		DMERR("Invalid arguments supplied to dm=.");
+		return 0;
+	}
+	DMDEBUG("Want to parse \"%s\"", str);
+	dm_setup_args.str = str;
+	dm_setup_args.early_setup = 1;
+
+	return 1;
+}
+
+__setup("dm=", dm_setup);
+
+void __init dm_run_setup(void)
+{
+	if (!dm_setup_args.early_setup)
+		return;
+	DMINFO("attempting early device configuration.");
+	dm_boot_setup_drives(dm_setup_args.str);
+}
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs
  2018-11-03  3:53 [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Helen Koike
  2018-11-03  3:53 ` [linux-lvm] [PATCH v10 1/2] dm ioctl: add a device mapper ioctl function Helen Koike
  2018-11-03  3:53 ` [linux-lvm] [PATCH v10 2/2] init: add support to directly boot to a mapped device Helen Koike
@ 2018-11-03  9:10 ` Richard Weinberger
  2018-11-06 14:24   ` Will Drewry
  2 siblings, 1 reply; 5+ messages in thread
From: Richard Weinberger @ 2018-11-03  9:10 UTC (permalink / raw)
  To: Helen Koike, keescook
  Cc: wad, snitzer, linux-doc, linux-kernel, dm-devel, linux-lvm,
	enric.balletbo, kernel, agk

Helen,

Am Samstag, 3. November 2018, 04:53:39 CET schrieb Helen Koike:
> As mentioned in the discussion from the previous version of this patch, Android
> and Chrome OS do not use initramfs mostly due to boot time and size liability.

Do you have numbers on that?

I understand that using something like dracut with systemd inside is not what you
want from a boot time point of view.
But having an initramfs embedded into the kernel image which contains only a single
static linked binary can be *very* small and fast.
If you invest a little more time, you don't even need a libc, just fire up some
syscalls to setup your dm. I use this technique regularly on deeply embedded systems
to setup non-trivial UBIFS/crypto stuff.

Want I'm trying to say, before adding ad-hoc a feature to the kernel, we should be
very sure that there is no other way to solve this in a sane manner.
We have initramfs support for reasons.

Thanks,
//richard

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs
  2018-11-03  9:10 ` [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Richard Weinberger
@ 2018-11-06 14:24   ` Will Drewry
  0 siblings, 0 replies; 5+ messages in thread
From: Will Drewry @ 2018-11-06 14:24 UTC (permalink / raw)
  To: richard
  Cc: Kees Cook, Mike Snitzer, linux-doc, device-mapper development,
	LKML, helen.koike, linux-lvm, enric.balletbo, kernel,
	Alasdair G Kergon

Hi Richard, Helen,

On Sat, Nov 3, 2018 at 4:10 AM Richard Weinberger <richard@nod.at> wrote:
>
> Helen,
>
> Am Samstag, 3. November 2018, 04:53:39 CET schrieb Helen Koike:
> > As mentioned in the discussion from the previous version of this patch, Android
> > and Chrome OS do not use initramfs mostly due to boot time and size liability.
>
> Do you have numbers on that?

Originally, we saved ~200 ms, but I don't think we have recent
numbers.  (Unless Helen has some!) We first authored and posted this
patch in 2010:
- https://marc.info/?l=dm-devel&m=127429492521964&w=2
- https://marc.info/?l=dm-devel&m=127429499422096&w=2
- https://marc.info/?l=dm-devel&m=127429493922000&w=2

Every Chrome OS device uses a variant of this patch as well as
Android devices starting last year (if they use AVB 2.0).

Originally, the intent was the measured latency reduction.  We get a
linear speed
improvement when doing a cryptographic verification of the kernel and
initramfs.
Why? More data == more hashes (sha256 w/compute per block).  There's
additional overhead from bringing up early userspace, but those are the
numbers I don't have.

> I understand that using something like dracut with systemd inside is not what you
> want from a boot time point of view.
> But having an initramfs embedded into the kernel image which contains only a single
> static linked binary can be *very* small and fast.
> If you invest a little more time, you don't even need a libc, just fire up some
> syscalls to setup your dm. I use this technique regularly on deeply embedded systems
> to setup non-trivial UBIFS/crypto stuff.
>
> Want I'm trying to say, before adding ad-hoc a feature to the kernel, we should be
> very sure that there is no other way to solve this in a sane manner.
> We have initramfs support for reasons.

I very much appreciate the perspective, but after 8 years in shipping
devices after
integrating feedback from kernel maintainers over the subsequent years, this
doesn't feel like an "ad-hoc" feature.  It's been effective and fit in
well with the
existing kernel functionality, etc (imho :).  What level of
performance improvement or
other changes might be necessary to make the cut?

Thanks!
will

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2018-11-06 14:24 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-03  3:53 [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Helen Koike
2018-11-03  3:53 ` [linux-lvm] [PATCH v10 1/2] dm ioctl: add a device mapper ioctl function Helen Koike
2018-11-03  3:53 ` [linux-lvm] [PATCH v10 2/2] init: add support to directly boot to a mapped device Helen Koike
2018-11-03  9:10 ` [linux-lvm] [PATCH v10 0/2] dm: boot a mapped device without an initramfs Richard Weinberger
2018-11-06 14:24   ` Will Drewry

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).