mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 10:41:38 -04:00
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:
commit
896be8f44d
3550 changed files with 333627 additions and 71650 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(¶m_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(¶m_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) {
|
||||
|
|
|
|||
964
drivers/md/dm-clone-metadata.c
Normal file
964
drivers/md/dm-clone-metadata.c
Normal 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;
|
||||
}
|
||||
158
drivers/md/dm-clone-metadata.h
Normal file
158
drivers/md/dm-clone-metadata.h
Normal 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
2191
drivers/md/dm-clone-target.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
133
drivers/md/dm-verity-verify-sig.c
Normal file
133
drivers/md/dm-verity-verify-sig.c
Normal 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;
|
||||
}
|
||||
60
drivers/md/dm-verity-verify-sig.h
Normal file
60
drivers/md/dm-verity-verify-sig.h
Normal 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 */
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue