ANDROID: dm: add support for passing through inline crypto support
Update the device-mapper core to support exposing the inline crypto support of the underlying device(s) through the device-mapper device. This works by creating a "passthrough keyslot manager" for the dm device, which declares support for the set of (crypto_mode, data_unit_size) combos which all the underlying devices support. When a supported combo is used, the bio cloning code handles cloning the crypto context to the bios for all the underlying devices. When an unsupported combo is used, the blk-crypto fallback is used as usual. Crypto support on each underlying device is ignored unless the corresponding dm target opts into exposing it. This is needed because for inline crypto to semantically operate on the original bio, the data must not be transformed by the dm target. Thus, targets like dm-linear can expose crypto support of the underlying device, but targets like dm-crypt can't. (dm-crypt could use inline crypto itself, though.) When a key is evicted from the dm device, it is evicted from all underlying devices. Bug: 137270441 Bug: 147814592 Change-Id: If28b574f2e28268db5eb9f325d4cf8f96cb63e3f Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
parent
1fc06baadc
commit
695cfeb088
@ -492,6 +492,34 @@ struct keyslot_manager *keyslot_manager_create_passthrough(
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(keyslot_manager_create_passthrough);
|
||||
|
||||
/**
|
||||
* keyslot_manager_intersect_modes() - restrict supported modes by child device
|
||||
* @parent: The keyslot manager for parent device
|
||||
* @child: The keyslot manager for child device, or NULL
|
||||
*
|
||||
* Clear any crypto mode support bits in @parent that aren't set in @child.
|
||||
* If @child is NULL, then all parent bits are cleared.
|
||||
*
|
||||
* Only use this when setting up the keyslot manager for a layered device,
|
||||
* before it's been exposed yet.
|
||||
*/
|
||||
void keyslot_manager_intersect_modes(struct keyslot_manager *parent,
|
||||
const struct keyslot_manager *child)
|
||||
{
|
||||
if (child) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(child->crypto_mode_supported); i++) {
|
||||
parent->crypto_mode_supported[i] &=
|
||||
child->crypto_mode_supported[i];
|
||||
}
|
||||
} else {
|
||||
memset(parent->crypto_mode_supported, 0,
|
||||
sizeof(parent->crypto_mode_supported));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(keyslot_manager_intersect_modes);
|
||||
|
||||
/**
|
||||
* keyslot_manager_derive_raw_secret() - Derive software secret from wrapped key
|
||||
* @ksm: The keyslot manager
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <linux/blk-mq.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/dax.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
|
||||
#define DM_MSG_PREFIX "table"
|
||||
|
||||
@ -1628,6 +1630,54 @@ static void dm_table_verify_integrity(struct dm_table *t)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
|
||||
static int device_intersect_crypto_modes(struct dm_target *ti,
|
||||
struct dm_dev *dev, sector_t start,
|
||||
sector_t len, void *data)
|
||||
{
|
||||
struct keyslot_manager *parent = data;
|
||||
struct keyslot_manager *child = bdev_get_queue(dev->bdev)->ksm;
|
||||
|
||||
keyslot_manager_intersect_modes(parent, child);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the inline crypto modes supported by 'q->ksm' to be the intersection
|
||||
* of the modes supported by all targets in the table.
|
||||
*
|
||||
* For any mode to be supported at all, all targets must have explicitly
|
||||
* declared that they can pass through inline crypto support. For a particular
|
||||
* mode to be supported, all underlying devices must also support it.
|
||||
*
|
||||
* Assume that 'q->ksm' initially declares all modes to be supported.
|
||||
*/
|
||||
static void dm_calculate_supported_crypto_modes(struct dm_table *t,
|
||||
struct request_queue *q)
|
||||
{
|
||||
struct dm_target *ti;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < dm_table_get_num_targets(t); i++) {
|
||||
ti = dm_table_get_target(t, i);
|
||||
|
||||
if (!ti->may_passthrough_inline_crypto) {
|
||||
keyslot_manager_intersect_modes(q->ksm, NULL);
|
||||
return;
|
||||
}
|
||||
if (!ti->type->iterate_devices)
|
||||
continue;
|
||||
ti->type->iterate_devices(ti, device_intersect_crypto_modes,
|
||||
q->ksm);
|
||||
}
|
||||
}
|
||||
#else /* CONFIG_BLK_INLINE_ENCRYPTION */
|
||||
static inline void dm_calculate_supported_crypto_modes(struct dm_table *t,
|
||||
struct request_queue *q)
|
||||
{
|
||||
}
|
||||
#endif /* !CONFIG_BLK_INLINE_ENCRYPTION */
|
||||
|
||||
static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev,
|
||||
sector_t start, sector_t len, void *data)
|
||||
{
|
||||
@ -1944,6 +1994,8 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
|
||||
|
||||
dm_table_verify_integrity(t);
|
||||
|
||||
dm_calculate_supported_crypto_modes(t, q);
|
||||
|
||||
/*
|
||||
* Some devices don't use blk_integrity but still want stable pages
|
||||
* because they do their own checksumming.
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/pr.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/blk-crypto.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
|
||||
#define DM_MSG_PREFIX "core"
|
||||
|
||||
@ -1862,6 +1864,8 @@ static void dm_init_normal_md_queue(struct mapped_device *md)
|
||||
md->queue->backing_dev_info->congested_fn = dm_any_congested;
|
||||
}
|
||||
|
||||
static void dm_destroy_inline_encryption(struct request_queue *q);
|
||||
|
||||
static void cleanup_mapped_device(struct mapped_device *md)
|
||||
{
|
||||
if (md->wq)
|
||||
@ -1883,8 +1887,10 @@ static void cleanup_mapped_device(struct mapped_device *md)
|
||||
put_disk(md->disk);
|
||||
}
|
||||
|
||||
if (md->queue)
|
||||
if (md->queue) {
|
||||
dm_destroy_inline_encryption(md->queue);
|
||||
blk_cleanup_queue(md->queue);
|
||||
}
|
||||
|
||||
cleanup_srcu_struct(&md->io_barrier);
|
||||
|
||||
@ -2243,6 +2249,89 @@ struct queue_limits *dm_get_queue_limits(struct mapped_device *md)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dm_get_queue_limits);
|
||||
|
||||
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
|
||||
struct dm_keyslot_evict_args {
|
||||
const struct blk_crypto_key *key;
|
||||
int err;
|
||||
};
|
||||
|
||||
static int dm_keyslot_evict_callback(struct dm_target *ti, struct dm_dev *dev,
|
||||
sector_t start, sector_t len, void *data)
|
||||
{
|
||||
struct dm_keyslot_evict_args *args = data;
|
||||
int err;
|
||||
|
||||
err = blk_crypto_evict_key(dev->bdev->bd_queue, args->key);
|
||||
if (!args->err)
|
||||
args->err = err;
|
||||
/* Always try to evict the key from all devices. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When an inline encryption key is evicted from a device-mapper device, evict
|
||||
* it from all the underlying devices.
|
||||
*/
|
||||
static int dm_keyslot_evict(struct keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key, unsigned int slot)
|
||||
{
|
||||
struct mapped_device *md = keyslot_manager_private(ksm);
|
||||
struct dm_keyslot_evict_args args = { key };
|
||||
struct dm_table *t;
|
||||
int srcu_idx;
|
||||
int i;
|
||||
struct dm_target *ti;
|
||||
|
||||
t = dm_get_live_table(md, &srcu_idx);
|
||||
if (!t)
|
||||
return 0;
|
||||
for (i = 0; i < dm_table_get_num_targets(t); i++) {
|
||||
ti = dm_table_get_target(t, i);
|
||||
if (!ti->type->iterate_devices)
|
||||
continue;
|
||||
ti->type->iterate_devices(ti, dm_keyslot_evict_callback, &args);
|
||||
}
|
||||
dm_put_live_table(md, srcu_idx);
|
||||
return args.err;
|
||||
}
|
||||
|
||||
static struct keyslot_mgmt_ll_ops dm_ksm_ll_ops = {
|
||||
.keyslot_evict = dm_keyslot_evict,
|
||||
};
|
||||
|
||||
static int dm_init_inline_encryption(struct mapped_device *md)
|
||||
{
|
||||
unsigned int mode_masks[BLK_ENCRYPTION_MODE_MAX];
|
||||
|
||||
/*
|
||||
* Start out with all crypto mode support bits set. Any unsupported
|
||||
* bits will be cleared later when calculating the device restrictions.
|
||||
*/
|
||||
memset(mode_masks, 0xFF, sizeof(mode_masks));
|
||||
|
||||
md->queue->ksm = keyslot_manager_create_passthrough(&dm_ksm_ll_ops,
|
||||
mode_masks, md);
|
||||
if (!md->queue->ksm)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dm_destroy_inline_encryption(struct request_queue *q)
|
||||
{
|
||||
keyslot_manager_destroy(q->ksm);
|
||||
q->ksm = NULL;
|
||||
}
|
||||
#else /* CONFIG_BLK_INLINE_ENCRYPTION */
|
||||
static inline int dm_init_inline_encryption(struct mapped_device *md)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dm_destroy_inline_encryption(struct request_queue *q)
|
||||
{
|
||||
}
|
||||
#endif /* !CONFIG_BLK_INLINE_ENCRYPTION */
|
||||
|
||||
/*
|
||||
* Setup the DM device's queue based on md's type
|
||||
*/
|
||||
@ -2276,6 +2365,13 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t)
|
||||
DMERR("Cannot calculate initial queue limits");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dm_init_inline_encryption(md);
|
||||
if (r) {
|
||||
DMERR("Cannot initialize inline encryption");
|
||||
return r;
|
||||
}
|
||||
|
||||
dm_table_set_restrictions(t, md->queue, &limits);
|
||||
blk_register_queue(md->disk);
|
||||
|
||||
|
@ -317,6 +317,12 @@ struct dm_target {
|
||||
* whether or not its underlying devices have support.
|
||||
*/
|
||||
bool discards_supported:1;
|
||||
|
||||
/*
|
||||
* Set if inline crypto capabilities from this target's underlying
|
||||
* device(s) can be exposed via the device-mapper device.
|
||||
*/
|
||||
bool may_passthrough_inline_crypto:1;
|
||||
};
|
||||
|
||||
/* Each target can link one of these into the table */
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
#include <linux/bio.h>
|
||||
|
||||
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
|
||||
|
||||
struct keyslot_manager;
|
||||
|
||||
/**
|
||||
@ -69,9 +71,14 @@ struct keyslot_manager *keyslot_manager_create_passthrough(
|
||||
const unsigned int crypto_mode_supported[BLK_ENCRYPTION_MODE_MAX],
|
||||
void *ll_priv_data);
|
||||
|
||||
void keyslot_manager_intersect_modes(struct keyslot_manager *parent,
|
||||
const struct keyslot_manager *child);
|
||||
|
||||
int keyslot_manager_derive_raw_secret(struct keyslot_manager *ksm,
|
||||
const u8 *wrapped_key,
|
||||
unsigned int wrapped_key_size,
|
||||
u8 *secret, unsigned int secret_size);
|
||||
|
||||
#endif /* CONFIG_BLK_INLINE_ENCRYPTION */
|
||||
|
||||
#endif /* __LINUX_KEYSLOT_MANAGER_H */
|
||||
|
Loading…
Reference in New Issue
Block a user