Merge tag 'for-7.2/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm

Pull device mapper updates from Mikulas Patocka:

 - small cleanups in dm-vdo, dm-raid, dm-cache, dm-zoned-metadata

 - rework of dm-ima

 - introduce dm-inlinecrypt

 - fix wrong return value in dm-ioctl

 - fix rcu stall when polling

* tag 'for-7.2/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm-zoned-metadata: Use strscpy() to copy device name
  dm cache: make smq background work limit configurable
  dm-inlinecrypt: add support for hardware-wrapped keys
  dm: limit target bio polling to one shot
  dm-ioctl: report an error if a device has no table
  dm: add documentation for dm-inlinecrypt target
  dm-inlinecrypt: add target for inline block device encryption
  block: export blk-crypto symbols required by dm-inlinecrypt
  dm-ima: use active table's size if available
  dm-ima: Fail more gracefully in dm_ima_measure_on_*
  dm-ima: Handle race between rename and table swap
  dm-ima: Fix issues with dm_ima_measure_on_device_rename
  dm-ima: remove new_map from dm_ima_measure_on_device_clear
  dm-ima: Fix UAF errors and measuring incorrect context
  dm-ima: don't copy the active table to the inactive table
  dm-ima: Remove status_flags from dm_ima_measure_on_table_load()
  dm-ima: remove broken last_target_measured logic
  dm-ima: remove dm_ima_reset_data()
  dm-raid: only requeue bios when dm is suspending
  dm vdo: use get_random_u32() where appropriate
This commit is contained in:
Linus Torvalds
2026-06-16 13:20:54 +05:30
13 changed files with 1205 additions and 308 deletions

View File

@@ -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"

View File

@@ -15,6 +15,7 @@ Device Mapper
dm-flakey
dm-ima
dm-init
dm-inlinecrypt
dm-integrity
dm-io
dm-log

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -21,6 +21,16 @@
/*----------------------------------------------------------------*/
/*
* Maximum number of concurrent background work items (promotions,
* demotions, writebacks) that can be queued in the background tracker.
* Tuneable via the module parameter smq_max_background_work.
* Only affects newly created cache devices.
*/
static unsigned int smq_max_background_work = 4096;
module_param(smq_max_background_work, uint, 0644);
MODULE_PARM_DESC(smq_max_background_work, "Max concurrent background work items");
/*
* Safe division functions that return zero on divide by zero.
*/
@@ -1820,7 +1830,7 @@ __smq_create(dm_cblock_t cache_size, sector_t origin_size, sector_t cache_block_
mq->next_hotspot_period = jiffies;
mq->next_cache_period = jiffies;
mq->bg_work = btracker_create(4096); /* FIXME: hard coded value */
mq->bg_work = btracker_create(max(1u, smq_max_background_work));
if (!mq->bg_work)
goto bad_btracker;

View File

@@ -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);
}
void dm_ima_alloc_context(struct dm_ima_context **context, bool noio)
{
*context = dm_ima_alloc(sizeof(struct dm_ima_context), noio);
}
void dm_ima_free_context(struct dm_ima_context *context)
{
if (likely(context)) {
kfree(context->table.device_metadata);
kfree(context->table.hash);
kfree(context);
}
}
*dev_uuid = dm_ima_alloc(DM_UUID_LEN*2, noio);
if (!(*dev_uuid)) {
r = -ENOMEM;
goto error;
}
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);
}
r = dm_copy_name_and_uuid(md, *dev_name, *dev_uuid);
if (r)
goto error;
fix_separator_chars(dev_name);
fix_separator_chars(dev_uuid);
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)
static sector_t dm_ima_capacity(struct mapped_device *md)
{
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)
{
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,65 +323,54 @@ void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_fl
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;
if (unlikely(!context))
return;
wait_to_measure(&md->ima, context->update_idx);
if (swap) {
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)
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;
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;
}
}
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,
@@ -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;
wait_to_measure(&md->ima, idx);
if (unlikely(!context))
goto exit;
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, noio);
if (!device_table_data)
goto exit;
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
if (capacity_len < 0) {
kfree(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);
memset(&md->ima.active_table, 0, sizeof(md->ima.active_table));
memset(&md->ima.inactive_table, 0, sizeof(md->ima.inactive_table));
dm_ima_reset_data(md);
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;
if (unlikely(!context))
return;
wait_to_measure(&md->ima, context->update_idx);
device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, noio);
if (!device_table_data)
return;
goto error;
capacity_len = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
if (capacity_len < 0)
goto error1;
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.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);
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));
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;
}
}
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);
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);
exit:
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);
}

View File

@@ -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 */

618
drivers/md/dm-inlinecrypt.c Normal file
View File

@@ -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");

View File

@@ -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 @@ static int dm_hash_remove_all(unsigned flags)
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 @@ static int dm_hash_remove_all(unsigned flags)
continue;
}
ima_idx = dm_ima_init_context(hc, ima_context, true);
t = __hash_remove(hc);
up_write(&_hash_lock);
@@ -375,7 +454,7 @@ static int dm_hash_remove_all(unsigned flags)
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 @@ static int dm_hash_remove_all(unsigned flags)
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;

View File

@@ -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));

View File

@@ -2871,7 +2871,7 @@ int dmz_ctr_metadata(struct dmz_dev *dev, int num_dev,
if (!zmd)
return -ENOMEM;
strcpy(zmd->devname, devname);
strscpy(zmd->devname, devname);
zmd->dev = dev;
zmd->nr_devs = num_dev;
zmd->mblk_rbtree = RB_ROOT;

View File

@@ -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;