Commit 3e414b5b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.4/dm-changes' of...

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

Pull device mapper updates from Mike Snitzer:

 - crypto and DM crypt advances that allow the crypto API to reclaim
   implementation details that do not belong in DM crypt. The wrapper
   template for ESSIV generation that was factored out will also be used
   by fscrypt in the future.

 - Add root hash pkcs#7 signature verification to the DM verity target.

 - Add a new "clone" DM target that allows for efficient remote
   replication of a device.

 - Enhance DM bufio's cache to be tailored to each client based on use.
   Clients that make heavy use of the cache get more of it, and those
   that use less have reduced cache usage.

 - Add a new DM_GET_TARGET_VERSION ioctl to allow userspace to query the
   version number of a DM target (even if the associated module isn't
   yet loaded).

 - Fix invalid memory access in DM zoned target.

 - Fix the max_discard_sectors limit advertised by the DM raid target;
   it was mistakenly storing the limit in bytes rather than sectors.

 - Small optimizations and cleanups in DM writecache target.

 - Various fixes and cleanups in DM core, DM raid1 and space map portion
   of DM persistent data library.

* tag 'for-5.4/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: (22 commits)
  dm: introduce DM_GET_TARGET_VERSION
  dm bufio: introduce a global cache replacement
  dm bufio: remove old-style buffer cleanup
  dm bufio: introduce a global queue
  dm bufio: refactor adjust_total_allocated
  dm bufio: call adjust_total_allocated from __link_buffer and __unlink_buffer
  dm: add clone target
  dm raid: fix updating of max_discard_sectors limit
  dm writecache: skip writecache_wait for pmem mode
  dm stats: use struct_size() helper
  dm crypt: omit parsing of the encapsulated cipher
  dm crypt: switch to ESSIV crypto API template
  crypto: essiv - create wrapper template for ESSIV generation
  dm space map common: remove check for impossible sm_find_free() return value
  dm raid1: use struct_size() with kzalloc()
  dm writecache: optimize performance by sorting the blocks for writeback_all
  dm writecache: add unlikely for getting two block with same LBA
  dm writecache: remove unused member pointer in writeback_struct
  dm zoned: fix invalid memory access
  dm verity: add root hash pkcs#7 signature verification
  ...
parents 018c6837 afa179eb
This diff is collapsed.
...@@ -125,6 +125,13 @@ check_at_most_once ...@@ -125,6 +125,13 @@ check_at_most_once
blocks, and a hash block will not be verified any more after all the data blocks, and a hash block will not be verified any more after all the data
blocks it covers have been verified anyway. blocks it covers have been verified anyway.
root_hash_sig_key_desc <key_description>
This is the description of the USER_KEY that the kernel will lookup to get
the pkcs7 signature of the roothash. The pkcs7 signature is used to validate
the root hash during the creation of the device mapper block device.
Verification of roothash depends on the config DM_VERITY_VERIFY_ROOTHASH_SIG
being set in the kernel.
Theory of operation Theory of operation
=================== ===================
......
...@@ -487,6 +487,34 @@ config CRYPTO_ADIANTUM ...@@ -487,6 +487,34 @@ config CRYPTO_ADIANTUM
If unsure, say N. If unsure, say N.
config CRYPTO_ESSIV
tristate "ESSIV support for block encryption"
select CRYPTO_AUTHENC
help
Encrypted salt-sector initialization vector (ESSIV) is an IV
generation method that is used in some cases by fscrypt and/or
dm-crypt. It uses the hash of the block encryption key as the
symmetric key for a block encryption pass applied to the input
IV, making low entropy IV sources more suitable for block
encryption.
This driver implements a crypto API template that can be
instantiated either as a skcipher or as a aead (depending on the
type of the first template argument), and which defers encryption
and decryption requests to the encapsulated cipher after applying
ESSIV to the input IV. Note that in the aead case, it is assumed
that the keys are presented in the same format used by the authenc
template, and that the IV appears at the end of the authenticated
associated data (AAD) region (which is how dm-crypt uses it.)
Note that the use of ESSIV is not recommended for new deployments,
and so this only needs to be enabled when interoperability with
existing encrypted volumes of filesystems is required, or when
building for a particular system that requires it (e.g., when
the SoC in question has accelerated CBC but not XTS, making CBC
combined with ESSIV the only feasible mode for h/w accelerated
block encryption)
comment "Hash modes" comment "Hash modes"
config CRYPTO_CMAC config CRYPTO_CMAC
......
...@@ -165,6 +165,7 @@ obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o ...@@ -165,6 +165,7 @@ obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o
obj-$(CONFIG_CRYPTO_OFB) += ofb.o obj-$(CONFIG_CRYPTO_OFB) += ofb.o
obj-$(CONFIG_CRYPTO_ECC) += ecc.o obj-$(CONFIG_CRYPTO_ECC) += ecc.o
obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o
ecdh_generic-y += ecdh.o ecdh_generic-y += ecdh.o
ecdh_generic-y += ecdh_helper.o ecdh_generic-y += ecdh_helper.o
......
This diff is collapsed.
...@@ -271,6 +271,7 @@ config DM_CRYPT ...@@ -271,6 +271,7 @@ config DM_CRYPT
depends on BLK_DEV_DM depends on BLK_DEV_DM
select CRYPTO select CRYPTO
select CRYPTO_CBC select CRYPTO_CBC
select CRYPTO_ESSIV
---help--- ---help---
This device-mapper target allows you to create a device that This device-mapper target allows you to create a device that
transparently encrypts the data on it. You'll need to activate transparently encrypts the data on it. You'll need to activate
...@@ -346,6 +347,20 @@ config DM_ERA ...@@ -346,6 +347,20 @@ config DM_ERA
over time. Useful for maintaining cache coherency when using over time. Useful for maintaining cache coherency when using
vendor snapshots. 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 config DM_MIRROR
tristate "Mirror target" tristate "Mirror target"
depends on BLK_DEV_DM depends on BLK_DEV_DM
...@@ -490,6 +505,18 @@ config DM_VERITY ...@@ -490,6 +505,18 @@ config DM_VERITY
If unsure, say N. 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_FEC config DM_VERITY_FEC
bool "Verity forward error correction support" bool "Verity forward error correction support"
depends on DM_VERITY depends on DM_VERITY
......
...@@ -18,6 +18,7 @@ dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o \ ...@@ -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-background-tracker.o
dm-cache-smq-y += dm-cache-policy-smq.o dm-cache-smq-y += dm-cache-policy-smq.o
dm-era-y += dm-era-target.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 dm-verity-y += dm-verity-target.o
md-mod-y += md.o md-bitmap.o md-mod-y += md.o md-bitmap.o
raid456-y += raid5.o raid5-cache.o raid5-ppl.o raid456-y += raid5.o raid5-cache.o raid5-ppl.o
...@@ -65,6 +66,7 @@ obj-$(CONFIG_DM_VERITY) += dm-verity.o ...@@ -65,6 +66,7 @@ obj-$(CONFIG_DM_VERITY) += dm-verity.o
obj-$(CONFIG_DM_CACHE) += dm-cache.o obj-$(CONFIG_DM_CACHE) += dm-cache.o
obj-$(CONFIG_DM_CACHE_SMQ) += dm-cache-smq.o obj-$(CONFIG_DM_CACHE_SMQ) += dm-cache-smq.o
obj-$(CONFIG_DM_ERA) += dm-era.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_LOG_WRITES) += dm-log-writes.o
obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o obj-$(CONFIG_DM_INTEGRITY) += dm-integrity.o
obj-$(CONFIG_DM_ZONED) += dm-zoned.o obj-$(CONFIG_DM_ZONED) += dm-zoned.o
...@@ -81,3 +83,7 @@ endif ...@@ -81,3 +83,7 @@ endif
ifeq ($(CONFIG_DM_VERITY_FEC),y) ifeq ($(CONFIG_DM_VERITY_FEC),y)
dm-verity-objs += dm-verity-fec.o dm-verity-objs += dm-verity-fec.o
endif endif
ifeq ($(CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG),y)
dm-verity-objs += dm-verity-verify-sig.o
endif
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
#define DM_BUFIO_MEMORY_PERCENT 2 #define DM_BUFIO_MEMORY_PERCENT 2
#define DM_BUFIO_VMALLOC_PERCENT 25 #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) * Check buffer ages in this interval (seconds)
...@@ -132,12 +133,14 @@ enum data_mode { ...@@ -132,12 +133,14 @@ enum data_mode {
struct dm_buffer { struct dm_buffer {
struct rb_node node; struct rb_node node;
struct list_head lru_list; struct list_head lru_list;
struct list_head global_list;
sector_t block; sector_t block;
void *data; void *data;
unsigned char data_mode; /* DATA_MODE_* */ unsigned char data_mode; /* DATA_MODE_* */
unsigned char list_mode; /* LIST_* */ unsigned char list_mode; /* LIST_* */
blk_status_t read_error; blk_status_t read_error;
blk_status_t write_error; blk_status_t write_error;
unsigned accessed;
unsigned hold_count; unsigned hold_count;
unsigned long state; unsigned long state;
unsigned long last_accessed; unsigned long last_accessed;
...@@ -192,7 +195,11 @@ static unsigned long dm_bufio_cache_size; ...@@ -192,7 +195,11 @@ static unsigned long dm_bufio_cache_size;
*/ */
static unsigned long dm_bufio_cache_size_latch; 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 * Buffers are freed after this timeout
...@@ -208,11 +215,6 @@ static unsigned long dm_bufio_current_allocated; ...@@ -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. * The current number of clients.
*/ */
...@@ -224,11 +226,15 @@ static int dm_bufio_client_count; ...@@ -224,11 +226,15 @@ static int dm_bufio_client_count;
static LIST_HEAD(dm_bufio_all_clients); static LIST_HEAD(dm_bufio_all_clients);
/* /*
* This mutex protects dm_bufio_cache_size_latch, * This mutex protects dm_bufio_cache_size_latch and dm_bufio_client_count
* dm_bufio_cache_size_per_client and dm_bufio_client_count
*/ */
static DEFINE_MUTEX(dm_bufio_clients_lock); 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 #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
static void buffer_record_stack(struct dm_buffer *b) 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) ...@@ -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] = { static unsigned long * const class_ptr[DATA_MODE_LIMIT] = {
&dm_bufio_allocated_kmem_cache, &dm_bufio_allocated_kmem_cache,
&dm_bufio_allocated_get_free_pages, &dm_bufio_allocated_get_free_pages,
&dm_bufio_allocated_vmalloc, &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; *class_ptr[data_mode] += diff;
...@@ -302,7 +316,19 @@ static void adjust_total_allocated(unsigned char data_mode, long 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) if (dm_bufio_current_allocated > dm_bufio_peak_allocated)
dm_bufio_peak_allocated = dm_bufio_current_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) ...@@ -323,9 +349,6 @@ static void __cache_size_refresh(void)
dm_bufio_default_cache_size); dm_bufio_default_cache_size);
dm_bufio_cache_size_latch = 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) ...@@ -431,8 +454,6 @@ static struct dm_buffer *alloc_buffer(struct dm_bufio_client *c, gfp_t gfp_mask)
return NULL; return NULL;
} }
adjust_total_allocated(b->data_mode, (long)c->block_size);
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING #ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
b->stack_len = 0; b->stack_len = 0;
#endif #endif
...@@ -446,8 +467,6 @@ static void free_buffer(struct dm_buffer *b) ...@@ -446,8 +467,6 @@ static void free_buffer(struct dm_buffer *b)
{ {
struct dm_bufio_client *c = b->c; 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); free_buffer_data(c, b->data, b->data_mode);
kmem_cache_free(c->slab_buffer, b); kmem_cache_free(c->slab_buffer, b);
} }
...@@ -465,6 +484,8 @@ static void __link_buffer(struct dm_buffer *b, sector_t block, int dirty) ...@@ -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]); list_add(&b->lru_list, &c->lru[dirty]);
__insert(b->c, b); __insert(b->c, b);
b->last_accessed = jiffies; b->last_accessed = jiffies;
adjust_total_allocated(b, false);
} }
/* /*
...@@ -479,6 +500,8 @@ static void __unlink_buffer(struct dm_buffer *b) ...@@ -479,6 +500,8 @@ static void __unlink_buffer(struct dm_buffer *b)
c->n_buffers[b->list_mode]--; c->n_buffers[b->list_mode]--;
__remove(b->c, b); __remove(b->c, b);
list_del(&b->lru_list); list_del(&b->lru_list);
adjust_total_allocated(b, true);
} }
/* /*
...@@ -488,6 +511,8 @@ static void __relink_lru(struct dm_buffer *b, int dirty) ...@@ -488,6 +511,8 @@ static void __relink_lru(struct dm_buffer *b, int dirty)
{ {
struct dm_bufio_client *c = b->c; struct dm_bufio_client *c = b->c;
b->accessed = 1;
BUG_ON(!c->n_buffers[b->list_mode]); BUG_ON(!c->n_buffers[b->list_mode]);
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, ...@@ -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. * Check if we're over watermark.
* If we are over threshold_buffers, start freeing buffers. * If we are over threshold_buffers, start freeing buffers.
...@@ -944,23 +939,7 @@ static void __get_memory_limit(struct dm_bufio_client *c, ...@@ -944,23 +939,7 @@ static void __get_memory_limit(struct dm_bufio_client *c,
static void __check_watermark(struct dm_bufio_client *c, static void __check_watermark(struct dm_bufio_client *c,
struct list_head *write_list) struct list_head *write_list)
{ {
unsigned long threshold_buffers, limit_buffers; if (c->n_buffers[LIST_DIRTY] > c->n_buffers[LIST_CLEAN] * DM_BUFIO_WRITEBACK_RATIO)
__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)
__write_dirty_buffers_async(c, 1, write_list); __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) ...@@ -1841,6 +1820,74 @@ static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
dm_bufio_unlock(c); 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) static void cleanup_old_buffers(void)
{ {
unsigned long max_age_hz = get_max_age_hz(); unsigned long max_age_hz = get_max_age_hz();
...@@ -1856,14 +1903,11 @@ static void cleanup_old_buffers(void) ...@@ -1856,14 +1903,11 @@ static void cleanup_old_buffers(void)
mutex_unlock(&dm_bufio_clients_lock); 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) static void work_fn(struct work_struct *w)
{ {
cleanup_old_buffers(); 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); DM_BUFIO_WORK_TIMER_SECS * HZ);
} }
...@@ -1905,8 +1949,9 @@ static int __init dm_bufio_init(void) ...@@ -1905,8 +1949,9 @@ static int __init dm_bufio_init(void)
if (!dm_bufio_wq) if (!dm_bufio_wq)
return -ENOMEM; return -ENOMEM;
INIT_DELAYED_WORK(&dm_bufio_work, work_fn); INIT_DELAYED_WORK(&dm_bufio_cleanup_old_work, work_fn);
queue_delayed_work(dm_bufio_wq, &dm_bufio_work, 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); DM_BUFIO_WORK_TIMER_SECS * HZ);
return 0; return 0;
...@@ -1919,7 +1964,8 @@ static void __exit dm_bufio_exit(void) ...@@ -1919,7 +1964,8 @@ static void __exit dm_bufio_exit(void)
{ {
int bug = 0; 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); destroy_workqueue(dm_bufio_wq);
if (dm_bufio_client_count) { if (dm_bufio_client_count) {
......
This diff is collapsed.
/* 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 */
This diff is collapsed.
This diff is collapsed.
...@@ -601,17 +601,27 @@ static void list_version_get_info(struct target_type *tt, void *param) ...@@ -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); 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; size_t len, needed = 0;
struct dm_target_versions *vers; struct dm_target_versions *vers;
struct vers_iter iter_info; 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 * Loop through all the devices working out how much
* space we need. * space we need.
*/ */
if (!tt)
dm_target_iterate(list_version_get_needed, &needed); dm_target_iterate(list_version_get_needed, &needed);
else
list_version_get_needed(tt, &needed);
/* /*
* Grab our output buffer. * Grab our output buffer.
...@@ -632,13 +642,28 @@ static int list_versions(struct file *filp, struct dm_ioctl *param, size_t param ...@@ -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. * Now loop through filling out the names & versions.
*/ */
if (!tt)
dm_target_iterate(list_version_get_info, &iter_info); dm_target_iterate(list_version_get_info, &iter_info);
else
list_version_get_info(tt, &iter_info);
param->flags |= iter_info.flags; param->flags |= iter_info.flags;
out: out:
if (tt)
dm_put_target_type(tt);
return 0; 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) static int check_name(const char *name)
{ {
if (strchr(name, '/')) { if (strchr(name, '/')) {
...@@ -1592,7 +1617,7 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para ...@@ -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); ti = dm_table_find_target(table, tmsg->sector);
if (!dm_target_is_valid(ti)) { if (!ti) {
DMWARN("Target message sector outside device."); DMWARN("Target message sector outside device.");
r = -EINVAL; r = -EINVAL;
} else if (ti->type->message) } else if (ti->type->message)
...@@ -1664,6 +1689,7 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) ...@@ -1664,6 +1689,7 @@ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
{DM_TARGET_MSG_CMD, 0, target_message}, {DM_TARGET_MSG_CMD, 0, target_message},
{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry}, {DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
{DM_DEV_ARM_POLL, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll}, {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))) if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))
......
...@@ -3738,18 +3738,18 @@ static int raid_iterate_devices(struct dm_target *ti, ...@@ -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) static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
{ {
struct raid_set *rs = ti->private; 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_min(limits, chunk_size_bytes);
blk_limits_io_opt(limits, chunk_size * mddev_data_stripes(rs)); blk_limits_io_opt(limits, chunk_size_bytes * mddev_data_stripes(rs));
/* /*
* RAID1 and RAID10 personalities require bio splitting, * RAID1 and RAID10 personalities require bio splitting,
* RAID0/4/5/6 don't and process large discard bios properly. * RAID0/4/5/6 don't and process large discard bios properly.
*/ */
if (rs_is_raid1(rs) || rs_is_raid10(rs)) { if (rs_is_raid1(rs) || rs_is_raid10(rs)) {
limits->discard_granularity = chunk_size; limits->discard_granularity = chunk_size_bytes;
limits->max_discard_sectors = chunk_size; limits->max_discard_sectors = rs->md.chunk_sectors;
} }
} }
......
...@@ -878,12 +878,9 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors, ...@@ -878,12 +878,9 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
struct dm_target *ti, struct dm_target *ti,
struct dm_dirty_log *dl) struct dm_dirty_log *dl)
{ {
size_t len; struct mirror_set *ms =
struct mirror_set *ms = NULL; 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) { if (!ms) {
ti->error = "Cannot allocate mirror context"; ti->error = "Cannot allocate mirror context";
return NULL; return NULL;
......
...@@ -262,7 +262,7 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end, ...@@ -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)) if (n_entries != (size_t)n_entries || !(size_t)(n_entries + 1))
return -EOVERFLOW; 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) if ((shared_alloc_size - sizeof(struct dm_stat)) / sizeof(struct dm_stat_shared) != n_entries)
return -EOVERFLOW; return -EOVERFLOW;
......
...@@ -163,10 +163,8 @@ static int alloc_targets(struct dm_table *t, unsigned int num) ...@@ -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. * 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)); sizeof(sector_t));
if (!n_highs) if (!n_highs)
return -ENOMEM; return -ENOMEM;
...@@ -1359,7 +1357,7 @@ struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index) ...@@ -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. * 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. * to trap I/O beyond end of device.
*/ */
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector) 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) ...@@ -1368,7 +1366,7 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
sector_t *node; sector_t *node;
if (unlikely(sector >= dm_table_get_size(t))) if (unlikely(sector >= dm_table_get_size(t)))
return &t->targets[t->num_targets]; return NULL;
for (l = 0; l < t->depth; l++) { for (l = 0; l < t->depth; l++) {
n = get_child(n, k); n = get_child(n, k);
......
This diff is collapsed.
This diff is collapsed.
// 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 { ...@@ -63,6 +63,8 @@ struct dm_verity {
struct dm_verity_fec *fec; /* forward error correction */ struct dm_verity_fec *fec; /* forward error correction */
unsigned long *validated_blocks; /* bitset blocks validated */ unsigned long *validated_blocks; /* bitset blocks validated */
char *signature_key_desc; /* signature keyring reference */
}; };
struct dm_verity_io { struct dm_verity_io {
......
This diff is collapsed.
...@@ -134,8 +134,6 @@ static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone, ...@@ -134,8 +134,6 @@ static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,
refcount_inc(&bioctx->ref); refcount_inc(&bioctx->ref);
generic_make_request(clone); generic_make_request(clone);
if (clone->bi_status == BLK_STS_IOERR)
return -EIO;
if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone)) if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone))
zone->wp_block += nr_blocks; zone->wp_block += nr_blocks;
......
This diff is collapsed.
...@@ -85,11 +85,6 @@ struct target_type *dm_get_immutable_target_type(struct mapped_device *md); ...@@ -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); 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). * 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, ...@@ -369,10 +369,6 @@ int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
*/ */
dm_tm_unlock(ll->tm, blk); dm_tm_unlock(ll->tm, blk);
continue; continue;
} else if (r < 0) {
dm_tm_unlock(ll->tm, blk);
return r;
} }
dm_tm_unlock(ll->tm, blk); dm_tm_unlock(ll->tm, blk);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment