Merge 5.4-rc1-prereleae into android-mainline

To make the 5.4-rc1 merge easier, merge at a prerelease point in time
before the final release happens.

Signed-off-by: Greg Kroah-Hartman <[email protected]>
Change-Id: I29b683c837ed1a3324644dbf9bf863f30740cd0b
This commit is contained in:
Greg Kroah-Hartman 2019-09-23 14:14:08 +02:00
commit 896be8f44d
3550 changed files with 333627 additions and 71650 deletions

View file

@ -271,6 +271,7 @@ config DM_CRYPT
depends on BLK_DEV_DM
select CRYPTO
select CRYPTO_CBC
select CRYPTO_ESSIV
---help---
This device-mapper target allows you to create a device that
transparently encrypts the data on it. You'll need to activate
@ -346,6 +347,20 @@ config DM_ERA
over time. Useful for maintaining cache coherency when using
vendor snapshots.
config DM_CLONE
tristate "Clone target (EXPERIMENTAL)"
depends on BLK_DEV_DM
default n
select DM_PERSISTENT_DATA
---help---
dm-clone produces a one-to-one copy of an existing, read-only source
device into a writable destination device. The cloned device is
visible/mountable immediately and the copy of the source device to the
destination device happens in the background, in parallel with user
I/O.
If unsure, say N.
config DM_MIRROR
tristate "Mirror target"
depends on BLK_DEV_DM
@ -490,6 +505,18 @@ config DM_VERITY
If unsure, say N.
config DM_VERITY_VERIFY_ROOTHASH_SIG
def_bool n
bool "Verity data device root hash signature verification support"
depends on DM_VERITY
select SYSTEM_DATA_VERIFICATION
help
Add ability for dm-verity device to be validated if the
pre-generated tree of cryptographic checksums passed has a pkcs#7
signature file that can validate the roothash of the tree.
If unsure, say N.
config DM_VERITY_AVB
tristate "Support AVB specific verity error behavior"
depends on DM_VERITY
@ -499,6 +526,8 @@ config DM_VERITY_AVB
specified on the kernel command-line when non-transient error
occurs (followed by a panic).
If unsure, say N.
config DM_VERITY_FEC
bool "Verity forward error correction support"
depends on DM_VERITY

View file

@ -18,6 +18,7 @@ dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o \
dm-cache-background-tracker.o
dm-cache-smq-y += dm-cache-policy-smq.o
dm-era-y += dm-era-target.o
dm-clone-y += dm-clone-target.o dm-clone-metadata.o
dm-verity-y += dm-verity-target.o
md-mod-y += md.o md-bitmap.o
raid456-y += raid5.o raid5-cache.o raid5-ppl.o
@ -65,6 +66,7 @@ obj-$(CONFIG_DM_VERITY) += dm-verity.o
obj-$(CONFIG_DM_CACHE) += dm-cache.o
obj-$(CONFIG_DM_CACHE_SMQ) += dm-cache-smq.o
obj-$(CONFIG_DM_ERA) += dm-era.o
obj-$(CONFIG_DM_CLONE) += dm-clone.o
obj-$(CONFIG_DM_LOG_WRITES) += dm-log-writes.o
obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
obj-$(CONFIG_DM_ZONED) += dm-zoned.o
@ -86,3 +88,7 @@ endif
ifeq ($(CONFIG_DM_VERITY_FEC),y)
dm-verity-objs += dm-verity-fec.o
endif
ifeq ($(CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG),y)
dm-verity-objs += dm-verity-verify-sig.o
endif

View file

@ -33,7 +33,8 @@
#define DM_BUFIO_MEMORY_PERCENT 2
#define DM_BUFIO_VMALLOC_PERCENT 25
#define DM_BUFIO_WRITEBACK_PERCENT 75
#define DM_BUFIO_WRITEBACK_RATIO 3
#define DM_BUFIO_LOW_WATERMARK_RATIO 16
/*
* Check buffer ages in this interval (seconds)
@ -132,12 +133,14 @@ enum data_mode {
struct dm_buffer {
struct rb_node node;
struct list_head lru_list;
struct list_head global_list;
sector_t block;
void *data;
unsigned char data_mode; /* DATA_MODE_* */
unsigned char list_mode; /* LIST_* */
blk_status_t read_error;
blk_status_t write_error;
unsigned accessed;
unsigned hold_count;
unsigned long state;
unsigned long last_accessed;
@ -192,7 +195,11 @@ static unsigned long dm_bufio_cache_size;
*/
static unsigned long dm_bufio_cache_size_latch;
static DEFINE_SPINLOCK(param_spinlock);
static DEFINE_SPINLOCK(global_spinlock);
static LIST_HEAD(global_queue);
static unsigned long global_num = 0;
/*
* Buffers are freed after this timeout
@ -208,11 +215,6 @@ static unsigned long dm_bufio_current_allocated;
/*----------------------------------------------------------------*/
/*
* Per-client cache: dm_bufio_cache_size / dm_bufio_client_count
*/
static unsigned long dm_bufio_cache_size_per_client;
/*
* The current number of clients.
*/
@ -224,11 +226,15 @@ static int dm_bufio_client_count;
static LIST_HEAD(dm_bufio_all_clients);
/*
* This mutex protects dm_bufio_cache_size_latch,
* dm_bufio_cache_size_per_client and dm_bufio_client_count
* This mutex protects dm_bufio_cache_size_latch and dm_bufio_client_count
*/
static DEFINE_MUTEX(dm_bufio_clients_lock);
static struct workqueue_struct *dm_bufio_wq;
static struct delayed_work dm_bufio_cleanup_old_work;
static struct work_struct dm_bufio_replacement_work;
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
static void buffer_record_stack(struct dm_buffer *b)
{
@ -285,15 +291,23 @@ static void __remove(struct dm_bufio_client *c, struct dm_buffer *b)
/*----------------------------------------------------------------*/
static void adjust_total_allocated(unsigned char data_mode, long diff)
static void adjust_total_allocated(struct dm_buffer *b, bool unlink)
{
unsigned char data_mode;
long diff;
static unsigned long * const class_ptr[DATA_MODE_LIMIT] = {
&dm_bufio_allocated_kmem_cache,
&dm_bufio_allocated_get_free_pages,
&dm_bufio_allocated_vmalloc,
};
spin_lock(&param_spinlock);
data_mode = b->data_mode;
diff = (long)b->c->block_size;
if (unlink)
diff = -diff;
spin_lock(&global_spinlock);
*class_ptr[data_mode] += diff;
@ -302,7 +316,19 @@ static void adjust_total_allocated(unsigned char data_mode, long diff)
if (dm_bufio_current_allocated > dm_bufio_peak_allocated)
dm_bufio_peak_allocated = dm_bufio_current_allocated;
spin_unlock(&param_spinlock);
b->accessed = 1;
if (!unlink) {
list_add(&b->global_list, &global_queue);
global_num++;
if (dm_bufio_current_allocated > dm_bufio_cache_size)
queue_work(dm_bufio_wq, &dm_bufio_replacement_work);
} else {
list_del(&b->global_list);
global_num--;
}
spin_unlock(&global_spinlock);
}
/*
@ -323,9 +349,6 @@ static void __cache_size_refresh(void)
dm_bufio_default_cache_size);
dm_bufio_cache_size_latch = dm_bufio_default_cache_size;
}
dm_bufio_cache_size_per_client = dm_bufio_cache_size_latch /
(dm_bufio_client_count ? : 1);
}
/*
@ -431,8 +454,6 @@ static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
return NULL;
}
adjust_total_allocated(b->data_mode, (long)c->block_size);
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
b->stack_len = 0;
#endif
@ -446,8 +467,6 @@ static void free_buffer(struct dm_buffer *b)
{
struct dm_bufio_client *c = b->c;
adjust_total_allocated(b->data_mode, -(long)c->block_size);
free_buffer_data(c, b->data, b->data_mode);
kmem_cache_free(c->slab_buffer, b);
}
@ -465,6 +484,8 @@ static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty)
list_add(&b->lru_list, &c->lru[dirty]);
__insert(b->c, b);
b->last_accessed = jiffies;
adjust_total_allocated(b, false);
}
/*
@ -479,6 +500,8 @@ static void __unlink_buffer(struct dm_buffer *b)
c->n_buffers[b->list_mode]--;
__remove(b->c, b);
list_del(&b->lru_list);
adjust_total_allocated(b, true);
}
/*
@ -488,6 +511,8 @@ static void __relink_lru(struct dm_buffer *b, int dirty)
{
struct dm_bufio_client *c = b->c;
b->accessed = 1;
BUG_ON(!c->n_buffers[b->list_mode]);
c->n_buffers[b->list_mode]--;
@ -906,36 +931,6 @@ static void __write_dirty_buffers_async(struct dm_bufio_client *c, int no_wait,
}
}
/*
* Get writeback threshold and buffer limit for a given client.
*/
static void __get_memory_limit(struct dm_bufio_client *c,
unsigned long *threshold_buffers,
unsigned long *limit_buffers)
{
unsigned long buffers;
if (unlikely(READ_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch)) {
if (mutex_trylock(&dm_bufio_clients_lock)) {
__cache_size_refresh();
mutex_unlock(&dm_bufio_clients_lock);
}
}
buffers = dm_bufio_cache_size_per_client;
if (likely(c->sectors_per_block_bits >= 0))
buffers >>= c->sectors_per_block_bits + SECTOR_SHIFT;
else
buffers /= c->block_size;
if (buffers < c->minimum_buffers)
buffers = c->minimum_buffers;
*limit_buffers = buffers;
*threshold_buffers = mult_frac(buffers,
DM_BUFIO_WRITEBACK_PERCENT, 100);
}
/*
* Check if we're over watermark.
* If we are over threshold_buffers, start freeing buffers.
@ -944,23 +939,7 @@ static void __get_memory_limit(struct dm_bufio_client *c,
static void __check_watermark(struct dm_bufio_client *c,
struct list_head *write_list)
{
unsigned long threshold_buffers, limit_buffers;
__get_memory_limit(c, &threshold_buffers, &limit_buffers);
while (c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY] >
limit_buffers) {
struct dm_buffer *b = __get_unclaimed_buffer(c);
if (!b)
return;
__free_buffer_wake(b);
cond_resched();
}
if (c->n_buffers[LIST_DIRTY] > threshold_buffers)
if (c->n_buffers[LIST_DIRTY] > c->n_buffers[LIST_CLEAN] * DM_BUFIO_WRITEBACK_RATIO)
__write_dirty_buffers_async(c, 1, write_list);
}
@ -1841,6 +1820,74 @@ static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
dm_bufio_unlock(c);
}
static void do_global_cleanup(struct work_struct *w)
{
struct dm_bufio_client *locked_client = NULL;
struct dm_bufio_client *current_client;
struct dm_buffer *b;
unsigned spinlock_hold_count;
unsigned long threshold = dm_bufio_cache_size -
dm_bufio_cache_size / DM_BUFIO_LOW_WATERMARK_RATIO;
unsigned long loops = global_num * 2;
mutex_lock(&dm_bufio_clients_lock);
while (1) {
cond_resched();
spin_lock(&global_spinlock);
if (unlikely(dm_bufio_current_allocated <= threshold))
break;
spinlock_hold_count = 0;
get_next:
if (!loops--)
break;
if (unlikely(list_empty(&global_queue)))
break;
b = list_entry(global_queue.prev, struct dm_buffer, global_list);
if (b->accessed) {
b->accessed = 0;
list_move(&b->global_list, &global_queue);
if (likely(++spinlock_hold_count < 16))
goto get_next;
spin_unlock(&global_spinlock);
continue;
}
current_client = b->c;
if (unlikely(current_client != locked_client)) {
if (locked_client)
dm_bufio_unlock(locked_client);
if (!dm_bufio_trylock(current_client)) {
spin_unlock(&global_spinlock);
dm_bufio_lock(current_client);
locked_client = current_client;
continue;
}
locked_client = current_client;
}
spin_unlock(&global_spinlock);
if (unlikely(!__try_evict_buffer(b, GFP_KERNEL))) {
spin_lock(&global_spinlock);
list_move(&b->global_list, &global_queue);
spin_unlock(&global_spinlock);
}
}
spin_unlock(&global_spinlock);
if (locked_client)
dm_bufio_unlock(locked_client);
mutex_unlock(&dm_bufio_clients_lock);
}
static void cleanup_old_buffers(void)
{
unsigned long max_age_hz = get_max_age_hz();
@ -1856,14 +1903,11 @@ static void cleanup_old_buffers(void)
mutex_unlock(&dm_bufio_clients_lock);
}
static struct workqueue_struct *dm_bufio_wq;
static struct delayed_work dm_bufio_work;
static void work_fn(struct work_struct *w)
{
cleanup_old_buffers();
queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
DM_BUFIO_WORK_TIMER_SECS * HZ);
}
@ -1905,8 +1949,9 @@ static int __init dm_bufio_init(void)
if (!dm_bufio_wq)
return -ENOMEM;
INIT_DELAYED_WORK(&dm_bufio_work, work_fn);
queue_delayed_work(dm_bufio_wq, &dm_bufio_work,
INIT_DELAYED_WORK(&dm_bufio_cleanup_old_work, work_fn);
INIT_WORK(&dm_bufio_replacement_work, do_global_cleanup);
queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
DM_BUFIO_WORK_TIMER_SECS * HZ);
return 0;
@ -1919,7 +1964,8 @@ static void __exit dm_bufio_exit(void)
{
int bug = 0;
cancel_delayed_work_sync(&dm_bufio_work);
cancel_delayed_work_sync(&dm_bufio_cleanup_old_work);
flush_workqueue(dm_bufio_wq);
destroy_workqueue(dm_bufio_wq);
if (dm_bufio_client_count) {

View file

@ -0,0 +1,964 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Arrikto, Inc. All Rights Reserved.
*/
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/rwsem.h>
#include <linux/bitops.h>
#include <linux/bitmap.h>
#include <linux/device-mapper.h>
#include "persistent-data/dm-bitset.h"
#include "persistent-data/dm-space-map.h"
#include "persistent-data/dm-block-manager.h"
#include "persistent-data/dm-transaction-manager.h"
#include "dm-clone-metadata.h"
#define DM_MSG_PREFIX "clone metadata"
#define SUPERBLOCK_LOCATION 0
#define SUPERBLOCK_MAGIC 0x8af27f64
#define SUPERBLOCK_CSUM_XOR 257649492
#define DM_CLONE_MAX_CONCURRENT_LOCKS 5
#define UUID_LEN 16
/* Min and max dm-clone metadata versions supported */
#define DM_CLONE_MIN_METADATA_VERSION 1
#define DM_CLONE_MAX_METADATA_VERSION 1
/*
* On-disk metadata layout
*/
struct superblock_disk {
__le32 csum;
__le32 flags;
__le64 blocknr;
__u8 uuid[UUID_LEN];
__le64 magic;
__le32 version;
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
__le64 region_size;
__le64 target_size;
__le64 bitset_root;
} __packed;
/*
* Region and Dirty bitmaps.
*
* dm-clone logically splits the source and destination devices in regions of
* fixed size. The destination device's regions are gradually hydrated, i.e.,
* we copy (clone) the source's regions to the destination device. Eventually,
* all regions will get hydrated and all I/O will be served from the
* destination device.
*
* We maintain an on-disk bitmap which tracks the state of each of the
* destination device's regions, i.e., whether they are hydrated or not.
*
* To save constantly doing look ups on disk we keep an in core copy of the
* on-disk bitmap, the region_map.
*
* To further reduce metadata I/O overhead we use a second bitmap, the dmap
* (dirty bitmap), which tracks the dirty words, i.e. longs, of the region_map.
*
* When a region finishes hydrating dm-clone calls
* dm_clone_set_region_hydrated(), or for discard requests
* dm_clone_cond_set_range(), which sets the corresponding bits in region_map
* and dmap.
*
* During a metadata commit we scan the dmap for dirty region_map words (longs)
* and update accordingly the on-disk metadata. Thus, we don't have to flush to
* disk the whole region_map. We can just flush the dirty region_map words.
*
* We use a dirty bitmap, which is smaller than the original region_map, to
* reduce the amount of memory accesses during a metadata commit. As dm-bitset
* accesses the on-disk bitmap in 64-bit word granularity, there is no
* significant benefit in tracking the dirty region_map bits with a smaller
* granularity.
*
* We could update directly the on-disk bitmap, when dm-clone calls either
* dm_clone_set_region_hydrated() or dm_clone_cond_set_range(), buts this
* inserts significant metadata I/O overhead in dm-clone's I/O path. Also, as
* these two functions don't block, we can call them in interrupt context,
* e.g., in a hooked overwrite bio's completion routine, and further reduce the
* I/O completion latency.
*
* We maintain two dirty bitmaps. During a metadata commit we atomically swap
* the currently used dmap with the unused one. This allows the metadata update
* functions to run concurrently with an ongoing commit.
*/
struct dirty_map {
unsigned long *dirty_words;
unsigned int changed;
};
struct dm_clone_metadata {
/* The metadata block device */
struct block_device *bdev;
sector_t target_size;
sector_t region_size;
unsigned long nr_regions;
unsigned long nr_words;
/* Spinlock protecting the region and dirty bitmaps. */
spinlock_t bitmap_lock;
struct dirty_map dmap[2];
struct dirty_map *current_dmap;
/*
* In core copy of the on-disk bitmap to save constantly doing look ups
* on disk.
*/
unsigned long *region_map;
/* Protected by bitmap_lock */
unsigned int read_only;
struct dm_block_manager *bm;
struct dm_space_map *sm;
struct dm_transaction_manager *tm;
struct rw_semaphore lock;
struct dm_disk_bitset bitset_info;
dm_block_t bitset_root;
/*
* Reading the space map root can fail, so we read it into this
* buffer before the superblock is locked and updated.
*/
__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
bool hydration_done:1;
bool fail_io:1;
};
/*---------------------------------------------------------------------------*/
/*
* Superblock validation.
*/
static void sb_prepare_for_write(struct dm_block_validator *v,
struct dm_block *b, size_t sb_block_size)
{
struct superblock_disk *sb;
u32 csum;
sb = dm_block_data(b);
sb->blocknr = cpu_to_le64(dm_block_location(b));
csum = dm_bm_checksum(&sb->flags, sb_block_size - sizeof(__le32),
SUPERBLOCK_CSUM_XOR);
sb->csum = cpu_to_le32(csum);
}
static int sb_check(struct dm_block_validator *v, struct dm_block *b,
size_t sb_block_size)
{
struct superblock_disk *sb;
u32 csum, metadata_version;
sb = dm_block_data(b);
if (dm_block_location(b) != le64_to_cpu(sb->blocknr)) {
DMERR("Superblock check failed: blocknr %llu, expected %llu",
le64_to_cpu(sb->blocknr),
(unsigned long long)dm_block_location(b));
return -ENOTBLK;
}
if (le64_to_cpu(sb->magic) != SUPERBLOCK_MAGIC) {
DMERR("Superblock check failed: magic %llu, expected %llu",
le64_to_cpu(sb->magic),
(unsigned long long)SUPERBLOCK_MAGIC);
return -EILSEQ;
}
csum = dm_bm_checksum(&sb->flags, sb_block_size - sizeof(__le32),
SUPERBLOCK_CSUM_XOR);
if (sb->csum != cpu_to_le32(csum)) {
DMERR("Superblock check failed: checksum %u, expected %u",
csum, le32_to_cpu(sb->csum));
return -EILSEQ;
}
/* Check metadata version */
metadata_version = le32_to_cpu(sb->version);
if (metadata_version < DM_CLONE_MIN_METADATA_VERSION ||
metadata_version > DM_CLONE_MAX_METADATA_VERSION) {
DMERR("Clone metadata version %u found, but only versions between %u and %u supported.",
metadata_version, DM_CLONE_MIN_METADATA_VERSION,
DM_CLONE_MAX_METADATA_VERSION);
return -EINVAL;
}
return 0;
}
static struct dm_block_validator sb_validator = {
.name = "superblock",
.prepare_for_write = sb_prepare_for_write,
.check = sb_check
};
/*
* Check if the superblock is formatted or not. We consider the superblock to
* be formatted in case we find non-zero bytes in it.
*/
static int __superblock_all_zeroes(struct dm_block_manager *bm, bool *formatted)
{
int r;
unsigned int i, nr_words;
struct dm_block *sblock;
__le64 *data_le, zero = cpu_to_le64(0);
/*
* We don't use a validator here because the superblock could be all
* zeroes.
*/
r = dm_bm_read_lock(bm, SUPERBLOCK_LOCATION, NULL, &sblock);
if (r) {
DMERR("Failed to read_lock superblock");
return r;
}
data_le = dm_block_data(sblock);
*formatted = false;
/* This assumes that the block size is a multiple of 8 bytes */
BUG_ON(dm_bm_block_size(bm) % sizeof(__le64));
nr_words = dm_bm_block_size(bm) / sizeof(__le64);
for (i = 0; i < nr_words; i++) {
if (data_le[i] != zero) {
*formatted = true;
break;
}
}
dm_bm_unlock(sblock);
return 0;
}
/*---------------------------------------------------------------------------*/
/*
* Low-level metadata handling.
*/
static inline int superblock_read_lock(struct dm_clone_metadata *cmd,
struct dm_block **sblock)
{
return dm_bm_read_lock(cmd->bm, SUPERBLOCK_LOCATION, &sb_validator, sblock);
}
static inline int superblock_write_lock(struct dm_clone_metadata *cmd,
struct dm_block **sblock)
{
return dm_bm_write_lock(cmd->bm, SUPERBLOCK_LOCATION, &sb_validator, sblock);
}
static inline int superblock_write_lock_zero(struct dm_clone_metadata *cmd,
struct dm_block **sblock)
{
return dm_bm_write_lock_zero(cmd->bm, SUPERBLOCK_LOCATION, &sb_validator, sblock);
}
static int __copy_sm_root(struct dm_clone_metadata *cmd)
{
int r;
size_t root_size;
r = dm_sm_root_size(cmd->sm, &root_size);
if (r)
return r;
return dm_sm_copy_root(cmd->sm, &cmd->metadata_space_map_root, root_size);
}
/* Save dm-clone metadata in superblock */
static void __prepare_superblock(struct dm_clone_metadata *cmd,
struct superblock_disk *sb)
{
sb->flags = cpu_to_le32(0UL);
/* FIXME: UUID is currently unused */
memset(sb->uuid, 0, sizeof(sb->uuid));
sb->magic = cpu_to_le64(SUPERBLOCK_MAGIC);
sb->version = cpu_to_le32(DM_CLONE_MAX_METADATA_VERSION);
/* Save the metadata space_map root */
memcpy(&sb->metadata_space_map_root, &cmd->metadata_space_map_root,
sizeof(cmd->metadata_space_map_root));
sb->region_size = cpu_to_le64(cmd->region_size);
sb->target_size = cpu_to_le64(cmd->target_size);
sb->bitset_root = cpu_to_le64(cmd->bitset_root);
}
static int __open_metadata(struct dm_clone_metadata *cmd)
{
int r;
struct dm_block *sblock;
struct superblock_disk *sb;
r = superblock_read_lock(cmd, &sblock);
if (r) {
DMERR("Failed to read_lock superblock");
return r;
}
sb = dm_block_data(sblock);
/* Verify that target_size and region_size haven't changed. */
if (cmd->region_size != le64_to_cpu(sb->region_size) ||
cmd->target_size != le64_to_cpu(sb->target_size)) {
DMERR("Region and/or target size don't match the ones in metadata");
r = -EINVAL;
goto out_with_lock;
}
r = dm_tm_open_with_sm(cmd->bm, SUPERBLOCK_LOCATION,
sb->metadata_space_map_root,
sizeof(sb->metadata_space_map_root),
&cmd->tm, &cmd->sm);
if (r) {
DMERR("dm_tm_open_with_sm failed");
goto out_with_lock;
}
dm_disk_bitset_init(cmd->tm, &cmd->bitset_info);
cmd->bitset_root = le64_to_cpu(sb->bitset_root);
out_with_lock:
dm_bm_unlock(sblock);
return r;
}
static int __format_metadata(struct dm_clone_metadata *cmd)
{
int r;
struct dm_block *sblock;
struct superblock_disk *sb;
r = dm_tm_create_with_sm(cmd->bm, SUPERBLOCK_LOCATION, &cmd->tm, &cmd->sm);
if (r) {
DMERR("Failed to create transaction manager");
return r;
}
dm_disk_bitset_init(cmd->tm, &cmd->bitset_info);
r = dm_bitset_empty(&cmd->bitset_info, &cmd->bitset_root);
if (r) {
DMERR("Failed to create empty on-disk bitset");
goto err_with_tm;
}
r = dm_bitset_resize(&cmd->bitset_info, cmd->bitset_root, 0,
cmd->nr_regions, false, &cmd->bitset_root);
if (r) {
DMERR("Failed to resize on-disk bitset to %lu entries", cmd->nr_regions);
goto err_with_tm;
}
/* Flush to disk all blocks, except the superblock */
r = dm_tm_pre_commit(cmd->tm);
if (r) {
DMERR("dm_tm_pre_commit failed");
goto err_with_tm;
}
r = __copy_sm_root(cmd);
if (r) {
DMERR("__copy_sm_root failed");
goto err_with_tm;
}
r = superblock_write_lock_zero(cmd, &sblock);
if (r) {
DMERR("Failed to write_lock superblock");
goto err_with_tm;
}
sb = dm_block_data(sblock);
__prepare_superblock(cmd, sb);
r = dm_tm_commit(cmd->tm, sblock);
if (r) {
DMERR("Failed to commit superblock");
goto err_with_tm;
}
return 0;
err_with_tm:
dm_sm_destroy(cmd->sm);
dm_tm_destroy(cmd->tm);
return r;
}
static int __open_or_format_metadata(struct dm_clone_metadata *cmd, bool may_format_device)
{
int r;
bool formatted = false;
r = __superblock_all_zeroes(cmd->bm, &formatted);
if (r)
return r;
if (!formatted)
return may_format_device ? __format_metadata(cmd) : -EPERM;
return __open_metadata(cmd);
}
static int __create_persistent_data_structures(struct dm_clone_metadata *cmd,
bool may_format_device)
{
int r;
/* Create block manager */
cmd->bm = dm_block_manager_create(cmd->bdev,
DM_CLONE_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
DM_CLONE_MAX_CONCURRENT_LOCKS);
if (IS_ERR(cmd->bm)) {
DMERR("Failed to create block manager");
return PTR_ERR(cmd->bm);
}
r = __open_or_format_metadata(cmd, may_format_device);
if (r)
dm_block_manager_destroy(cmd->bm);
return r;
}
static void __destroy_persistent_data_structures(struct dm_clone_metadata *cmd)
{
dm_sm_destroy(cmd->sm);
dm_tm_destroy(cmd->tm);
dm_block_manager_destroy(cmd->bm);
}
/*---------------------------------------------------------------------------*/
static size_t bitmap_size(unsigned long nr_bits)
{
return BITS_TO_LONGS(nr_bits) * sizeof(long);
}
static int dirty_map_init(struct dm_clone_metadata *cmd)
{
cmd->dmap[0].changed = 0;
cmd->dmap[0].dirty_words = kvzalloc(bitmap_size(cmd->nr_words), GFP_KERNEL);
if (!cmd->dmap[0].dirty_words) {
DMERR("Failed to allocate dirty bitmap");
return -ENOMEM;
}
cmd->dmap[1].changed = 0;
cmd->dmap[1].dirty_words = kvzalloc(bitmap_size(cmd->nr_words), GFP_KERNEL);
if (!cmd->dmap[1].dirty_words) {
DMERR("Failed to allocate dirty bitmap");
kvfree(cmd->dmap[0].dirty_words);
return -ENOMEM;
}
cmd->current_dmap = &cmd->dmap[0];
return 0;
}
static void dirty_map_exit(struct dm_clone_metadata *cmd)
{
kvfree(cmd->dmap[0].dirty_words);
kvfree(cmd->dmap[1].dirty_words);
}
static int __load_bitset_in_core(struct dm_clone_metadata *cmd)
{
int r;
unsigned long i;
struct dm_bitset_cursor c;
/* Flush bitset cache */
r = dm_bitset_flush(&cmd->bitset_info, cmd->bitset_root, &cmd->bitset_root);
if (r)
return r;
r = dm_bitset_cursor_begin(&cmd->bitset_info, cmd->bitset_root, cmd->nr_regions, &c);
if (r)
return r;
for (i = 0; ; i++) {
if (dm_bitset_cursor_get_value(&c))
__set_bit(i, cmd->region_map);
else
__clear_bit(i, cmd->region_map);
if (i >= (cmd->nr_regions - 1))
break;
r = dm_bitset_cursor_next(&c);
if (r)
break;
}
dm_bitset_cursor_end(&c);
return r;
}
struct dm_clone_metadata *dm_clone_metadata_open(struct block_device *bdev,
sector_t target_size,
sector_t region_size)
{
int r;
struct dm_clone_metadata *cmd;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
DMERR("Failed to allocate memory for dm-clone metadata");
return ERR_PTR(-ENOMEM);
}
cmd->bdev = bdev;
cmd->target_size = target_size;
cmd->region_size = region_size;
cmd->nr_regions = dm_sector_div_up(cmd->target_size, cmd->region_size);
cmd->nr_words = BITS_TO_LONGS(cmd->nr_regions);
init_rwsem(&cmd->lock);
spin_lock_init(&cmd->bitmap_lock);
cmd->read_only = 0;
cmd->fail_io = false;
cmd->hydration_done = false;
cmd->region_map = kvmalloc(bitmap_size(cmd->nr_regions), GFP_KERNEL);
if (!cmd->region_map) {
DMERR("Failed to allocate memory for region bitmap");
r = -ENOMEM;
goto out_with_md;
}
r = __create_persistent_data_structures(cmd, true);
if (r)
goto out_with_region_map;
r = __load_bitset_in_core(cmd);
if (r) {
DMERR("Failed to load on-disk region map");
goto out_with_pds;
}
r = dirty_map_init(cmd);
if (r)
goto out_with_pds;
if (bitmap_full(cmd->region_map, cmd->nr_regions))
cmd->hydration_done = true;
return cmd;
out_with_pds:
__destroy_persistent_data_structures(cmd);
out_with_region_map:
kvfree(cmd->region_map);
out_with_md:
kfree(cmd);
return ERR_PTR(r);
}
void dm_clone_metadata_close(struct dm_clone_metadata *cmd)
{
if (!cmd->fail_io)
__destroy_persistent_data_structures(cmd);
dirty_map_exit(cmd);
kvfree(cmd->region_map);
kfree(cmd);
}
bool dm_clone_is_hydration_done(struct dm_clone_metadata *cmd)
{
return cmd->hydration_done;
}
bool dm_clone_is_region_hydrated(struct dm_clone_metadata *cmd, unsigned long region_nr)
{
return dm_clone_is_hydration_done(cmd) || test_bit(region_nr, cmd->region_map);
}
bool dm_clone_is_range_hydrated(struct dm_clone_metadata *cmd,
unsigned long start, unsigned long nr_regions)
{
unsigned long bit;
if (dm_clone_is_hydration_done(cmd))
return true;
bit = find_next_zero_bit(cmd->region_map, cmd->nr_regions, start);
return (bit >= (start + nr_regions));
}
unsigned long dm_clone_nr_of_hydrated_regions(struct dm_clone_metadata *cmd)
{
return bitmap_weight(cmd->region_map, cmd->nr_regions);
}
unsigned long dm_clone_find_next_unhydrated_region(struct dm_clone_metadata *cmd,
unsigned long start)
{
return find_next_zero_bit(cmd->region_map, cmd->nr_regions, start);
}
static int __update_metadata_word(struct dm_clone_metadata *cmd, unsigned long word)
{
int r;
unsigned long index = word * BITS_PER_LONG;
unsigned long max_index = min(cmd->nr_regions, (word + 1) * BITS_PER_LONG);
while (index < max_index) {
if (test_bit(index, cmd->region_map)) {
r = dm_bitset_set_bit(&cmd->bitset_info, cmd->bitset_root,
index, &cmd->bitset_root);
if (r) {
DMERR("dm_bitset_set_bit failed");
return r;
}
}
index++;
}
return 0;
}
static int __metadata_commit(struct dm_clone_metadata *cmd)
{
int r;
struct dm_block *sblock;
struct superblock_disk *sb;
/* Flush bitset cache */
r = dm_bitset_flush(&cmd->bitset_info, cmd->bitset_root, &cmd->bitset_root);
if (r) {
DMERR("dm_bitset_flush failed");
return r;
}
/* Flush to disk all blocks, except the superblock */
r = dm_tm_pre_commit(cmd->tm);
if (r) {
DMERR("dm_tm_pre_commit failed");
return r;
}
/* Save the space map root in cmd->metadata_space_map_root */
r = __copy_sm_root(cmd);
if (r) {
DMERR("__copy_sm_root failed");
return r;
}
/* Lock the superblock */
r = superblock_write_lock_zero(cmd, &sblock);
if (r) {
DMERR("Failed to write_lock superblock");
return r;
}
/* Save the metadata in superblock */
sb = dm_block_data(sblock);
__prepare_superblock(cmd, sb);
/* Unlock superblock and commit it to disk */
r = dm_tm_commit(cmd->tm, sblock);
if (r) {
DMERR("Failed to commit superblock");
return r;
}
/*
* FIXME: Find a more efficient way to check if the hydration is done.
*/
if (bitmap_full(cmd->region_map, cmd->nr_regions))
cmd->hydration_done = true;
return 0;
}
static int __flush_dmap(struct dm_clone_metadata *cmd, struct dirty_map *dmap)
{
int r;
unsigned long word, flags;
word = 0;
do {
word = find_next_bit(dmap->dirty_words, cmd->nr_words, word);
if (word == cmd->nr_words)
break;
r = __update_metadata_word(cmd, word);
if (r)
return r;
__clear_bit(word, dmap->dirty_words);
word++;
} while (word < cmd->nr_words);
r = __metadata_commit(cmd);
if (r)
return r;
/* Update the changed flag */
spin_lock_irqsave(&cmd->bitmap_lock, flags);
dmap->changed = 0;
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
return 0;
}
int dm_clone_metadata_commit(struct dm_clone_metadata *cmd)
{
int r = -EPERM;
unsigned long flags;
struct dirty_map *dmap, *next_dmap;
down_write(&cmd->lock);
if (cmd->fail_io || dm_bm_is_read_only(cmd->bm))
goto out;
/* Get current dirty bitmap */
dmap = cmd->current_dmap;
/* Get next dirty bitmap */
next_dmap = (dmap == &cmd->dmap[0]) ? &cmd->dmap[1] : &cmd->dmap[0];
/*
* The last commit failed, so we don't have a clean dirty-bitmap to
* use.
*/
if (WARN_ON(next_dmap->changed)) {
r = -EINVAL;
goto out;
}
/* Swap dirty bitmaps */
spin_lock_irqsave(&cmd->bitmap_lock, flags);
cmd->current_dmap = next_dmap;
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
/*
* No one is accessing the old dirty bitmap anymore, so we can flush
* it.
*/
r = __flush_dmap(cmd, dmap);
out:
up_write(&cmd->lock);
return r;
}
int dm_clone_set_region_hydrated(struct dm_clone_metadata *cmd, unsigned long region_nr)
{
int r = 0;
struct dirty_map *dmap;
unsigned long word, flags;
word = region_nr / BITS_PER_LONG;
spin_lock_irqsave(&cmd->bitmap_lock, flags);
if (cmd->read_only) {
r = -EPERM;
goto out;
}
dmap = cmd->current_dmap;
__set_bit(word, dmap->dirty_words);
__set_bit(region_nr, cmd->region_map);
dmap->changed = 1;
out:
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
return r;
}
int dm_clone_cond_set_range(struct dm_clone_metadata *cmd, unsigned long start,
unsigned long nr_regions)
{
int r = 0;
struct dirty_map *dmap;
unsigned long word, region_nr, flags;
spin_lock_irqsave(&cmd->bitmap_lock, flags);
if (cmd->read_only) {
r = -EPERM;
goto out;
}
dmap = cmd->current_dmap;
for (region_nr = start; region_nr < (start + nr_regions); region_nr++) {
if (!test_bit(region_nr, cmd->region_map)) {
word = region_nr / BITS_PER_LONG;
__set_bit(word, dmap->dirty_words);
__set_bit(region_nr, cmd->region_map);
dmap->changed = 1;
}
}
out:
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
return r;
}
/*
* WARNING: This must not be called concurrently with either
* dm_clone_set_region_hydrated() or dm_clone_cond_set_range(), as it changes
* cmd->region_map without taking the cmd->bitmap_lock spinlock. The only
* exception is after setting the metadata to read-only mode, using
* dm_clone_metadata_set_read_only().
*
* We don't take the spinlock because __load_bitset_in_core() does I/O, so it
* may block.
*/
int dm_clone_reload_in_core_bitset(struct dm_clone_metadata *cmd)
{
int r = -EINVAL;
down_write(&cmd->lock);
if (cmd->fail_io)
goto out;
r = __load_bitset_in_core(cmd);
out:
up_write(&cmd->lock);
return r;
}
bool dm_clone_changed_this_transaction(struct dm_clone_metadata *cmd)
{
bool r;
unsigned long flags;
spin_lock_irqsave(&cmd->bitmap_lock, flags);
r = cmd->dmap[0].changed || cmd->dmap[1].changed;
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
return r;
}
int dm_clone_metadata_abort(struct dm_clone_metadata *cmd)
{
int r = -EPERM;
down_write(&cmd->lock);
if (cmd->fail_io || dm_bm_is_read_only(cmd->bm))
goto out;
__destroy_persistent_data_structures(cmd);
r = __create_persistent_data_structures(cmd, false);
if (r) {
/* If something went wrong we can neither write nor read the metadata */
cmd->fail_io = true;
}
out:
up_write(&cmd->lock);
return r;
}
void dm_clone_metadata_set_read_only(struct dm_clone_metadata *cmd)
{
unsigned long flags;
down_write(&cmd->lock);
spin_lock_irqsave(&cmd->bitmap_lock, flags);
cmd->read_only = 1;
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
if (!cmd->fail_io)
dm_bm_set_read_only(cmd->bm);
up_write(&cmd->lock);
}
void dm_clone_metadata_set_read_write(struct dm_clone_metadata *cmd)
{
unsigned long flags;
down_write(&cmd->lock);
spin_lock_irqsave(&cmd->bitmap_lock, flags);
cmd->read_only = 0;
spin_unlock_irqrestore(&cmd->bitmap_lock, flags);
if (!cmd->fail_io)
dm_bm_set_read_write(cmd->bm);
up_write(&cmd->lock);
}
int dm_clone_get_free_metadata_block_count(struct dm_clone_metadata *cmd,
dm_block_t *result)
{
int r = -EINVAL;
down_read(&cmd->lock);
if (!cmd->fail_io)
r = dm_sm_get_nr_free(cmd->sm, result);
up_read(&cmd->lock);
return r;
}
int dm_clone_get_metadata_dev_size(struct dm_clone_metadata *cmd,
dm_block_t *result)
{
int r = -EINVAL;
down_read(&cmd->lock);
if (!cmd->fail_io)
r = dm_sm_get_nr_blocks(cmd->sm, result);
up_read(&cmd->lock);
return r;
}

View file

@ -0,0 +1,158 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2019 Arrikto, Inc. All Rights Reserved.
*/
#ifndef DM_CLONE_METADATA_H
#define DM_CLONE_METADATA_H
#include "persistent-data/dm-block-manager.h"
#include "persistent-data/dm-space-map-metadata.h"
#define DM_CLONE_METADATA_BLOCK_SIZE DM_SM_METADATA_BLOCK_SIZE
/*
* The metadata device is currently limited in size.
*/
#define DM_CLONE_METADATA_MAX_SECTORS DM_SM_METADATA_MAX_SECTORS
/*
* A metadata device larger than 16GB triggers a warning.
*/
#define DM_CLONE_METADATA_MAX_SECTORS_WARNING (16 * (1024 * 1024 * 1024 >> SECTOR_SHIFT))
#define SPACE_MAP_ROOT_SIZE 128
/* dm-clone metadata */
struct dm_clone_metadata;
/*
* Set region status to hydrated.
*
* @cmd: The dm-clone metadata
* @region_nr: The region number
*
* This function doesn't block, so it's safe to call it from interrupt context.
*/
int dm_clone_set_region_hydrated(struct dm_clone_metadata *cmd, unsigned long region_nr);
/*
* Set status of all regions in the provided range to hydrated, if not already
* hydrated.
*
* @cmd: The dm-clone metadata
* @start: Starting region number
* @nr_regions: Number of regions in the range
*
* This function doesn't block, so it's safe to call it from interrupt context.
*/
int dm_clone_cond_set_range(struct dm_clone_metadata *cmd, unsigned long start,
unsigned long nr_regions);
/*
* Read existing or create fresh metadata.
*
* @bdev: The device storing the metadata
* @target_size: The target size
* @region_size: The region size
*
* @returns: The dm-clone metadata
*
* This function reads the superblock of @bdev and checks if it's all zeroes.
* If it is, it formats @bdev and creates fresh metadata. If it isn't, it
* validates the metadata stored in @bdev.
*/
struct dm_clone_metadata *dm_clone_metadata_open(struct block_device *bdev,
sector_t target_size,
sector_t region_size);
/*
* Free the resources related to metadata management.
*/
void dm_clone_metadata_close(struct dm_clone_metadata *cmd);
/*
* Commit dm-clone metadata to disk.
*/
int dm_clone_metadata_commit(struct dm_clone_metadata *cmd);
/*
* Reload the in core copy of the on-disk bitmap.
*
* This should be used after aborting a metadata transaction and setting the
* metadata to read-only, to invalidate the in-core cache and make it match the
* on-disk metadata.
*
* WARNING: It must not be called concurrently with either
* dm_clone_set_region_hydrated() or dm_clone_cond_set_range(), as it updates
* the region bitmap without taking the relevant spinlock. We don't take the
* spinlock because dm_clone_reload_in_core_bitset() does I/O, so it may block.
*
* But, it's safe to use it after calling dm_clone_metadata_set_read_only(),
* because the latter sets the metadata to read-only mode. Both
* dm_clone_set_region_hydrated() and dm_clone_cond_set_range() refuse to touch
* the region bitmap, after calling dm_clone_metadata_set_read_only().
*/
int dm_clone_reload_in_core_bitset(struct dm_clone_metadata *cmd);
/*
* Check whether dm-clone's metadata changed this transaction.
*/
bool dm_clone_changed_this_transaction(struct dm_clone_metadata *cmd);
/*
* Abort current metadata transaction and rollback metadata to the last
* committed transaction.
*/
int dm_clone_metadata_abort(struct dm_clone_metadata *cmd);
/*
* Switches metadata to a read only mode. Once read-only mode has been entered
* the following functions will return -EPERM:
*
* dm_clone_metadata_commit()
* dm_clone_set_region_hydrated()
* dm_clone_cond_set_range()
* dm_clone_metadata_abort()
*/
void dm_clone_metadata_set_read_only(struct dm_clone_metadata *cmd);
void dm_clone_metadata_set_read_write(struct dm_clone_metadata *cmd);
/*
* Returns true if the hydration of the destination device is finished.
*/
bool dm_clone_is_hydration_done(struct dm_clone_metadata *cmd);
/*
* Returns true if region @region_nr is hydrated.
*/
bool dm_clone_is_region_hydrated(struct dm_clone_metadata *cmd, unsigned long region_nr);
/*
* Returns true if all the regions in the range are hydrated.
*/
bool dm_clone_is_range_hydrated(struct dm_clone_metadata *cmd,
unsigned long start, unsigned long nr_regions);
/*
* Returns the number of hydrated regions.
*/
unsigned long dm_clone_nr_of_hydrated_regions(struct dm_clone_metadata *cmd);
/*
* Returns the first unhydrated region with region_nr >= @start
*/
unsigned long dm_clone_find_next_unhydrated_region(struct dm_clone_metadata *cmd,
unsigned long start);
/*
* Get the number of free metadata blocks.
*/
int dm_clone_get_free_metadata_block_count(struct dm_clone_metadata *cmd, dm_block_t *result);
/*
* Get the total number of metadata blocks.
*/
int dm_clone_get_metadata_dev_size(struct dm_clone_metadata *cmd, dm_block_t *result);
#endif /* DM_CLONE_METADATA_H */

2191
drivers/md/dm-clone-target.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -98,11 +98,6 @@ struct crypt_iv_operations {
struct dm_crypt_request *dmreq);
};
struct iv_essiv_private {
struct crypto_shash *hash_tfm;
u8 *salt;
};
struct iv_benbi_private {
int shift;
};
@ -120,10 +115,6 @@ struct iv_tcw_private {
u8 *whitening;
};
struct iv_eboiv_private {
struct crypto_cipher *tfm;
};
/*
* Crypt: maps a linear range of a block device
* and encrypts / decrypts at the same time.
@ -152,26 +143,21 @@ struct crypt_config {
struct task_struct *write_thread;
struct rb_root write_tree;
char *cipher;
char *cipher_string;
char *cipher_auth;
char *key_string;
const struct crypt_iv_operations *iv_gen_ops;
union {
struct iv_essiv_private essiv;
struct iv_benbi_private benbi;
struct iv_lmk_private lmk;
struct iv_tcw_private tcw;
struct iv_eboiv_private eboiv;
} iv_gen_private;
u64 iv_offset;
unsigned int iv_size;
unsigned short int sector_size;
unsigned char sector_shift;
/* ESSIV: struct crypto_cipher *essiv_tfm */
void *iv_private;
union {
struct crypto_skcipher **tfms;
struct crypto_aead **tfms_aead;
@ -329,157 +315,15 @@ static int crypt_iv_plain64be_gen(struct crypt_config *cc, u8 *iv,
return 0;
}
/* Initialise ESSIV - compute salt but no local memory allocations */
static int crypt_iv_essiv_init(struct crypt_config *cc)
{
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
SHASH_DESC_ON_STACK(desc, essiv->hash_tfm);
struct crypto_cipher *essiv_tfm;
int err;
desc->tfm = essiv->hash_tfm;
err = crypto_shash_digest(desc, cc->key, cc->key_size, essiv->salt);
shash_desc_zero(desc);
if (err)
return err;
essiv_tfm = cc->iv_private;
err = crypto_cipher_setkey(essiv_tfm, essiv->salt,
crypto_shash_digestsize(essiv->hash_tfm));
if (err)
return err;
return 0;
}
/* Wipe salt and reset key derived from volume key */
static int crypt_iv_essiv_wipe(struct crypt_config *cc)
{
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
unsigned salt_size = crypto_shash_digestsize(essiv->hash_tfm);
struct crypto_cipher *essiv_tfm;
int r, err = 0;
memset(essiv->salt, 0, salt_size);
essiv_tfm = cc->iv_private;
r = crypto_cipher_setkey(essiv_tfm, essiv->salt, salt_size);
if (r)
err = r;
return err;
}
/* Allocate the cipher for ESSIV */
static struct crypto_cipher *alloc_essiv_cipher(struct crypt_config *cc,
struct dm_target *ti,
const u8 *salt,
unsigned int saltsize)
{
struct crypto_cipher *essiv_tfm;
int err;
/* Setup the essiv_tfm with the given salt */
essiv_tfm = crypto_alloc_cipher(cc->cipher, 0, 0);
if (IS_ERR(essiv_tfm)) {
ti->error = "Error allocating crypto tfm for ESSIV";
return essiv_tfm;
}
if (crypto_cipher_blocksize(essiv_tfm) != cc->iv_size) {
ti->error = "Block size of ESSIV cipher does "
"not match IV size of block cipher";
crypto_free_cipher(essiv_tfm);
return ERR_PTR(-EINVAL);
}
err = crypto_cipher_setkey(essiv_tfm, salt, saltsize);
if (err) {
ti->error = "Failed to set key for ESSIV cipher";
crypto_free_cipher(essiv_tfm);
return ERR_PTR(err);
}
return essiv_tfm;
}
static void crypt_iv_essiv_dtr(struct crypt_config *cc)
{
struct crypto_cipher *essiv_tfm;
struct iv_essiv_private *essiv = &cc->iv_gen_private.essiv;
crypto_free_shash(essiv->hash_tfm);
essiv->hash_tfm = NULL;
kzfree(essiv->salt);
essiv->salt = NULL;
essiv_tfm = cc->iv_private;
if (essiv_tfm)
crypto_free_cipher(essiv_tfm);
cc->iv_private = NULL;
}
static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
const char *opts)
{
struct crypto_cipher *essiv_tfm = NULL;
struct crypto_shash *hash_tfm = NULL;
u8 *salt = NULL;
int err;
if (!opts) {
ti->error = "Digest algorithm missing for ESSIV mode";
return -EINVAL;
}
/* Allocate hash algorithm */
hash_tfm = crypto_alloc_shash(opts, 0, 0);
if (IS_ERR(hash_tfm)) {
ti->error = "Error initializing ESSIV hash";
err = PTR_ERR(hash_tfm);
goto bad;
}
salt = kzalloc(crypto_shash_digestsize(hash_tfm), GFP_KERNEL);
if (!salt) {
ti->error = "Error kmallocing salt storage in ESSIV";
err = -ENOMEM;
goto bad;
}
cc->iv_gen_private.essiv.salt = salt;
cc->iv_gen_private.essiv.hash_tfm = hash_tfm;
essiv_tfm = alloc_essiv_cipher(cc, ti, salt,
crypto_shash_digestsize(hash_tfm));
if (IS_ERR(essiv_tfm)) {
crypt_iv_essiv_dtr(cc);
return PTR_ERR(essiv_tfm);
}
cc->iv_private = essiv_tfm;
return 0;
bad:
if (hash_tfm && !IS_ERR(hash_tfm))
crypto_free_shash(hash_tfm);
kfree(salt);
return err;
}
static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
struct dm_crypt_request *dmreq)
{
struct crypto_cipher *essiv_tfm = cc->iv_private;
/*
* ESSIV encryption of the IV is now handled by the crypto API,
* so just pass the plain sector number here.
*/
memset(iv, 0, cc->iv_size);
*(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
return 0;
}
@ -847,65 +691,47 @@ static int crypt_iv_random_gen(struct crypt_config *cc, u8 *iv,
return 0;
}
static void crypt_iv_eboiv_dtr(struct crypt_config *cc)
{
struct iv_eboiv_private *eboiv = &cc->iv_gen_private.eboiv;
crypto_free_cipher(eboiv->tfm);
eboiv->tfm = NULL;
}
static int crypt_iv_eboiv_ctr(struct crypt_config *cc, struct dm_target *ti,
const char *opts)
{
struct iv_eboiv_private *eboiv = &cc->iv_gen_private.eboiv;
struct crypto_cipher *tfm;
tfm = crypto_alloc_cipher(cc->cipher, 0, 0);
if (IS_ERR(tfm)) {
ti->error = "Error allocating crypto tfm for EBOIV";
return PTR_ERR(tfm);
}
if (crypto_cipher_blocksize(tfm) != cc->iv_size) {
ti->error = "Block size of EBOIV cipher does "
"not match IV size of block cipher";
crypto_free_cipher(tfm);
if (test_bit(CRYPT_MODE_INTEGRITY_AEAD, &cc->cipher_flags)) {
ti->error = "AEAD transforms not supported for EBOIV";
return -EINVAL;
}
eboiv->tfm = tfm;
return 0;
}
static int crypt_iv_eboiv_init(struct crypt_config *cc)
{
struct iv_eboiv_private *eboiv = &cc->iv_gen_private.eboiv;
int err;
err = crypto_cipher_setkey(eboiv->tfm, cc->key, cc->key_size);
if (err)
return err;
if (crypto_skcipher_blocksize(any_tfm(cc)) != cc->iv_size) {
ti->error = "Block size of EBOIV cipher does "
"not match IV size of block cipher";
return -EINVAL;
}
return 0;
}
static int crypt_iv_eboiv_wipe(struct crypt_config *cc)
{
/* Called after cc->key is set to random key in crypt_wipe() */
return crypt_iv_eboiv_init(cc);
}
static int crypt_iv_eboiv_gen(struct crypt_config *cc, u8 *iv,
struct dm_crypt_request *dmreq)
{
struct iv_eboiv_private *eboiv = &cc->iv_gen_private.eboiv;
u8 buf[MAX_CIPHER_BLOCKSIZE] __aligned(__alignof__(__le64));
struct skcipher_request *req;
struct scatterlist src, dst;
struct crypto_wait wait;
int err;
memset(iv, 0, cc->iv_size);
*(__le64 *)iv = cpu_to_le64(dmreq->iv_sector * cc->sector_size);
crypto_cipher_encrypt_one(eboiv->tfm, iv, iv);
req = skcipher_request_alloc(any_tfm(cc), GFP_KERNEL | GFP_NOFS);
if (!req)
return -ENOMEM;
return 0;
memset(buf, 0, cc->iv_size);
*(__le64 *)buf = cpu_to_le64(dmreq->iv_sector * cc->sector_size);
sg_init_one(&src, page_address(ZERO_PAGE(0)), cc->iv_size);
sg_init_one(&dst, iv, cc->iv_size);
skcipher_request_set_crypt(req, &src, &dst, cc->iv_size, buf);
skcipher_request_set_callback(req, 0, crypto_req_done, &wait);
err = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
skcipher_request_free(req);
return err;
}
static const struct crypt_iv_operations crypt_iv_plain_ops = {
@ -921,10 +747,6 @@ static const struct crypt_iv_operations crypt_iv_plain64be_ops = {
};
static const struct crypt_iv_operations crypt_iv_essiv_ops = {
.ctr = crypt_iv_essiv_ctr,
.dtr = crypt_iv_essiv_dtr,
.init = crypt_iv_essiv_init,
.wipe = crypt_iv_essiv_wipe,
.generator = crypt_iv_essiv_gen
};
@ -962,9 +784,6 @@ static struct crypt_iv_operations crypt_iv_random_ops = {
static struct crypt_iv_operations crypt_iv_eboiv_ops = {
.ctr = crypt_iv_eboiv_ctr,
.dtr = crypt_iv_eboiv_dtr,
.init = crypt_iv_eboiv_init,
.wipe = crypt_iv_eboiv_wipe,
.generator = crypt_iv_eboiv_gen
};
@ -2320,7 +2139,6 @@ static void crypt_dtr(struct dm_target *ti)
if (cc->dev)
dm_put_device(ti, cc->dev);
kzfree(cc->cipher);
kzfree(cc->cipher_string);
kzfree(cc->key_string);
kzfree(cc->cipher_auth);
@ -2401,52 +2219,6 @@ static int crypt_ctr_ivmode(struct dm_target *ti, const char *ivmode)
return 0;
}
/*
* Workaround to parse cipher algorithm from crypto API spec.
* The cc->cipher is currently used only in ESSIV.
* This should be probably done by crypto-api calls (once available...)
*/
static int crypt_ctr_blkdev_cipher(struct crypt_config *cc)
{
const char *alg_name = NULL;
char *start, *end;
if (crypt_integrity_aead(cc)) {
alg_name = crypto_tfm_alg_name(crypto_aead_tfm(any_tfm_aead(cc)));
if (!alg_name)
return -EINVAL;
if (crypt_integrity_hmac(cc)) {
alg_name = strchr(alg_name, ',');
if (!alg_name)
return -EINVAL;
}
alg_name++;
} else {
alg_name = crypto_tfm_alg_name(crypto_skcipher_tfm(any_tfm(cc)));
if (!alg_name)
return -EINVAL;
}
start = strchr(alg_name, '(');
end = strchr(alg_name, ')');
if (!start && !end) {
cc->cipher = kstrdup(alg_name, GFP_KERNEL);
return cc->cipher ? 0 : -ENOMEM;
}
if (!start || !end || ++start >= end)
return -EINVAL;
cc->cipher = kzalloc(end - start + 1, GFP_KERNEL);
if (!cc->cipher)
return -ENOMEM;
strncpy(cc->cipher, start, end - start);
return 0;
}
/*
* Workaround to parse HMAC algorithm from AEAD crypto API spec.
* The HMAC is needed to calculate tag size (HMAC digest size).
@ -2490,7 +2262,7 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
char **ivmode, char **ivopts)
{
struct crypt_config *cc = ti->private;
char *tmp, *cipher_api;
char *tmp, *cipher_api, buf[CRYPTO_MAX_ALG_NAME];
int ret = -EINVAL;
cc->tfms_count = 1;
@ -2516,9 +2288,32 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
/* The rest is crypto API spec */
cipher_api = tmp;
/* Alloc AEAD, can be used only in new format. */
if (crypt_integrity_aead(cc)) {
ret = crypt_ctr_auth_cipher(cc, cipher_api);
if (ret < 0) {
ti->error = "Invalid AEAD cipher spec";
return -ENOMEM;
}
}
if (*ivmode && !strcmp(*ivmode, "lmk"))
cc->tfms_count = 64;
if (*ivmode && !strcmp(*ivmode, "essiv")) {
if (!*ivopts) {
ti->error = "Digest algorithm missing for ESSIV mode";
return -EINVAL;
}
ret = snprintf(buf, CRYPTO_MAX_ALG_NAME, "essiv(%s,%s)",
cipher_api, *ivopts);
if (ret < 0 || ret >= CRYPTO_MAX_ALG_NAME) {
ti->error = "Cannot allocate cipher string";
return -ENOMEM;
}
cipher_api = buf;
}
cc->key_parts = cc->tfms_count;
/* Allocate cipher */
@ -2528,23 +2323,11 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
return ret;
}
/* Alloc AEAD, can be used only in new format. */
if (crypt_integrity_aead(cc)) {
ret = crypt_ctr_auth_cipher(cc, cipher_api);
if (ret < 0) {
ti->error = "Invalid AEAD cipher spec";
return -ENOMEM;
}
if (crypt_integrity_aead(cc))
cc->iv_size = crypto_aead_ivsize(any_tfm_aead(cc));
} else
else
cc->iv_size = crypto_skcipher_ivsize(any_tfm(cc));
ret = crypt_ctr_blkdev_cipher(cc);
if (ret < 0) {
ti->error = "Cannot allocate cipher string";
return -ENOMEM;
}
return 0;
}
@ -2579,10 +2362,6 @@ static int crypt_ctr_cipher_old(struct dm_target *ti, char *cipher_in, char *key
}
cc->key_parts = cc->tfms_count;
cc->cipher = kstrdup(cipher, GFP_KERNEL);
if (!cc->cipher)
goto bad_mem;
chainmode = strsep(&tmp, "-");
*ivmode = strsep(&tmp, ":");
*ivopts = tmp;
@ -2605,9 +2384,19 @@ static int crypt_ctr_cipher_old(struct dm_target *ti, char *cipher_in, char *key
if (!cipher_api)
goto bad_mem;
ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
"%s(%s)", chainmode, cipher);
if (ret < 0) {
if (*ivmode && !strcmp(*ivmode, "essiv")) {
if (!*ivopts) {
ti->error = "Digest algorithm missing for ESSIV mode";
kfree(cipher_api);
return -EINVAL;
}
ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
"essiv(%s(%s),%s)", chainmode, cipher, *ivopts);
} else {
ret = snprintf(cipher_api, CRYPTO_MAX_ALG_NAME,
"%s(%s)", chainmode, cipher);
}
if (ret < 0 || ret >= CRYPTO_MAX_ALG_NAME) {
kfree(cipher_api);
goto bad_mem;
}

View file

@ -601,17 +601,27 @@ static void list_version_get_info(struct target_type *tt, void *param)
info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1);
}
static int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size)
static int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name)
{
size_t len, needed = 0;
struct dm_target_versions *vers;
struct vers_iter iter_info;
struct target_type *tt = NULL;
if (name) {
tt = dm_get_target_type(name);
if (!tt)
return -EINVAL;
}
/*
* Loop through all the devices working out how much
* space we need.
*/
dm_target_iterate(list_version_get_needed, &needed);
if (!tt)
dm_target_iterate(list_version_get_needed, &needed);
else
list_version_get_needed(tt, &needed);
/*
* Grab our output buffer.
@ -632,13 +642,28 @@ static int list_versions(struct file *filp, struct dm_ioctl *param, size_t param
/*
* Now loop through filling out the names & versions.
*/
dm_target_iterate(list_version_get_info, &iter_info);
if (!tt)
dm_target_iterate(list_version_get_info, &iter_info);
else
list_version_get_info(tt, &iter_info);
param->flags |= iter_info.flags;
out:
if (tt)
dm_put_target_type(tt);
return 0;
}
static int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size)
{
return __list_versions(param, param_size, NULL);
}
static int get_target_version(struct file *filp, struct dm_ioctl *param, size_t param_size)
{
return __list_versions(param, param_size, param->name);
}
static int check_name(const char *name)
{
if (strchr(name, '/')) {
@ -1592,7 +1617,7 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para
}
ti = dm_table_find_target(table, tmsg->sector);
if (!dm_target_is_valid(ti)) {
if (!ti) {
DMWARN("Target message sector outside device.");
r = -EINVAL;
} else if (ti->type->message)
@ -1664,6 +1689,7 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
{DM_TARGET_MSG_CMD, 0, target_message},
{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
{DM_DEV_ARM_POLL, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
{DM_GET_TARGET_VERSION, 0, get_target_version},
};
if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))

View file

@ -3738,18 +3738,18 @@ static int raid_iterate_devices(struct dm_target *ti,
static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct raid_set *rs = ti->private;
unsigned int chunk_size = to_bytes(rs->md.chunk_sectors);
unsigned int chunk_size_bytes = to_bytes(rs->md.chunk_sectors);
blk_limits_io_min(limits, chunk_size);
blk_limits_io_opt(limits, chunk_size * mddev_data_stripes(rs));
blk_limits_io_min(limits, chunk_size_bytes);
blk_limits_io_opt(limits, chunk_size_bytes * mddev_data_stripes(rs));
/*
* RAID1 and RAID10 personalities require bio splitting,
* RAID0/4/5/6 don't and process large discard bios properly.
*/
if (rs_is_raid1(rs) || rs_is_raid10(rs)) {
limits->discard_granularity = chunk_size;
limits->max_discard_sectors = chunk_size;
limits->discard_granularity = chunk_size_bytes;
limits->max_discard_sectors = rs->md.chunk_sectors;
}
}

View file

@ -878,12 +878,9 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
struct dm_target *ti,
struct dm_dirty_log *dl)
{
size_t len;
struct mirror_set *ms = NULL;
struct mirror_set *ms =
kzalloc(struct_size(ms, mirror, nr_mirrors), GFP_KERNEL);
len = sizeof(*ms) + (sizeof(ms->mirror[0]) * nr_mirrors);
ms = kzalloc(len, GFP_KERNEL);
if (!ms) {
ti->error = "Cannot allocate mirror context";
return NULL;

View file

@ -262,7 +262,7 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
if (n_entries != (size_t)n_entries || !(size_t)(n_entries + 1))
return -EOVERFLOW;
shared_alloc_size = sizeof(struct dm_stat) + (size_t)n_entries * sizeof(struct dm_stat_shared);
shared_alloc_size = struct_size(s, stat_shared, n_entries);
if ((shared_alloc_size - sizeof(struct dm_stat)) / sizeof(struct dm_stat_shared) != n_entries)
return -EOVERFLOW;

View file

@ -163,10 +163,8 @@ static int alloc_targets(struct dm_table *t, unsigned int num)
/*
* Allocate both the target array and offset array at once.
* Append an empty entry to catch sectors beyond the end of
* the device.
*/
n_highs = (sector_t *) dm_vcalloc(num + 1, sizeof(struct dm_target) +
n_highs = (sector_t *) dm_vcalloc(num, sizeof(struct dm_target) +
sizeof(sector_t));
if (!n_highs)
return -ENOMEM;
@ -1359,7 +1357,7 @@ struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
/*
* Search the btree for the correct target.
*
* Caller should check returned pointer with dm_target_is_valid()
* Caller should check returned pointer for NULL
* to trap I/O beyond end of device.
*/
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
@ -1368,7 +1366,7 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
sector_t *node;
if (unlikely(sector >= dm_table_get_size(t)))
return &t->targets[t->num_targets];
return NULL;
for (l = 0; l < t->depth; l++) {
n = get_child(n, k);

View file

@ -15,7 +15,7 @@
#include "dm-verity.h"
#include "dm-verity-fec.h"
#include "dm-verity-verify-sig.h"
#include <linux/module.h>
#include <linux/reboot.h>
@ -33,7 +33,8 @@
#define DM_VERITY_OPT_IGN_ZEROES "ignore_zero_blocks"
#define DM_VERITY_OPT_AT_MOST_ONCE "check_at_most_once"
#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC)
#define DM_VERITY_OPTS_MAX (2 + DM_VERITY_OPTS_FEC + \
DM_VERITY_ROOT_HASH_VERIFICATION_OPTS)
static unsigned dm_verity_prefetch_cluster = DM_VERITY_DEFAULT_PREFETCH_SIZE;
@ -717,6 +718,8 @@ static void verity_status(struct dm_target *ti, status_type_t type,
args++;
if (v->validated_blocks)
args++;
if (v->signature_key_desc)
args += DM_VERITY_ROOT_HASH_VERIFICATION_OPTS;
if (!args)
return;
DMEMIT(" %u", args);
@ -738,6 +741,9 @@ static void verity_status(struct dm_target *ti, status_type_t type,
if (v->validated_blocks)
DMEMIT(" " DM_VERITY_OPT_AT_MOST_ONCE);
sz = verity_fec_status_table(v, sz, result, maxlen);
if (v->signature_key_desc)
DMEMIT(" " DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY
" %s", v->signature_key_desc);
break;
}
}
@ -803,6 +809,8 @@ static void verity_dtr(struct dm_target *ti)
verity_fec_dtr(v);
kfree(v->signature_key_desc);
kfree(v);
}
@ -858,7 +866,8 @@ out:
return r;
}
static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
struct dm_verity_sig_opts *verify_args)
{
int r;
unsigned argc;
@ -907,6 +916,14 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
if (r)
return r;
continue;
} else if (verity_verify_is_sig_opt_arg(arg_name)) {
r = verity_verify_sig_parse_opt_args(as, v,
verify_args,
&argc, arg_name);
if (r)
return r;
continue;
}
ti->error = "Unrecognized verity feature request";
@ -933,6 +950,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v)
static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
struct dm_verity *v;
struct dm_verity_sig_opts verify_args = {0};
struct dm_arg_set as;
unsigned int num;
unsigned long long num_ll;
@ -940,6 +958,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
int i;
sector_t hash_position;
char dummy;
char *root_hash_digest_to_validate;
v = kzalloc(sizeof(struct dm_verity), GFP_KERNEL);
if (!v) {
@ -1073,6 +1092,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
r = -EINVAL;
goto bad;
}
root_hash_digest_to_validate = argv[8];
if (strcmp(argv[9], "-")) {
v->salt_size = strlen(argv[9]) / 2;
@ -1098,11 +1118,20 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
as.argc = argc;
as.argv = argv;
r = verity_parse_opt_args(&as, v);
r = verity_parse_opt_args(&as, v, &verify_args);
if (r < 0)
goto bad;
}
/* Root hash signature is a optional parameter*/
r = verity_verify_root_hash(root_hash_digest_to_validate,
strlen(root_hash_digest_to_validate),
verify_args.sig,
verify_args.sig_size);
if (r < 0) {
ti->error = "Root hash verification failed";
goto bad;
}
v->hash_per_block_bits =
__fls((1 << v->hash_dev_block_bits) / v->digest_size);
@ -1168,9 +1197,13 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
ti->per_io_data_size = roundup(ti->per_io_data_size,
__alignof__(struct dm_verity_io));
verity_verify_sig_opts_cleanup(&verify_args);
return 0;
bad:
verity_verify_sig_opts_cleanup(&verify_args);
verity_dtr(ti);
return r;
@ -1178,7 +1211,7 @@ bad:
static struct target_type verity_target = {
.name = "verity",
.version = {1, 4, 0},
.version = {1, 5, 0},
.module = THIS_MODULE,
.ctr = verity_ctr,
.dtr = verity_dtr,

View file

@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Microsoft Corporation.
*
* Author: Jaskaran Singh Khurana <jaskarankhurana@linux.microsoft.com>
*
*/
#include <linux/device-mapper.h>
#include <linux/verification.h>
#include <keys/user-type.h>
#include <linux/module.h>
#include "dm-verity.h"
#include "dm-verity-verify-sig.h"
#define DM_VERITY_VERIFY_ERR(s) DM_VERITY_ROOT_HASH_VERIFICATION " " s
static bool require_signatures;
module_param(require_signatures, bool, false);
MODULE_PARM_DESC(require_signatures,
"Verify the roothash of dm-verity hash tree");
#define DM_VERITY_IS_SIG_FORCE_ENABLED() \
(require_signatures != false)
bool verity_verify_is_sig_opt_arg(const char *arg_name)
{
return (!strcasecmp(arg_name,
DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY));
}
static int verity_verify_get_sig_from_key(const char *key_desc,
struct dm_verity_sig_opts *sig_opts)
{
struct key *key;
const struct user_key_payload *ukp;
int ret = 0;
key = request_key(&key_type_user,
key_desc, NULL);
if (IS_ERR(key))
return PTR_ERR(key);
down_read(&key->sem);
ukp = user_key_payload_locked(key);
if (!ukp) {
ret = -EKEYREVOKED;
goto end;
}
sig_opts->sig = kmalloc(ukp->datalen, GFP_KERNEL);
if (!sig_opts->sig) {
ret = -ENOMEM;
goto end;
}
sig_opts->sig_size = ukp->datalen;
memcpy(sig_opts->sig, ukp->data, sig_opts->sig_size);
end:
up_read(&key->sem);
key_put(key);
return ret;
}
int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
struct dm_verity *v,
struct dm_verity_sig_opts *sig_opts,
unsigned int *argc,
const char *arg_name)
{
struct dm_target *ti = v->ti;
int ret = 0;
const char *sig_key = NULL;
if (!*argc) {
ti->error = DM_VERITY_VERIFY_ERR("Signature key not specified");
return -EINVAL;
}
sig_key = dm_shift_arg(as);
(*argc)--;
ret = verity_verify_get_sig_from_key(sig_key, sig_opts);
if (ret < 0)
ti->error = DM_VERITY_VERIFY_ERR("Invalid key specified");
v->signature_key_desc = kstrdup(sig_key, GFP_KERNEL);
if (!v->signature_key_desc)
return -ENOMEM;
return ret;
}
/*
* verify_verify_roothash - Verify the root hash of the verity hash device
* using builtin trusted keys.
*
* @root_hash: For verity, the roothash/data to be verified.
* @root_hash_len: Size of the roothash/data to be verified.
* @sig_data: The trusted signature that verifies the roothash/data.
* @sig_len: Size of the signature.
*
*/
int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
const void *sig_data, size_t sig_len)
{
int ret;
if (!root_hash || root_hash_len == 0)
return -EINVAL;
if (!sig_data || sig_len == 0) {
if (DM_VERITY_IS_SIG_FORCE_ENABLED())
return -ENOKEY;
else
return 0;
}
ret = verify_pkcs7_signature(root_hash, root_hash_len, sig_data,
sig_len, NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
NULL, NULL);
return ret;
}
void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
{
kfree(sig_opts->sig);
sig_opts->sig = NULL;
sig_opts->sig_size = 0;
}

View file

@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Microsoft Corporation.
*
* Author: Jaskaran Singh Khurana <jaskarankhurana@linux.microsoft.com>
*
*/
#ifndef DM_VERITY_SIG_VERIFICATION_H
#define DM_VERITY_SIG_VERIFICATION_H
#define DM_VERITY_ROOT_HASH_VERIFICATION "DM Verity Sig Verification"
#define DM_VERITY_ROOT_HASH_VERIFICATION_OPT_SIG_KEY "root_hash_sig_key_desc"
struct dm_verity_sig_opts {
unsigned int sig_size;
u8 *sig;
};
#ifdef CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG
#define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 2
int verity_verify_root_hash(const void *data, size_t data_len,
const void *sig_data, size_t sig_len);
bool verity_verify_is_sig_opt_arg(const char *arg_name);
int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
struct dm_verity_sig_opts *sig_opts,
unsigned int *argc, const char *arg_name);
void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts);
#else
#define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 0
int verity_verify_root_hash(const void *data, size_t data_len,
const void *sig_data, size_t sig_len)
{
return 0;
}
bool verity_verify_is_sig_opt_arg(const char *arg_name)
{
return false;
}
int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
struct dm_verity_sig_opts *sig_opts,
unsigned int *argc, const char *arg_name)
{
return -EINVAL;
}
void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
{
}
#endif /* CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG */
#endif /* DM_VERITY_SIG_VERIFICATION_H */

View file

@ -63,6 +63,8 @@ struct dm_verity {
struct dm_verity_fec *fec; /* forward error correction */
unsigned long *validated_blocks; /* bitset blocks validated */
char *signature_key_desc; /* signature keyring reference */
};
struct dm_verity_io {

View file

@ -190,7 +190,6 @@ struct writeback_struct {
struct dm_writecache *wc;
struct wc_entry **wc_list;
unsigned wc_list_n;
struct page *page;
struct wc_entry *wc_list_inline[WB_LIST_INLINE];
struct bio bio;
};
@ -727,7 +726,8 @@ static void writecache_flush(struct dm_writecache *wc)
}
writecache_commit_flushed(wc);
writecache_wait_for_ios(wc, WRITE);
if (!WC_MODE_PMEM(wc))
writecache_wait_for_ios(wc, WRITE);
wc->seq_count++;
pmem_assign(sb(wc)->seq_count, cpu_to_le64(wc->seq_count));
@ -1561,7 +1561,7 @@ static void writecache_writeback(struct work_struct *work)
{
struct dm_writecache *wc = container_of(work, struct dm_writecache, writeback_work);
struct blk_plug plug;
struct wc_entry *e, *f, *g;
struct wc_entry *f, *g, *e = NULL;
struct rb_node *node, *next_node;
struct list_head skipped;
struct writeback_list wbl;
@ -1598,7 +1598,14 @@ restart:
break;
}
e = container_of(wc->lru.prev, struct wc_entry, lru);
if (unlikely(wc->writeback_all)) {
if (unlikely(!e)) {
writecache_flush(wc);
e = container_of(rb_first(&wc->tree), struct wc_entry, rb_node);
} else
e = g;
} else
e = container_of(wc->lru.prev, struct wc_entry, lru);
BUG_ON(e->write_in_progress);
if (unlikely(!writecache_entry_is_committed(wc, e))) {
writecache_flush(wc);
@ -1629,8 +1636,8 @@ restart:
if (unlikely(!next_node))
break;
g = container_of(next_node, struct wc_entry, rb_node);
if (read_original_sector(wc, g) ==
read_original_sector(wc, f)) {
if (unlikely(read_original_sector(wc, g) ==
read_original_sector(wc, f))) {
f = g;
continue;
}
@ -1659,8 +1666,14 @@ restart:
g->wc_list_contiguous = BIO_MAX_PAGES;
f = g;
e->wc_list_contiguous++;
if (unlikely(e->wc_list_contiguous == BIO_MAX_PAGES))
if (unlikely(e->wc_list_contiguous == BIO_MAX_PAGES)) {
if (unlikely(wc->writeback_all)) {
next_node = rb_next(&f->rb_node);
if (likely(next_node))
g = container_of(next_node, struct wc_entry, rb_node);
}
break;
}
}
cond_resched();
}

View file

@ -134,8 +134,6 @@ static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,
refcount_inc(&bioctx->ref);
generic_make_request(clone);
if (clone->bi_status == BLK_STS_IOERR)
return -EIO;
if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone))
zone->wp_block += nr_blocks;

View file

@ -457,7 +457,7 @@ static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
return -EIO;
tgt = dm_table_find_target(map, sector);
if (!dm_target_is_valid(tgt)) {
if (!tgt) {
ret = -EIO;
goto out;
}
@ -1072,7 +1072,7 @@ static struct dm_target *dm_dax_get_live_target(struct mapped_device *md,
return NULL;
ti = dm_table_find_target(map, sector);
if (!dm_target_is_valid(ti))
if (!ti)
return NULL;
return ti;
@ -1572,7 +1572,7 @@ static int __split_and_process_non_flush(struct clone_info *ci)
int r;
ti = dm_table_find_target(ci->map, ci->sector);
if (!dm_target_is_valid(ti))
if (!ti)
return -EIO;
if (__process_abnormal_io(ci, ti, &r))
@ -1748,7 +1748,7 @@ static blk_qc_t dm_process_bio(struct mapped_device *md,
if (!ti) {
ti = dm_table_find_target(map, bio->bi_iter.bi_sector);
if (unlikely(!ti || !dm_target_is_valid(ti))) {
if (unlikely(!ti)) {
bio_io_error(bio);
return ret;
}

View file

@ -85,11 +85,6 @@ struct target_type *dm_get_immutable_target_type(struct mapped_device *md);
int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t);
/*
* To check the return value from dm_table_find_target().
*/
#define dm_target_is_valid(t) ((t)->table)
/*
* To check whether the target type is bio-based or not (request-based).
*/

View file

@ -369,10 +369,6 @@ int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
*/
dm_tm_unlock(ll->tm, blk);
continue;
} else if (r < 0) {
dm_tm_unlock(ll->tm, blk);
return r;
}
dm_tm_unlock(ll->tm, blk);