aboutsummaryrefslogtreecommitdiffstats
diff options
-rw-r--r--Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst129
-rw-r--r--Documentation/admin-guide/device-mapper/index.rst1
-rw-r--r--block/blk-crypto.c3
-rw-r--r--drivers/md/Kconfig11
-rw-r--r--drivers/md/Makefile1
-rw-r--r--drivers/md/dm-ima.c492
-rw-r--r--drivers/md/dm-ima.h68
-rw-r--r--drivers/md/dm-inlinecrypt.c618
-rw-r--r--drivers/md/dm-ioctl.c151
-rw-r--r--drivers/md/dm-raid.c6
-rw-r--r--drivers/md/dm-vdo/indexer/index-layout.c2
-rw-r--r--drivers/md/dm.c15
-rw-r--r--drivers/md/md.h2
-rw-r--r--drivers/md/raid5.c7
14 files changed, 1202 insertions, 304 deletions
diff --git a/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst b/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst
new file mode 100644
index 0000000000000..76b3aae21eb4c
--- /dev/null
+++ b/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst
@@ -0,0 +1,129 @@
+==============
+dm-inlinecrypt
+==============
+
+Device-Mapper's "inlinecrypt" target provides transparent encryption of block devices
+using the inline encryption hardware.
+
+For a more detailed description of inline encryption, see:
+https://docs.kernel.org/block/inline-encryption.html
+
+Parameters::
+
+ <cipher> <key> <iv_offset> <device path> \
+ <offset> [<#opt_params> <opt_params>]
+
+<cipher>
+ Encryption cipher type.
+
+ The cipher specifications format is::
+
+ cipher
+
+ Examples::
+
+ aes-xts-plain64
+
+ The cipher type corresponds to the encryption modes supported by
+ inline crypto in the block layer. Currently, only
+ BLK_ENCRYPTION_MODE_AES_256_XTS (i.e. aes-xts-plain64) is supported.
+
+<key>
+ Key used for encryption. It is encoded either as a hexadecimal number
+ or it can be passed as <key_string> prefixed with single colon
+ character (':') for keys residing in kernel keyring service.
+ You can only use key sizes that are valid for the selected cipher.
+ Note that the size in bytes of a valid key must be in bellow range.
+
+ [BLK_CRYPTO_KEY_TYPE_RAW, BLK_CRYPTO_KEY_TYPE_HW_WRAPPED]
+
+<key_string>
+ The kernel keyring key is identified by string in following format:
+ <key_size>:<keyring_type>:<key_description>.
+
+<key_size>
+ The encryption key size in bytes. The kernel key payload size must match
+ the value passed in <key_size>.
+
+<keyring_type>
+ The type of the key inside the kernel keyring. It can be either 'logon',
+ or 'trusted' kernel key type.
+
+<key_description>
+ The kernel keyring key description inlinecrypt target should look for
+ when loading key of <keyring_type>.
+
+<iv_offset>
+ The IV offset is a sector count that is added to the sector number
+ before creating the IV.
+
+<device path>
+ This is the device that is going to be used as backend and contains the
+ encrypted data. You can specify it as a path like /dev/xxx or a device
+ number <major>:<minor>.
+
+<offset>
+ Starting sector within the device where the encrypted data begins.
+
+<#opt_params>
+ Number of optional parameters. If there are no optional parameters,
+ the optional parameters section can be skipped or #opt_params can be zero.
+ Otherwise #opt_params is the number of following arguments.
+
+ Example of optional parameters section:
+ keytype:raw allow_discards sector_size:4096 iv_large_sectors
+
+<key_type>
+ The type of the key as seen by the block layer, either standard or
+ hardware-wrapped. The string is supplied in the table as <keytype:raw>
+ or <keytype:hw-wrapped>.
+
+allow_discards
+ Block discard requests (a.k.a. TRIM) are passed through the inlinecrypt
+ device. The default is to ignore discard requests.
+
+ WARNING: Assess the specific security risks carefully before enabling this
+ option. For example, allowing discards on encrypted devices may lead to
+ the leak of information about the ciphertext device (filesystem type,
+ used space etc.) if the discarded blocks can be located easily on the
+ device later.
+
+sector_size:<bytes>
+ Use <bytes> as the encryption unit instead of 512 bytes sectors.
+ This option can be in range 512 - 4096 bytes and must be power of two.
+ Virtual device will announce this size as a minimal IO and logical sector.
+
+iv_large_sectors
+ Use <sector_size>-based sector numbers for IV generation instead of
+ 512-byte sectors.
+
+ For dm-inlinecrypt, this flag must be specified when <sector_size>
+ is larger than 512 bytes. The legacy 512-byte-based IV behavior is
+ not supported.
+
+ When specified, if <sector_size> is 4096 bytes, plain64 IV for the
+ second sector will be 1, and <iv_offset> must be a multiple of
+ <sector_size> (in 512-byte units).
+
+Example scripts
+===============
+Currently, dm-inlinecrypt devices must be set up directly using dmsetup.
+There is no userspace support yet to integrate dm-inlinecrypt with LUKS
+or cryptsetup. In particular, cryptsetup currently only supports
+dm-crypt, and cannot be used to create dm-inlinecrypt mappings.
+
+The following examples demonstrate how to create dm-inlinecrypt devices
+using dmsetup
+
+::
+
+ #!/bin/sh
+ # Create a inlinecrypt device using dmsetup
+ dmsetup create inlinecrypt1 --table "0 `blockdev --getsz $1` inlinecrypt aes-xts-plain64 babebabebabebabebabebabebabebabebabebabebabebabebabebabebabebabe 0 0 $1 0 1 keytype:raw"
+
+::
+
+ #!/bin/sh
+ # Create a inlinecrypt device using dmsetup when encryption key is stored in keyring service
+ dmsetup create inlinecrypt2 --table "0 `blockdev --getsz $1` inlinecrypt aes-xts-plain64 :64:logon:fde:dminlinecrypt_test_key 0 0 $1 0 1 keytype:raw"
+
diff --git a/Documentation/admin-guide/device-mapper/index.rst b/Documentation/admin-guide/device-mapper/index.rst
index 030d854628ac2..73c1f20021343 100644
--- a/Documentation/admin-guide/device-mapper/index.rst
+++ b/Documentation/admin-guide/device-mapper/index.rst
@@ -15,6 +15,7 @@ Device Mapper
dm-flakey
dm-ima
dm-init
+ dm-inlinecrypt
dm-integrity
dm-io
dm-log
diff --git a/block/blk-crypto.c b/block/blk-crypto.c
index 165c9d2cce073..15e25e41b1669 100644
--- a/block/blk-crypto.c
+++ b/block/blk-crypto.c
@@ -116,6 +116,7 @@ void bio_crypt_set_ctx(struct bio *bio, const struct blk_crypto_key *key,
bio->bi_crypt_context = bc;
}
+EXPORT_SYMBOL_GPL(bio_crypt_set_ctx);
void __bio_crypt_free_ctx(struct bio *bio)
{
@@ -348,6 +349,7 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key,
return 0;
}
+EXPORT_SYMBOL_GPL(blk_crypto_init_key);
bool blk_crypto_config_supported_natively(struct block_device *bdev,
const struct blk_crypto_config *cfg)
@@ -398,6 +400,7 @@ int blk_crypto_start_using_key(struct block_device *bdev,
}
return blk_crypto_fallback_start_using_mode(key->crypto_cfg.crypto_mode);
}
+EXPORT_SYMBOL_GPL(blk_crypto_start_using_key);
/**
* blk_crypto_evict_key() - Evict a blk_crypto_key from a block_device
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index a3fcdca7e6db3..df27c7d066d23 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -315,6 +315,17 @@ config DM_CRYPT
If unsure, say N.
+config DM_INLINECRYPT
+ tristate "Inline encryption target support"
+ depends on BLK_DEV_DM
+ depends on (KEYS || KEYS=n)
+ depends on BLK_INLINE_ENCRYPTION
+ help
+ This device-mapper target is similar to dm-crypt, but it uses the
+ blk-crypto API instead of the regular crypto API. This allows it to
+ take advantage of inline encryption hardware such as that commonly
+ built into UFS host controllers.
+
config DM_SNAPSHOT
tristate "Snapshot target"
depends on BLK_DEV_DM
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index c338cc6fbe2eb..517d1f7d82888 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_DM_UNSTRIPED) += dm-unstripe.o
obj-$(CONFIG_DM_BUFIO) += dm-bufio.o
obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
+obj-$(CONFIG_DM_INLINECRYPT) += dm-inlinecrypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_DUST) += dm-dust.o
obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
index 9495ca0350563..5e7232582feb7 100644
--- a/drivers/md/dm-ima.c
+++ b/drivers/md/dm-ima.c
@@ -21,25 +21,32 @@
* character, so that they don't interfere with the construction of key-value pairs,
* and clients can split the key1=val1,key2=val2,key3=val3; pairs properly.
*/
-static void fix_separator_chars(char **buf)
+static void fix_separator_chars(char *buf)
{
- int l = strlen(*buf);
+ int l = strlen(buf);
int i, j, sp = 0;
for (i = 0; i < l; i++)
- if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',')
+ if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',')
sp++;
if (!sp)
return;
+ buf[l + sp] = '\0';
for (i = l-1, j = i+sp; i >= 0; i--) {
- (*buf)[j--] = (*buf)[i];
- if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',')
- (*buf)[j--] = '\\';
+ buf[j--] = buf[i];
+ if (buf[i] == '\\' || buf[i] == ';' || buf[i] == '=' || buf[i] == ',')
+ buf[j--] = '\\';
}
}
+static void fix_context_strings(struct dm_ima_context *context)
+{
+ fix_separator_chars(context->dev_name);
+ fix_separator_chars(context->dev_uuid);
+}
+
/*
* Internal function to allocate memory for IMA measurements.
*/
@@ -59,68 +66,85 @@ static void *dm_ima_alloc(size_t len, bool noio)
return ptr;
}
-/*
- * Internal function to allocate and copy name and uuid for IMA measurements.
- */
-static int dm_ima_alloc_and_copy_name_uuid(struct mapped_device *md, char **dev_name,
- char **dev_uuid, bool noio)
+void dm_ima_init(struct mapped_device *md)
{
- int r;
- *dev_name = dm_ima_alloc(DM_NAME_LEN*2, noio);
- if (!(*dev_name)) {
- r = -ENOMEM;
- goto error;
- }
+ md->ima.update_idx = 0;
+ md->ima.measure_idx = 0;
+ init_waitqueue_head(&md->ima.ima_wq);
+ spin_lock_init(&md->ima.ima_lock);
+}
- *dev_uuid = dm_ima_alloc(DM_UUID_LEN*2, noio);
- if (!(*dev_uuid)) {
- r = -ENOMEM;
- goto error;
- }
+void dm_ima_alloc_context(struct dm_ima_context **context, bool noio)
+{
+ *context = dm_ima_alloc(sizeof(struct dm_ima_context), noio);
+}
- r = dm_copy_name_and_uuid(md, *dev_name, *dev_uuid);
- if (r)
- goto error;
+void dm_ima_free_context(struct dm_ima_context *context)
+{
+ if (likely(context)) {
+ kfree(context->table.device_metadata);
+ kfree(context->table.hash);
+ kfree(context);
+ }
+}
- fix_separator_chars(dev_name);
- fix_separator_chars(dev_uuid);
+static void wait_to_measure(struct dm_ima_measurements *ima,
+ unsigned int update_idx)
+{
+ spin_lock_irq(&ima->ima_lock);
+ wait_event_lock_irq(ima->ima_wq,
+ ima->measure_idx == update_idx,
+ ima->ima_lock);
+ spin_unlock_irq(&ima->ima_lock);
+}
- return 0;
-error:
- kfree(*dev_name);
- kfree(*dev_uuid);
- *dev_name = NULL;
- *dev_uuid = NULL;
- return r;
+static void wake_next_measure(struct dm_ima_measurements *ima)
+{
+ spin_lock_irq(&ima->ima_lock);
+ ima->measure_idx++;
+ spin_unlock_irq(&ima->ima_lock);
+ wake_up_all(&ima->ima_wq);
}
/*
- * Internal function to allocate and copy device data for IMA measurements.
+ * Helper function for swapping the table, to make sure that the
+ * correct table metadata is saved and restored.
*/
-static int dm_ima_alloc_and_copy_device_data(struct mapped_device *md, char **device_data,
- unsigned int num_targets, bool noio)
+void dm_ima_context_table_op(struct mapped_device *md,
+ struct dm_ima_context *context,
+ enum dm_ima_table_op op)
{
- char *dev_name = NULL, *dev_uuid = NULL;
- int r;
+ struct dm_ima_measurements *ima = &md->ima;
- r = dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio);
- if (r)
- return r;
+ if (unlikely(!context))
+ return;
- *device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
- if (!(*device_data)) {
- r = -ENOMEM;
- goto error;
+ wait_to_measure(ima, context->update_idx);
+
+ if (op == DM_IMA_TABLE_SAVE) {
+ context->table = ima->inactive_table;
+ memset(&ima->inactive_table, 0, sizeof(ima->inactive_table));
+ } else {
+ ima->inactive_table = context->table;
+ memset(&context->table, 0, sizeof(context->table));
}
- scnprintf(*device_data, DM_IMA_DEVICE_BUF_LEN,
+ wake_next_measure(ima);
+}
+
+/*
+ * Internal function to copy device data for IMA measurements.
+ */
+static void dm_ima_copy_device_data(struct mapped_device *md, char *device_data,
+ struct dm_ima_context *context,
+ unsigned int num_targets)
+{
+ memset(device_data, 0, DM_IMA_DEVICE_BUF_LEN);
+ scnprintf(device_data, DM_IMA_DEVICE_BUF_LEN,
"name=%s,uuid=%s,major=%d,minor=%d,minor_count=%d,num_targets=%u;",
- dev_name, dev_uuid, md->disk->major, md->disk->first_minor,
- md->disk->minors, num_targets);
-error:
- kfree(dev_name);
- kfree(dev_uuid);
- return r;
+ context->dev_name, context->dev_uuid, md->disk->major,
+ md->disk->first_minor, md->disk->minors, num_targets);
+
}
/*
@@ -141,42 +165,21 @@ static void dm_ima_measure_data(const char *event_name, const void *buf, size_t
memalloc_noio_restore(noio_flag);
}
-/*
- * Internal function to allocate and copy current device capacity for IMA measurements.
- */
-static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **capacity_str,
- bool noio)
-{
- sector_t capacity;
-
- capacity = get_capacity(md->disk);
-
- *capacity_str = dm_ima_alloc(DM_IMA_DEVICE_CAPACITY_BUF_LEN, noio);
- if (!(*capacity_str))
- return -ENOMEM;
-
- return scnprintf(*capacity_str, DM_IMA_DEVICE_BUF_LEN, "current_device_capacity=%llu;",
- capacity);
-}
-
-/*
- * Initialize/reset the dm ima related data structure variables.
- */
-void dm_ima_reset_data(struct mapped_device *md)
+static sector_t dm_ima_capacity(struct mapped_device *md)
{
- memset(&(md->ima), 0, sizeof(md->ima));
- md->ima.dm_version_str_len = strlen(DM_IMA_VERSION_STR);
+ return (md->ima.active_table.device_metadata) ?
+ md->ima.active_table.capacity : get_capacity(md->disk);
}
/*
* Build up the IMA data for each target, and finally measure.
*/
-void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags)
+void dm_ima_measure_on_table_load(struct dm_table *table,
+ struct dm_ima_context *context)
{
size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0;
char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL;
char *ima_buf = NULL, *device_data_buf = NULL;
- int last_target_measured = -1;
status_type_t type = STATUSTYPE_IMA;
size_t cur_total_buf_len = 0;
unsigned int num_targets, i;
@@ -185,9 +188,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
bool noio = false;
char table_load_event_name[] = "dm_table_load";
+ if (unlikely(!context))
+ return;
+
+ wait_to_measure(&table->md->ima, context->update_idx);
+
ima_buf = dm_ima_alloc(DM_IMA_MEASUREMENT_BUF_LEN, noio);
if (!ima_buf)
- return;
+ goto error;
target_metadata_buf = dm_ima_alloc(DM_IMA_TARGET_METADATA_BUF_LEN, noio);
if (!target_metadata_buf)
@@ -199,13 +207,18 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
num_targets = table->num_targets;
- if (dm_ima_alloc_and_copy_device_data(table->md, &device_data_buf, num_targets, noio))
+ device_data_buf = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
+ if (!device_data_buf)
goto error;
+ fix_context_strings(context);
+ dm_ima_copy_device_data(table->md, device_data_buf, context,
+ num_targets);
+
sha256_init(&hash_ctx);
- memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len);
- l += table->md->ima.dm_version_str_len;
+ memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
device_data_buf_len = strlen(device_data_buf);
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
@@ -214,8 +227,6 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
for (i = 0; i < num_targets; i++) {
struct dm_target *ti = dm_table_get_target(table, i);
- last_target_measured = 0;
-
/*
* First retrieve the target metadata.
*/
@@ -229,7 +240,7 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
* Then retrieve the actual target data.
*/
if (ti->type->status)
- ti->type->status(ti, type, status_flags, target_data_buf,
+ ti->type->status(ti, type, 0, target_data_buf,
DM_IMA_TARGET_DATA_BUF_LEN);
else
target_data_buf[0] = '\0';
@@ -260,19 +271,11 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
* prefix, so that multiple records from the same "dm_table_load" for
* a given device can be linked together.
*/
- memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len);
- l += table->md->ima.dm_version_str_len;
+ memcpy(ima_buf + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
memcpy(ima_buf + l, device_data_buf, device_data_buf_len);
l += device_data_buf_len;
-
- /*
- * If this iteration of the for loop turns out to be the last target
- * in the table, dm_ima_measure_data("dm_table_load", ...) doesn't need
- * to be called again, just the hash needs to be finalized.
- * "last_target_measured" tracks this state.
- */
- last_target_measured = 1;
}
/*
@@ -286,11 +289,8 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
l += target_data_buf_len;
}
- if (!last_target_measured) {
- dm_ima_measure_data(table_load_event_name, ima_buf, l, noio);
-
- sha256_update(&hash_ctx, (const u8 *)ima_buf, l);
- }
+ dm_ima_measure_data(table_load_event_name, ima_buf, l, noio);
+ sha256_update(&hash_ctx, (const u8 *)ima_buf, l);
/*
* Finalize the table hash, and store it in table->md->ima.inactive_table.hash,
@@ -304,17 +304,14 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
if (!digest_buf)
goto error;
- if (table->md->ima.active_table.hash != table->md->ima.inactive_table.hash)
- kfree(table->md->ima.inactive_table.hash);
-
+ kfree(table->md->ima.inactive_table.hash);
table->md->ima.inactive_table.hash = digest_buf;
table->md->ima.inactive_table.hash_len = strlen(digest_buf);
table->md->ima.inactive_table.num_targets = num_targets;
+ table->md->ima.inactive_table.capacity = dm_table_get_size(table);
- if (table->md->ima.active_table.device_metadata !=
- table->md->ima.inactive_table.device_metadata)
- kfree(table->md->ima.inactive_table.device_metadata);
+ kfree(table->md->ima.inactive_table.device_metadata);
table->md->ima.inactive_table.device_metadata = device_data_buf;
table->md->ima.inactive_table.device_metadata_len = device_data_buf_len;
@@ -326,66 +323,55 @@ exit:
kfree(ima_buf);
kfree(target_metadata_buf);
kfree(target_data_buf);
+
+ wake_next_measure(&table->md->ima);
}
/*
* Measure IMA data on device resume.
*/
-void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
+void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
+ struct dm_ima_context *context)
{
- char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+ char *device_table_data = NULL;
char active[] = "active_table_hash=";
unsigned int active_len = strlen(active);
unsigned int l = 0;
bool noio = true;
bool nodata = true;
- int capacity_len;
- device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
- if (!device_table_data)
+ if (unlikely(!context))
return;
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0)
- goto error;
-
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
- l += md->ima.dm_version_str_len;
+ wait_to_measure(&md->ima, context->update_idx);
if (swap) {
- if (md->ima.active_table.hash != md->ima.inactive_table.hash)
- kfree(md->ima.active_table.hash);
-
- md->ima.active_table.hash = NULL;
- md->ima.active_table.hash_len = 0;
-
- if (md->ima.active_table.device_metadata !=
- md->ima.inactive_table.device_metadata)
- kfree(md->ima.active_table.device_metadata);
-
- md->ima.active_table.device_metadata = NULL;
- md->ima.active_table.device_metadata_len = 0;
- md->ima.active_table.num_targets = 0;
-
- if (md->ima.inactive_table.hash) {
- md->ima.active_table.hash = md->ima.inactive_table.hash;
- md->ima.active_table.hash_len = md->ima.inactive_table.hash_len;
- md->ima.inactive_table.hash = NULL;
- md->ima.inactive_table.hash_len = 0;
- }
-
- if (md->ima.inactive_table.device_metadata) {
- md->ima.active_table.device_metadata =
- md->ima.inactive_table.device_metadata;
- md->ima.active_table.device_metadata_len =
- md->ima.inactive_table.device_metadata_len;
- md->ima.active_table.num_targets = md->ima.inactive_table.num_targets;
- md->ima.inactive_table.device_metadata = NULL;
- md->ima.inactive_table.device_metadata_len = 0;
- md->ima.inactive_table.num_targets = 0;
+ kfree(md->ima.active_table.hash);
+ kfree(md->ima.active_table.device_metadata);
+ md->ima.active_table = context->table;
+ memset(&context->table, 0, sizeof(context->table));
+ if (md->ima.active_table.device_metadata) {
+ /*
+ * A rename could have happened while the swap was
+ * going on. In that case, the saved table info would
+ * still have the old name. Update the metadata to be
+ * sure that it has the current name
+ */
+ struct dm_ima_device_table_metadata *table = &md->ima.active_table;
+ fix_context_strings(context);
+ dm_ima_copy_device_data(md, table->device_metadata,
+ context, table->num_targets);
+ table->device_metadata_len = strlen(table->device_metadata);
}
}
+ device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
+ if (!device_table_data)
+ goto error;
+
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
+
if (md->ima.active_table.device_metadata) {
memcpy(device_table_data + l, md->ima.active_table.device_metadata,
md->ima.active_table.device_metadata_len);
@@ -409,59 +395,54 @@ void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
}
if (nodata) {
- if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
- goto error;
-
+ fix_context_strings(context);
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
"%sname=%s,uuid=%s;device_resume=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
+ DM_IMA_VERSION_STR, context->dev_name,
+ context->dev_uuid);
}
-
- memcpy(device_table_data + l, capacity_str, capacity_len);
- l += capacity_len;
+ l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l,
+ "current_device_capacity=%llu;", dm_ima_capacity(md));
dm_ima_measure_data("dm_device_resume", device_table_data, l, noio);
- kfree(dev_name);
- kfree(dev_uuid);
error:
- kfree(capacity_str);
kfree(device_table_data);
+
+ wake_next_measure(&md->ima);
}
/*
* Measure IMA data on remove.
*/
-void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
+void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
+ struct dm_ima_context *context,
+ unsigned int idx)
{
- char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+ char *device_table_data;
char active_table_str[] = "active_table_hash=";
char inactive_table_str[] = "inactive_table_hash=";
char device_active_str[] = "device_active_metadata=";
char device_inactive_str[] = "device_inactive_metadata=";
- char remove_all_str[] = "remove_all=";
unsigned int active_table_len = strlen(active_table_str);
unsigned int inactive_table_len = strlen(inactive_table_str);
unsigned int device_active_len = strlen(device_active_str);
unsigned int device_inactive_len = strlen(device_inactive_str);
- unsigned int remove_all_len = strlen(remove_all_str);
unsigned int l = 0;
bool noio = true;
bool nodata = true;
- int capacity_len;
- device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio);
- if (!device_table_data)
+ wait_to_measure(&md->ima, idx);
+
+ if (unlikely(!context))
goto exit;
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0) {
- kfree(device_table_data);
+ device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio);
+ if (!device_table_data)
goto exit;
- }
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
- l += md->ima.dm_version_str_len;
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
if (md->ima.active_table.device_metadata) {
memcpy(device_table_data + l, device_active_str, device_active_len);
@@ -518,68 +499,57 @@ void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all)
* in IMA measurements.
*/
if (nodata) {
- if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
- goto error;
-
+ fix_context_strings(context);
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
"%sname=%s,uuid=%s;device_remove=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
+ DM_IMA_VERSION_STR, context->dev_name,
+ context->dev_uuid);
}
- memcpy(device_table_data + l, remove_all_str, remove_all_len);
- l += remove_all_len;
- memcpy(device_table_data + l, remove_all ? "y;" : "n;", 2);
- l += 2;
-
- memcpy(device_table_data + l, capacity_str, capacity_len);
- l += capacity_len;
+ l += scnprintf(device_table_data + l, (DM_IMA_DEVICE_BUF_LEN * 2) - l,
+ "remove_all=%c;current_device_capacity=%llu;",
+ remove_all ? 'y' : 'n', dm_ima_capacity(md));
dm_ima_measure_data("dm_device_remove", device_table_data, l, noio);
-error:
kfree(device_table_data);
- kfree(capacity_str);
exit:
kfree(md->ima.active_table.device_metadata);
-
- if (md->ima.active_table.device_metadata !=
- md->ima.inactive_table.device_metadata)
- kfree(md->ima.inactive_table.device_metadata);
+ kfree(md->ima.inactive_table.device_metadata);
kfree(md->ima.active_table.hash);
+ kfree(md->ima.inactive_table.hash);
- if (md->ima.active_table.hash != md->ima.inactive_table.hash)
- kfree(md->ima.inactive_table.hash);
-
- dm_ima_reset_data(md);
+ memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
+ memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
- kfree(dev_name);
- kfree(dev_uuid);
+ wake_next_measure(&md->ima);
}
/*
* Measure ima data on table clear.
*/
-void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
+void dm_ima_measure_on_table_clear(struct mapped_device *md,
+ struct dm_ima_context *context)
{
unsigned int l = 0;
- char *device_table_data = NULL, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+ char *device_table_data = NULL;
char inactive_str[] = "inactive_table_hash=";
unsigned int inactive_len = strlen(inactive_str);
bool noio = true;
bool nodata = true;
- int capacity_len;
- device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
- if (!device_table_data)
+ if (unlikely(!context))
return;
- capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
- if (capacity_len < 0)
- goto error1;
+ wait_to_measure(&md->ima, context->update_idx);
- memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len);
- l += md->ima.dm_version_str_len;
+ device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
+ if (!device_table_data)
+ goto error;
+
+ memcpy(device_table_data + l, DM_IMA_VERSION_STR, strlen(DM_IMA_VERSION_STR));
+ l += strlen(DM_IMA_VERSION_STR);
if (md->ima.inactive_table.device_metadata_len &&
md->ima.inactive_table.hash_len) {
@@ -602,101 +572,79 @@ void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map)
}
if (nodata) {
- if (dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio))
- goto error2;
-
+ fix_context_strings(context);
l = scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
"%sname=%s,uuid=%s;table_clear=no_data;",
- DM_IMA_VERSION_STR, dev_name, dev_uuid);
+ DM_IMA_VERSION_STR, context->dev_name,
+ context->dev_uuid);
}
- memcpy(device_table_data + l, capacity_str, capacity_len);
- l += capacity_len;
+ l += scnprintf(device_table_data + l, DM_IMA_DEVICE_BUF_LEN - l,
+ "current_device_capacity=%llu;", dm_ima_capacity(md));
dm_ima_measure_data("dm_table_clear", device_table_data, l, noio);
- if (new_map) {
- if (md->ima.inactive_table.hash &&
- md->ima.inactive_table.hash != md->ima.active_table.hash)
- kfree(md->ima.inactive_table.hash);
-
- md->ima.inactive_table.hash = NULL;
- md->ima.inactive_table.hash_len = 0;
-
- if (md->ima.inactive_table.device_metadata &&
- md->ima.inactive_table.device_metadata != md->ima.active_table.device_metadata)
- kfree(md->ima.inactive_table.device_metadata);
-
- md->ima.inactive_table.device_metadata = NULL;
- md->ima.inactive_table.device_metadata_len = 0;
- md->ima.inactive_table.num_targets = 0;
-
- if (md->ima.active_table.hash) {
- md->ima.inactive_table.hash = md->ima.active_table.hash;
- md->ima.inactive_table.hash_len = md->ima.active_table.hash_len;
- }
-
- if (md->ima.active_table.device_metadata) {
- md->ima.inactive_table.device_metadata =
- md->ima.active_table.device_metadata;
- md->ima.inactive_table.device_metadata_len =
- md->ima.active_table.device_metadata_len;
- md->ima.inactive_table.num_targets =
- md->ima.active_table.num_targets;
- }
- }
+error:
+ kfree(md->ima.inactive_table.hash);
+ kfree(md->ima.inactive_table.device_metadata);
+ memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
- kfree(dev_name);
- kfree(dev_uuid);
-error2:
- kfree(capacity_str);
-error1:
kfree(device_table_data);
+
+ wake_next_measure(&md->ima);
}
/*
* Measure IMA data on device rename.
*/
-void dm_ima_measure_on_device_rename(struct mapped_device *md)
+void dm_ima_measure_on_device_rename(struct mapped_device *md,
+ struct dm_ima_context *context)
{
- char *old_device_data = NULL, *new_device_data = NULL, *combined_device_data = NULL;
- char *new_dev_name = NULL, *new_dev_uuid = NULL, *capacity_str = NULL;
+ char *old_device_data = NULL;
+ char *combined_device_data = NULL;
bool noio = true;
int len;
+ struct dm_ima_device_table_metadata *table;
- if (dm_ima_alloc_and_copy_device_data(md, &new_device_data,
- md->ima.active_table.num_targets, noio))
+ if (unlikely(!context))
return;
- if (dm_ima_alloc_and_copy_name_uuid(md, &new_dev_name, &new_dev_uuid, noio))
- goto error;
+ wait_to_measure(&md->ima, context->update_idx);
+
+ fix_context_strings(context);
combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, noio);
if (!combined_device_data)
- goto error;
-
- if (dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio) < 0)
- goto error;
-
- old_device_data = md->ima.active_table.device_metadata;
-
- md->ima.active_table.device_metadata = new_device_data;
- md->ima.active_table.device_metadata_len = strlen(new_device_data);
+ goto exit;
+ if (md->ima.active_table.device_metadata)
+ old_device_data = md->ima.active_table.device_metadata;
+ else if (md->ima.inactive_table.device_metadata)
+ old_device_data = md->ima.inactive_table.device_metadata;
+ else
+ old_device_data = "device_rename=no_data;";
len = scnprintf(combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2,
- "%s%snew_name=%s,new_uuid=%s;%s", DM_IMA_VERSION_STR, old_device_data,
- new_dev_name, new_dev_uuid, capacity_str);
+ "%s%snew_name=%s,new_uuid=%s;current_device_capacity=%llu;",
+ DM_IMA_VERSION_STR, old_device_data, context->dev_name,
+ context->dev_uuid, dm_ima_capacity(md));
dm_ima_measure_data("dm_device_rename", combined_device_data, len, noio);
+ kfree(combined_device_data);
- goto exit;
-
-error:
- kfree(new_device_data);
exit:
- kfree(capacity_str);
- kfree(combined_device_data);
- kfree(old_device_data);
- kfree(new_dev_name);
- kfree(new_dev_uuid);
+ if (md->ima.active_table.device_metadata) {
+ table = &md->ima.active_table;
+ dm_ima_copy_device_data(md, table->device_metadata, context,
+ table->num_targets);
+ table->device_metadata_len = strlen(table->device_metadata);
+ }
+
+ if (md->ima.inactive_table.device_metadata) {
+ table = &md->ima.inactive_table;
+ dm_ima_copy_device_data(md, table->device_metadata, context,
+ table->num_targets);
+ table->device_metadata_len = strlen(table->device_metadata);
+ }
+
+ wake_next_measure(&md->ima);
}
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
index a403deca6093e..0ec013d1545cc 100644
--- a/drivers/md/dm-ima.h
+++ b/drivers/md/dm-ima.h
@@ -24,6 +24,11 @@
__dm_ima_str(DM_VERSION_MINOR) "." \
__dm_ima_str(DM_VERSION_PATCHLEVEL) ";"
+enum dm_ima_table_op {
+ DM_IMA_TABLE_SAVE,
+ DM_IMA_TABLE_RESTORE,
+};
+
#ifdef CONFIG_IMA
struct dm_ima_device_table_metadata {
@@ -36,6 +41,7 @@ struct dm_ima_device_table_metadata {
char *device_metadata;
unsigned int device_metadata_len;
unsigned int num_targets;
+ sector_t capacity;
/*
* Contains the sha256 hashes of the IMA measurements of the target
@@ -45,31 +51,67 @@ struct dm_ima_device_table_metadata {
unsigned int hash_len;
};
+struct dm_ima_context {
+ struct dm_ima_device_table_metadata table;
+ unsigned int update_idx;
+ char dev_name[DM_NAME_LEN*2];
+ char dev_uuid[DM_UUID_LEN*2];
+};
+
/*
* This structure contains device metadata, and table hash for
* active and inactive tables for ima measurements.
*/
struct dm_ima_measurements {
+ unsigned int update_idx;
+ unsigned int measure_idx;
+ struct wait_queue_head ima_wq;
+ spinlock_t ima_lock;
struct dm_ima_device_table_metadata active_table;
struct dm_ima_device_table_metadata inactive_table;
- unsigned int dm_version_str_len;
};
-void dm_ima_reset_data(struct mapped_device *md);
-void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags);
-void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap);
-void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all);
-void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map);
-void dm_ima_measure_on_device_rename(struct mapped_device *md);
+void dm_ima_init(struct mapped_device *md);
+void dm_ima_alloc_context(struct dm_ima_context **context, bool noio);
+void dm_ima_free_context(struct dm_ima_context *context);
+void dm_ima_context_table_op(struct mapped_device *md,
+ struct dm_ima_context *context,
+ enum dm_ima_table_op op);
+void dm_ima_measure_on_table_load(struct dm_table *table,
+ struct dm_ima_context *context);
+void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap,
+ struct dm_ima_context *context);
+void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all,
+ struct dm_ima_context *context,
+ unsigned int idx);
+void dm_ima_measure_on_table_clear(struct mapped_device *md,
+ struct dm_ima_context *context);
+void dm_ima_measure_on_device_rename(struct mapped_device *md,
+ struct dm_ima_context *context);
#else
-static inline void dm_ima_reset_data(struct mapped_device *md) {}
-static inline void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {}
-static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {}
-static inline void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) {}
-static inline void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) {}
-static inline void dm_ima_measure_on_device_rename(struct mapped_device *md) {}
+struct dm_ima_context;
+
+static inline void dm_ima_init(struct mapped_device *md) {}
+static inline void dm_ima_alloc_context(struct dm_ima_context **context, bool noio) {}
+static inline void dm_ima_free_context(struct dm_ima_context *context) {}
+static inline void dm_ima_context_table_op(struct mapped_device *md,
+ struct dm_ima_context *context,
+ enum dm_ima_table_op op) {}
+static inline void dm_ima_measure_on_table_load(struct dm_table *table,
+ struct dm_ima_context *context) {}
+static inline void dm_ima_measure_on_device_resume(struct mapped_device *md,
+ bool swap,
+ struct dm_ima_context *context) {}
+static inline void dm_ima_measure_on_device_remove(struct mapped_device *md,
+ bool remove_all,
+ struct dm_ima_context *context,
+ unsigned int idx) {}
+static inline void dm_ima_measure_on_table_clear(struct mapped_device *md,
+ struct dm_ima_context *context) {}
+static inline void dm_ima_measure_on_device_rename(struct mapped_device *md,
+ struct dm_ima_context *context) {}
#endif /* CONFIG_IMA */
diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c
new file mode 100644
index 0000000000000..be1b4aa8f28bc
--- /dev/null
+++ b/drivers/md/dm-inlinecrypt.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2024 Google LLC
+ */
+
+#include <linux/blk-crypto.h>
+#include <linux/ctype.h>
+#include <linux/device-mapper.h>
+#include <linux/hex.h>
+#include <linux/module.h>
+#include <keys/user-type.h>
+
+#define DM_MSG_PREFIX "inlinecrypt"
+
+static const struct dm_inlinecrypt_cipher {
+ const char *name;
+ enum blk_crypto_mode_num mode_num;
+} dm_inlinecrypt_ciphers[] = {
+ {
+ .name = "aes-xts-plain64",
+ .mode_num = BLK_ENCRYPTION_MODE_AES_256_XTS,
+ },
+};
+
+/**
+ * struct inlinecrypt_ctx - private data of an inlinecrypt target
+ * @dev: the underlying device
+ * @start: starting sector of the range of @dev which this target actually maps.
+ * For this purpose a "sector" is 512 bytes.
+ * @cipher_string: the name of the encryption algorithm being used
+ * @key_size: size of the encryption key in bytes
+ * @iv_offset: starting offset for IVs. IVs are generated as if the target were
+ * preceded by @iv_offset 512-byte sectors.
+ * @sector_size: crypto sector size in bytes (usually 4096)
+ * @sector_bits: log2(sector_size)
+ * @key_type: type of the key -- either raw or hardware-wrapped
+ * @key: the encryption key to use
+ * @max_dun: the maximum DUN that may be used (computed from other params)
+ */
+struct inlinecrypt_ctx {
+ struct dm_dev *dev;
+ sector_t start;
+ const char *cipher_string;
+ unsigned int key_size;
+ u64 iv_offset;
+ unsigned int sector_size;
+ unsigned int sector_bits;
+ enum blk_crypto_key_type key_type;
+ struct blk_crypto_key key;
+ u64 max_dun;
+};
+
+static const struct dm_inlinecrypt_cipher *
+lookup_cipher(const char *cipher_string)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dm_inlinecrypt_ciphers); i++) {
+ if (strcmp(cipher_string, dm_inlinecrypt_ciphers[i].name) == 0)
+ return &dm_inlinecrypt_ciphers[i];
+ }
+ return NULL;
+}
+
+static void inlinecrypt_dtr(struct dm_target *ti)
+{
+ struct inlinecrypt_ctx *ctx = ti->private;
+
+ if (ctx->dev) {
+ if (ctx->key.size)
+ blk_crypto_evict_key(ctx->dev->bdev, &ctx->key);
+ dm_put_device(ti, ctx->dev);
+ }
+ kfree_sensitive(ctx->cipher_string);
+ kfree_sensitive(ctx);
+}
+
+#ifdef CONFIG_KEYS
+
+static bool contains_whitespace(const char *str)
+{
+ while (*str)
+ if (isspace(*str++))
+ return true;
+ return false;
+}
+
+static int set_key_user(struct key *key, char *key_bytes,
+ const unsigned int key_bytes_size)
+{
+ const struct user_key_payload *ukp;
+
+ ukp = user_key_payload_locked(key);
+ if (!ukp)
+ return -EKEYREVOKED;
+
+ if (key_bytes_size != ukp->datalen)
+ return -EINVAL;
+
+ memcpy(key_bytes, ukp->data, key_bytes_size);
+
+ return 0;
+}
+
+static int inlinecrypt_get_keyring_key(const char *key_string, u8 *key_bytes,
+ const unsigned int key_bytes_size)
+{
+ char *key_desc;
+ int ret;
+ struct key_type *type;
+ struct key *key;
+ int (*set_key)(struct key *key, char *key_bytes,
+ const unsigned int key_bytes_size);
+
+ /*
+ * Reject key_string with whitespace. dm core currently lacks code for
+ * proper whitespace escaping in arguments on DM_TABLE_STATUS path.
+ */
+ if (contains_whitespace(key_string)) {
+ DMERR("whitespace chars not allowed in key string");
+ return -EINVAL;
+ }
+
+ /* look for next ':' separating key_type from key_description */
+ key_desc = strchr(key_string, ':');
+ if (!key_desc || key_desc == key_string || !strlen(key_desc + 1))
+ return -EINVAL;
+
+ if (!strncmp(key_string, "logon:", key_desc - key_string + 1)) {
+ type = &key_type_logon;
+ set_key = set_key_user;
+ } else {
+ return -EINVAL;
+ }
+
+ key = request_key(type, key_desc + 1, NULL);
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+
+ down_read(&key->sem);
+
+ ret = set_key(key, (char *)key_bytes, key_bytes_size);
+
+ up_read(&key->sem);
+ key_put(key);
+
+ return ret;
+}
+
+static int get_key_size(char **key_string)
+{
+ char *colon, dummy;
+ int ret;
+
+ if (*key_string[0] != ':') {
+ ret = strlen(*key_string);
+
+ if (ret > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE
+ || ret % 2
+ || !ret) {
+ DMERR("Invalid keysize");
+ return -EINVAL;
+ }
+ return ret >> 1;
+ }
+
+ /* look for next ':' in key string */
+ colon = strpbrk(*key_string + 1, ":");
+ if (!colon)
+ return -EINVAL;
+
+ if (sscanf(*key_string + 1, "%u%c", &ret, &dummy) != 2 || dummy != ':')
+ return -EINVAL;
+
+ /* remaining key string should be :<logon|user>:<key_desc> */
+ *key_string = colon;
+
+ return ret;
+}
+
+#else
+
+static int inlinecrypt_get_keyring_key(const char *key_string, u8 *key_bytes,
+ const unsigned int key_bytes_size)
+{
+ return -EINVAL;
+}
+
+static int get_key_size(char **key_string)
+{
+ int key_hex_size = strlen(*key_string);
+
+ if (*key_string[0] == ':')
+ return -EINVAL;
+
+ if (key_hex_size > 2 * BLK_CRYPTO_MAX_ANY_KEY_SIZE
+ || key_hex_size % 2
+ || !key_hex_size) {
+ DMERR("Invalid keysize");
+ return -EINVAL;
+ }
+
+ return key_hex_size >> 1;
+}
+
+#endif /* CONFIG_KEYS */
+
+static int inlinecrypt_get_key(const char *key_string,
+ u8 key[BLK_CRYPTO_MAX_ANY_KEY_SIZE],
+ const unsigned int key_size)
+{
+ int ret = 0;
+
+ if (key_size > BLK_CRYPTO_MAX_ANY_KEY_SIZE) {
+ DMERR("Invalid keysize");
+ return -EINVAL;
+ }
+
+ /* ':' means the key is in kernel keyring, short-circuit normal key processing */
+ if (key_string[0] == ':') {
+ /* key string should be :<logon|user>:<key_desc> */
+ ret = inlinecrypt_get_keyring_key(key_string + 1, key, key_size);
+ goto out;
+ }
+
+ if (hex2bin(key, key_string, key_size) != 0)
+ ret = -EINVAL;
+
+out:
+ return ret;
+}
+
+static int inlinecrypt_ctr_optional(struct dm_target *ti,
+ unsigned int argc, char **argv)
+{
+ struct inlinecrypt_ctx *ctx = ti->private;
+ struct dm_arg_set as;
+ static const struct dm_arg _args[] = {
+ {0, 4, "Invalid number of feature args"},
+ };
+ unsigned int opt_params;
+ const char *opt_string;
+ bool iv_large_sectors = false;
+ char dummy;
+ int err;
+
+ as.argc = argc;
+ as.argv = argv;
+
+ err = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+ if (err)
+ return err;
+
+ while (opt_params--) {
+ opt_string = dm_shift_arg(&as);
+ if (!opt_string) {
+ ti->error = "Not enough feature arguments";
+ return -EINVAL;
+ }
+ if (str_has_prefix(opt_string, "keytype:")) {
+ const char *val = opt_string + strlen("keytype:");
+
+ if (!*val) {
+ ti->error = "Invalid block key type";
+ return -EINVAL;
+ }
+
+ if (!strcmp(val, "raw")) {
+ ctx->key_type = BLK_CRYPTO_KEY_TYPE_RAW;
+ } else if (!strcmp(val, "hw-wrapped")) {
+ ctx->key_type = BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+ } else {
+ ti->error = "Invalid block key type";
+ return -EINVAL;
+ }
+ } else if (!strcmp(opt_string, "allow_discards")) {
+ ti->num_discard_bios = 1;
+ } else if (sscanf(opt_string, "sector_size:%u%c",
+ &ctx->sector_size, &dummy) == 1) {
+ if (ctx->sector_size < SECTOR_SIZE ||
+ ctx->sector_size > 4096 ||
+ !is_power_of_2(ctx->sector_size)) {
+ ti->error = "Invalid sector_size";
+ return -EINVAL;
+ }
+ } else if (!strcmp(opt_string, "iv_large_sectors")) {
+ iv_large_sectors = true;
+ } else {
+ ti->error = "Invalid feature arguments";
+ return -EINVAL;
+ }
+ }
+
+ /* dm-inlinecrypt doesn't implement iv_large_sectors=false. */
+ if (ctx->sector_size != SECTOR_SIZE && !iv_large_sectors) {
+ ti->error = "iv_large_sectors must be specified";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Construct an inlinecrypt mapping:
+ * <cipher> [<key>|:<key_size>:<logon>:<key_description>] <iv_offset> <dev_path> <start>
+ *
+ * This syntax matches dm-crypt's, but the set of supported functionality has
+ * been stripped down.
+ */
+static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+ struct inlinecrypt_ctx *ctx;
+ const struct dm_inlinecrypt_cipher *cipher;
+ u8 key_bytes[BLK_CRYPTO_MAX_ANY_KEY_SIZE];
+ unsigned int dun_bytes;
+ unsigned long long tmpll;
+ char dummy;
+ int err;
+
+ if (argc < 5) {
+ ti->error = "Not enough arguments";
+ return -EINVAL;
+ }
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ ti->error = "Out of memory";
+ return -ENOMEM;
+ }
+ ti->private = ctx;
+
+ /* <cipher> */
+ ctx->cipher_string = kstrdup(argv[0], GFP_KERNEL);
+ if (!ctx->cipher_string) {
+ ti->error = "Out of memory";
+ err = -ENOMEM;
+ goto bad;
+ }
+ cipher = lookup_cipher(ctx->cipher_string);
+ if (!cipher) {
+ ti->error = "Unsupported cipher";
+ err = -EINVAL;
+ goto bad;
+ }
+
+ /* <key> */
+ err = get_key_size(&argv[1]);
+ if (err < 0) {
+ ti->error = "Cannot parse key size";
+ return -EINVAL;
+ }
+ ctx->key_size = err;
+
+ err = inlinecrypt_get_key(argv[1], key_bytes, ctx->key_size);
+ if (err) {
+ ti->error = "Malformed key string";
+ goto bad;
+ }
+
+ /* <iv_offset> */
+ if (sscanf(argv[2], "%llu%c", &ctx->iv_offset, &dummy) != 1) {
+ ti->error = "Invalid iv_offset sector";
+ err = -EINVAL;
+ goto bad;
+ }
+
+ /* <dev_path> */
+ err = dm_get_device(ti, argv[3], dm_table_get_mode(ti->table),
+ &ctx->dev);
+ if (err) {
+ ti->error = "Device lookup failed";
+ goto bad;
+ }
+
+ /* <start> */
+ if (sscanf(argv[4], "%llu%c", &tmpll, &dummy) != 1 ||
+ tmpll != (sector_t)tmpll) {
+ ti->error = "Invalid start sector";
+ err = -EINVAL;
+ goto bad;
+ }
+ ctx->start = tmpll;
+
+ /* optional arguments */
+ ctx->sector_size = SECTOR_SIZE;
+ ctx->key_type = BLK_CRYPTO_KEY_TYPE_RAW;
+ if (argc > 5) {
+ err = inlinecrypt_ctr_optional(ti, argc - 5, &argv[5]);
+ if (err)
+ goto bad;
+ }
+ ctx->sector_bits = ilog2(ctx->sector_size);
+ if (ti->len & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) {
+ ti->error = "Device size is not a multiple of sector_size";
+ err = -EINVAL;
+ goto bad;
+ }
+ if (ctx->iv_offset & ((ctx->sector_size >> SECTOR_SHIFT) - 1)) {
+ ti->error = "Wrong alignment of iv_offset sector";
+ err = -EINVAL;
+ }
+
+ ctx->max_dun = (ctx->iv_offset + ti->len - 1) >>
+ (ctx->sector_bits - SECTOR_SHIFT);
+ dun_bytes = DIV_ROUND_UP(fls64(ctx->max_dun), 8);
+
+ err = blk_crypto_init_key(&ctx->key, key_bytes, ctx->key_size,
+ ctx->key_type, cipher->mode_num,
+ dun_bytes, ctx->sector_size);
+ if (err) {
+ ti->error = "Error initializing blk-crypto key";
+ goto bad;
+ }
+
+ err = blk_crypto_start_using_key(ctx->dev->bdev, &ctx->key);
+ if (err) {
+ ti->error = "Error starting to use blk-crypto";
+ goto bad;
+ }
+
+ ti->num_flush_bios = 1;
+
+ err = 0;
+ goto out;
+
+bad:
+ inlinecrypt_dtr(ti);
+out:
+ memzero_explicit(key_bytes, sizeof(key_bytes));
+ return err;
+}
+
+static int inlinecrypt_map(struct dm_target *ti, struct bio *bio)
+{
+ const struct inlinecrypt_ctx *ctx = ti->private;
+ sector_t sector_in_target;
+ u64 dun[BLK_CRYPTO_DUN_ARRAY_SIZE] = {};
+
+ bio_set_dev(bio, ctx->dev->bdev);
+
+ /*
+ * If the bio is a device-level request which doesn't target a specific
+ * sector, there's nothing more to do.
+ */
+ if (bio_sectors(bio) == 0)
+ return DM_MAPIO_REMAPPED;
+
+ /*
+ * The bio should never have an encryption context already, since
+ * dm-inlinecrypt doesn't pass through any inline encryption
+ * capabilities to the layer above it.
+ */
+ if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
+ return DM_MAPIO_KILL;
+
+ /* Map the bio's sector to the underlying device. (512-byte sectors) */
+ sector_in_target = dm_target_offset(ti, bio->bi_iter.bi_sector);
+ bio->bi_iter.bi_sector = ctx->start + sector_in_target;
+ /*
+ * If the bio doesn't have any data (e.g. if it's a DISCARD request),
+ * there's nothing more to do.
+ */
+ if (!bio_has_data(bio))
+ return DM_MAPIO_REMAPPED;
+
+ /* Calculate the DUN and enforce data-unit (crypto sector) alignment. */
+ dun[0] = ctx->iv_offset + sector_in_target; /* 512-byte sectors */
+ if (dun[0] & ((ctx->sector_size >> SECTOR_SHIFT) - 1))
+ return DM_MAPIO_KILL;
+ dun[0] >>= ctx->sector_bits - SECTOR_SHIFT; /* crypto sectors */
+
+ /*
+ * This check isn't necessary as we should have calculated max_dun
+ * correctly, but be safe.
+ */
+ if (WARN_ON_ONCE(dun[0] > ctx->max_dun))
+ return DM_MAPIO_KILL;
+
+ bio_crypt_set_ctx(bio, &ctx->key, dun, GFP_NOIO);
+
+ /*
+ * Since we've added an encryption context to the bio and
+ * blk-crypto-fallback may be needed to process it, it's necessary to
+ * use the fallback-aware bio submission code rather than
+ * unconditionally returning DM_MAPIO_REMAPPED.
+ *
+ * To get the correct accounting for a dm target in the case where
+ * __blk_crypto_submit_bio() doesn't take ownership of the bio (returns
+ * true), call __blk_crypto_submit_bio() directly and return
+ * DM_MAPIO_REMAPPED in that case, rather than relying on
+ * blk_crypto_submit_bio() which calls submit_bio() in that case.
+ *
+ * TODO: blk-crypto fallback write slow-path currently double-accounts
+ * IO in vmstat, as encrypted bios are submitted via submit_bio().
+ * This does not affect data correctness. Consider fixing this if
+ * a cleaner accounting model for derived bios is introduced.
+ */
+ if (__blk_crypto_submit_bio(bio))
+ return DM_MAPIO_REMAPPED;
+ return DM_MAPIO_SUBMITTED;
+}
+
+static void inlinecrypt_status(struct dm_target *ti, status_type_t type,
+ unsigned int status_flags, char *result,
+ unsigned int maxlen)
+{
+ const struct inlinecrypt_ctx *ctx = ti->private;
+ unsigned int sz = 0;
+ int num_feature_args = 0;
+
+ switch (type) {
+ case STATUSTYPE_INFO:
+ case STATUSTYPE_IMA:
+ result[0] = '\0';
+ break;
+
+ case STATUSTYPE_TABLE:
+ /*
+ * Warning: like dm-crypt, dm-inlinecrypt includes the key in
+ * the returned table. Userspace is responsible for redacting
+ * the key when needed.
+ */
+ DMEMIT("%s %*phN %u %llu %s %llu", ctx->cipher_string,
+ ctx->key.size, ctx->key.bytes,
+ ctx->key_type, ctx->iv_offset,
+ ctx->dev->name, ctx->start);
+ num_feature_args += !!ti->num_discard_bios;
+ if (ctx->sector_size != SECTOR_SIZE)
+ num_feature_args += 2;
+ if (num_feature_args != 0) {
+ DMEMIT(" %d", num_feature_args);
+ if (ti->num_discard_bios)
+ DMEMIT(" allow_discards");
+ if (ctx->sector_size != SECTOR_SIZE) {
+ DMEMIT(" sector_size:%u", ctx->sector_size);
+ DMEMIT(" iv_large_sectors");
+ }
+ }
+ break;
+ }
+}
+
+static int inlinecrypt_prepare_ioctl(struct dm_target *ti,
+ struct block_device **bdev, unsigned int cmd,
+ unsigned long arg, bool *forward)
+{
+ const struct inlinecrypt_ctx *ctx = ti->private;
+ const struct dm_dev *dev = ctx->dev;
+
+ *bdev = dev->bdev;
+
+ /* Only pass ioctls through if the device sizes match exactly. */
+ return ctx->start != 0 || ti->len != bdev_nr_sectors(dev->bdev);
+}
+
+static int inlinecrypt_iterate_devices(struct dm_target *ti,
+ iterate_devices_callout_fn fn,
+ void *data)
+{
+ const struct inlinecrypt_ctx *ctx = ti->private;
+
+ return fn(ti, ctx->dev, ctx->start, ti->len, data);
+}
+
+#ifdef CONFIG_BLK_DEV_ZONED
+static int inlinecrypt_report_zones(struct dm_target *ti,
+ struct dm_report_zones_args *args,
+ unsigned int nr_zones)
+{
+ const struct inlinecrypt_ctx *ctx = ti->private;
+
+ return dm_report_zones(ctx->dev->bdev, ctx->start,
+ ctx->start + dm_target_offset(ti, args->next_sector),
+ args, nr_zones);
+}
+#else
+#define inlinecrypt_report_zones NULL
+#endif
+
+static void inlinecrypt_io_hints(struct dm_target *ti,
+ struct queue_limits *limits)
+{
+ const struct inlinecrypt_ctx *ctx = ti->private;
+ const unsigned int sector_size = ctx->sector_size;
+
+ limits->logical_block_size =
+ max_t(unsigned int, limits->logical_block_size, sector_size);
+ limits->physical_block_size =
+ max_t(unsigned int, limits->physical_block_size, sector_size);
+ limits->io_min = max_t(unsigned int, limits->io_min, sector_size);
+ limits->dma_alignment = limits->logical_block_size - 1;
+}
+
+static struct target_type inlinecrypt_target = {
+ .name = "inlinecrypt",
+ .version = {1, 0, 0},
+ /*
+ * Do not set DM_TARGET_PASSES_CRYPTO, since dm-inlinecrypt consumes the
+ * crypto capability itself.
+ */
+ .features = DM_TARGET_ZONED_HM,
+ .module = THIS_MODULE,
+ .ctr = inlinecrypt_ctr,
+ .dtr = inlinecrypt_dtr,
+ .map = inlinecrypt_map,
+ .status = inlinecrypt_status,
+ .prepare_ioctl = inlinecrypt_prepare_ioctl,
+ .iterate_devices = inlinecrypt_iterate_devices,
+ .report_zones = inlinecrypt_report_zones,
+ .io_hints = inlinecrypt_io_hints,
+};
+
+module_dm(inlinecrypt);
+
+MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
+MODULE_AUTHOR("Linlin Zhang <linlin.zhang@oss.qualcomm.com>");
+MODULE_DESCRIPTION(DM_NAME " target for inline encryption");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index a529174c94cf3..ac77dc0ca2255 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -259,6 +259,80 @@ static void free_cell(struct hash_cell *hc)
}
}
+#ifdef CONFIG_IMA
+
+/*
+ * Called while holding to _hash_lock, to guarantee the ordering of the
+ * following dm_ima_measure_on_* functions, which should be called
+ * right after dropping the _hash_lock
+ */
+static unsigned int dm_ima_init_context(struct hash_cell *hc,
+ struct dm_ima_context *context,
+ bool need_idx)
+{
+ lockdep_assert_held(&_hash_lock);
+
+ if (unlikely(!context))
+ return need_idx ? hc->md->ima.update_idx++ : 0;
+
+ context->update_idx = hc->md->ima.update_idx++;
+ strcpy(context->dev_name, hc->name);
+ strcpy(context->dev_uuid, hc->uuid ? : "");
+
+ return context->update_idx;
+}
+
+/*
+ * Called by do_resume() to guarantee correct ordering, since do_resume()
+ * does not grab the _hash_lock when the table is not getting swapped or
+ * when actually swapping the active table
+ */
+static bool dm_ima_need_measure(struct mapped_device *md,
+ struct dm_table *table,
+ struct dm_ima_context *context)
+{
+ int srcu_idx;
+ struct hash_cell *hc;
+ bool need_measure = false;
+
+ if (unlikely(!context))
+ return false;
+
+ down_write(&_hash_lock);
+ /* Check if the device has been removed */
+ hc = dm_get_mdptr(md);
+ if (hc) {
+ /*
+ * If we have a table, we need to make sure that it's the
+ * active table. Otherwise we raced with another process
+ * setting the active table and it will do the measurement
+ */
+ if (!table || dm_get_live_table(md, &srcu_idx) == table) {
+ dm_ima_init_context(hc, context, false);
+ need_measure = true;
+ }
+ if (table)
+ dm_put_live_table(md, srcu_idx);
+ }
+ up_write(&_hash_lock);
+
+ return need_measure;
+}
+#else
+static inline unsigned int dm_ima_init_context(struct hash_cell *hc,
+ struct dm_ima_context *context,
+ bool neex_idx)
+{
+ return 0;
+}
+static inline bool dm_ima_need_measure(struct mapped_device *md,
+ struct dm_table *table,
+ struct dm_ima_context *context)
+{
+ return false;
+}
+#endif
+
/*
* The kdev_t and uuid of a device can never change once it is
* initially inserted.
@@ -344,7 +418,10 @@ static int dm_hash_remove_all(unsigned flags)
struct hash_cell *hc;
struct mapped_device *md;
struct dm_table *t;
+ struct dm_ima_context *ima_context = NULL;
+ unsigned int ima_idx;
+ dm_ima_alloc_context(&ima_context, true);
retry:
dev_skipped = 0;
@@ -353,6 +430,7 @@ retry:
for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
if (flags & DM_REMOVE_INTERRUPTIBLE && fatal_signal_pending(current)) {
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -EINTR;
}
@@ -367,6 +445,7 @@ retry:
continue;
}
+ ima_idx = dm_ima_init_context(hc, ima_context, true);
t = __hash_remove(hc);
up_write(&_hash_lock);
@@ -375,7 +454,7 @@ retry:
dm_sync_table(md);
dm_table_destroy(t);
}
- dm_ima_measure_on_device_remove(md, true);
+ dm_ima_measure_on_device_remove(md, true, ima_context, ima_idx);
dm_put(md);
if (likely(flags & DM_REMOVE_KEEP_OPEN_DEVICES))
dm_destroy(md);
@@ -396,6 +475,7 @@ retry:
if (dev_skipped && !(flags & DM_REMOVE_ONLY_DEFERRED))
DMWARN("remove_all left %d open device(s)", dev_skipped);
+ dm_ima_free_context(ima_context);
return 0;
}
@@ -443,6 +523,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
struct mapped_device *md;
unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
int srcu_idx;
+ struct dm_ima_context *ima_context = NULL;
/*
* duplicate new.
@@ -451,6 +532,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
if (!new_data)
return ERR_PTR(-ENOMEM);
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
/*
@@ -467,6 +549,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
param->name, new);
dm_put(hc->md);
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
kfree(new_data);
return ERR_PTR(-EBUSY);
}
@@ -479,6 +562,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
DMERR("Unable to rename non-existent device, %s to %s%s",
param->name, change_uuid ? "uuid " : "", new);
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
kfree(new_data);
return ERR_PTR(-ENXIO);
}
@@ -492,6 +576,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
param->name, new, hc->uuid);
dm_put(hc->md);
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
kfree(new_data);
return ERR_PTR(-EINVAL);
}
@@ -514,9 +599,11 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
md = hc->md;
- dm_ima_measure_on_device_rename(md);
+ dm_ima_init_context(hc, ima_context, false);
up_write(&_hash_lock);
+ dm_ima_measure_on_device_rename(md, ima_context);
+ dm_ima_free_context(ima_context);
kfree(old_name);
return md;
@@ -995,13 +1082,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
struct mapped_device *md;
int r;
struct dm_table *t;
+ struct dm_ima_context *ima_context = NULL;
+ unsigned int ima_idx;
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -ENXIO;
}
@@ -1015,14 +1106,17 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
up_write(&_hash_lock);
dm_put(md);
+ dm_ima_free_context(ima_context);
return 0;
}
DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
up_write(&_hash_lock);
dm_put(md);
+ dm_ima_free_context(ima_context);
return r;
}
+ ima_idx = dm_ima_init_context(hc, ima_context, true);
t = __hash_remove(hc);
up_write(&_hash_lock);
@@ -1033,7 +1127,8 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
param->flags &= ~DM_DEFERRED_REMOVE;
- dm_ima_measure_on_device_remove(md, false);
+ dm_ima_measure_on_device_remove(md, false, ima_context, ima_idx);
+ dm_ima_free_context(ima_context);
if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
param->flags |= DM_UEVENT_GENERATED_FLAG;
@@ -1169,13 +1264,16 @@ static int do_resume(struct dm_ioctl *param)
struct mapped_device *md;
struct dm_table *new_map, *old_map = NULL;
bool need_resize_uevent = false;
+ struct dm_ima_context *ima_context = NULL;
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -ENXIO;
}
@@ -1184,13 +1282,15 @@ static int do_resume(struct dm_ioctl *param)
new_map = hc->new_map;
hc->new_map = NULL;
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
-
+ if (new_map)
+ dm_ima_init_context(hc, ima_context, false);
up_write(&_hash_lock);
/* Do we need to load a new map ? */
if (new_map) {
sector_t old_size, new_size;
+ dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_SAVE);
/* Suspend if it isn't already suspended */
if (param->flags & DM_SKIP_LOCKFS_FLAG)
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
@@ -1204,6 +1304,8 @@ static int do_resume(struct dm_ioctl *param)
if (hc && !hc->new_map) {
hc->new_map = new_map;
new_map = NULL;
+ dm_ima_init_context(hc, ima_context,
+ false);
} else {
r = -ENXIO;
}
@@ -1211,7 +1313,9 @@ static int do_resume(struct dm_ioctl *param)
if (new_map) {
dm_sync_table(md);
dm_table_destroy(new_map);
- }
+ } else
+ dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_RESTORE);
+ dm_ima_free_context(ima_context);
dm_put(md);
return r;
}
@@ -1222,9 +1326,12 @@ static int do_resume(struct dm_ioctl *param)
if (IS_ERR(old_map)) {
dm_sync_table(md);
dm_table_destroy(new_map);
+ dm_ima_free_context(ima_context);
dm_put(md);
return PTR_ERR(old_map);
}
+ if (dm_ima_need_measure(md, new_map, ima_context))
+ dm_ima_measure_on_device_resume(md, true, ima_context);
new_size = dm_get_size(md);
if (old_size && new_size && old_size != new_size)
need_resize_uevent = true;
@@ -1238,7 +1345,10 @@ static int do_resume(struct dm_ioctl *param)
if (dm_suspended_md(md)) {
r = dm_resume(md);
if (!r) {
- dm_ima_measure_on_device_resume(md, new_map ? true : false);
+ if (!new_map && dm_ima_need_measure(md, NULL,
+ ima_context))
+ dm_ima_measure_on_device_resume(md, false,
+ ima_context);
if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
param->flags |= DM_UEVENT_GENERATED_FLAG;
@@ -1255,6 +1365,7 @@ static int do_resume(struct dm_ioctl *param)
if (!r)
__dev_status(md, param);
+ dm_ima_free_context(ima_context);
dm_put(md);
return r;
}
@@ -1532,11 +1643,12 @@ static bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new)
static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size)
{
- int r;
+ int r, srcu_idx;
struct hash_cell *hc;
struct dm_table *t, *old_map = NULL;
struct mapped_device *md;
struct target_type *immutable_target_type;
+ struct dm_ima_context *ima_context = NULL;
md = find_device(param);
if (!md)
@@ -1552,8 +1664,6 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
if (r)
goto err_unlock_md_type;
- dm_ima_measure_on_table_load(t, STATUSTYPE_IMA);
-
immutable_target_type = dm_get_immutable_target_type(md);
if (immutable_target_type &&
(immutable_target_type != dm_table_get_immutable_target_type(t)) &&
@@ -1580,12 +1690,14 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
dm_unlock_md_type(md);
+ dm_ima_alloc_context(&ima_context, false);
/* stage inactive table */
down_write(&_hash_lock);
hc = dm_get_mdptr(md);
if (!hc) {
DMERR("device has been removed from the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
r = -ENXIO;
goto err_destroy_table;
}
@@ -1593,8 +1705,15 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
if (hc->new_map)
old_map = hc->new_map;
hc->new_map = t;
+ dm_ima_init_context(hc, ima_context, false);
+ /* Make sure new_map doesn't get freed before we measure it*/
+ dm_get_live_table(md, &srcu_idx);
up_write(&_hash_lock);
+ dm_ima_measure_on_table_load(t, ima_context);
+ dm_ima_free_context(ima_context);
+ dm_put_live_table(md, srcu_idx);
+
param->flags |= DM_INACTIVE_PRESENT_FLAG;
__dev_status(md, param);
@@ -1622,25 +1741,29 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
struct hash_cell *hc;
struct mapped_device *md;
struct dm_table *old_map = NULL;
- bool has_new_map = false;
+ struct dm_ima_context *ima_context = NULL;
+ dm_ima_alloc_context(&ima_context, true);
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
if (!hc) {
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
up_write(&_hash_lock);
+ dm_ima_free_context(ima_context);
return -ENXIO;
}
if (hc->new_map) {
old_map = hc->new_map;
hc->new_map = NULL;
- has_new_map = true;
}
+ dm_ima_init_context(hc, ima_context, false);
md = hc->md;
up_write(&_hash_lock);
+ dm_ima_measure_on_table_clear(md, ima_context);
+ dm_ima_free_context(ima_context);
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
__dev_status(md, param);
@@ -1649,7 +1772,6 @@ static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_s
dm_sync_table(md);
dm_table_destroy(old_map);
}
- dm_ima_measure_on_table_clear(md, has_new_map);
dm_put(md);
return 0;
@@ -1816,8 +1938,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para
goto out_argv;
table = dm_get_live_table(md, &srcu_idx);
- if (!table)
+ if (!table) {
+ DMERR("The device has no table.");
+ r = -EINVAL;
goto out_table;
+ }
if (dm_deleting_md(md)) {
r = -ENXIO;
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index c5dc083c72441..8f5a5e1342a95 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -3831,6 +3831,7 @@ static void raid_presuspend(struct dm_target *ti)
* resume, raid_postsuspend() is too late.
*/
set_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
+ set_bit(MD_DM_SUSPENDING, &mddev->flags);
if (!reshape_interrupted(mddev))
return;
@@ -3847,13 +3848,16 @@ static void raid_presuspend(struct dm_target *ti)
static void raid_presuspend_undo(struct dm_target *ti)
{
struct raid_set *rs = ti->private;
+ struct mddev *mddev = &rs->md;
+ clear_bit(MD_DM_SUSPENDING, &mddev->flags);
clear_bit(RT_FLAG_RS_FROZEN, &rs->runtime_flags);
}
static void raid_postsuspend(struct dm_target *ti)
{
struct raid_set *rs = ti->private;
+ struct mddev *mddev = &rs->md;
if (!test_and_set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) {
/*
@@ -3864,6 +3868,8 @@ static void raid_postsuspend(struct dm_target *ti)
mddev_suspend(&rs->md, false);
rs->md.ro = MD_RDONLY;
}
+ clear_bit(MD_DM_SUSPENDING, &mddev->flags);
+
}
static void attempt_restore_of_faulty_devices(struct raid_set *rs)
diff --git a/drivers/md/dm-vdo/indexer/index-layout.c b/drivers/md/dm-vdo/indexer/index-layout.c
index 5f4ce4ab1b1ea..2d529250000eb 100644
--- a/drivers/md/dm-vdo/indexer/index-layout.c
+++ b/drivers/md/dm-vdo/indexer/index-layout.c
@@ -282,7 +282,7 @@ static void create_unique_nonce_data(u8 *buffer)
u32 rand;
size_t offset = 0;
- get_random_bytes(&rand, sizeof(u32));
+ rand = get_random_u32();
memcpy(buffer + offset, &now, sizeof(now));
offset += sizeof(now);
memcpy(buffer + offset, &rand, sizeof(rand));
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e178fe19973ea..7287bed6eb642 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2098,8 +2098,17 @@ static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob,
WARN_ON_ONCE(!dm_tio_is_normal(&io->tio));
/* don't poll if the mapped io is done */
- if (atomic_read(&io->io_count) > 1)
- bio_poll(&io->tio.clone, iob, flags);
+ if (atomic_read(&io->io_count) > 1) {
+ /*
+ * DM hides the target queues from the upper poller, which may
+ * decide it is safe to spin on a single stacked queue. Do not
+ * pass that spinning policy down to a target queue: one slow
+ * clone could keep the task inside dm_poll_bio() for a long
+ * time. Poll target bios once and let the caller decide
+ * whether to keep polling, reap completions or reschedule.
+ */
+ bio_poll(&io->tio.clone, iob, flags | BLK_POLL_ONESHOT);
+ }
/* bio_poll holds the last reference */
return atomic_read(&io->io_count) == 1;
@@ -2546,7 +2555,7 @@ int dm_create(int minor, struct mapped_device **result)
if (!md)
return -ENXIO;
- dm_ima_reset_data(md);
+ dm_ima_init(md);
*result = md;
return 0;
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 52c3780860464..9e5100609d120 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -346,6 +346,7 @@ struct md_cluster_operations;
* @MD_HAS_SUPERBLOCK: There is persistence sb in member disks.
* @MD_FAILLAST_DEV: Allow last rdev to be removed.
* @MD_SERIALIZE_POLICY: Enforce write IO is not reordered, just used by raid1.
+ * @MD_DM_SUSPENDING: This DM raid device is suspending.
*
* change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added
*/
@@ -365,6 +366,7 @@ enum mddev_flags {
MD_HAS_SUPERBLOCK,
MD_FAILLAST_DEV,
MD_SERIALIZE_POLICY,
+ MD_DM_SUSPENDING,
};
enum mddev_sb_flags {
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index ebcb193176702..d0d7f96fd7cd9 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6042,8 +6042,11 @@ out_release:
raid5_release_stripe(sh);
out:
if (ret == STRIPE_SCHEDULE_AND_RETRY && reshape_interrupted(mddev)) {
- bi->bi_status = BLK_STS_RESOURCE;
- ret = STRIPE_WAIT_RESHAPE;
+ if (!mddev_is_dm(mddev) ||
+ test_bit(MD_DM_SUSPENDING, &mddev->flags)) {
+ bi->bi_status = BLK_STS_RESOURCE;
+ ret = STRIPE_WAIT_RESHAPE;
+ }
pr_err_ratelimited("dm-raid456: io across reshape position while reshape can't make progress");
}
return ret;