Commit 48dc8100 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

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

Pull device mapper updates from Mike Snitzer:

 - Split dm-bufio's rw_semaphore and rbtree. Offers improvements to
   dm-bufio's locking to allow increased concurrent IO -- particularly
   for read access for buffers already in dm-bufio's cache.

 - Also split dm-bio-prison-v1's spinlock and rbtree with comparable aim
   at improving concurrent IO (for the DM thinp target).

 - Both the dm-bufio and dm-bio-prison-v1 scaling of the number of locks
   and rbtrees used are managed by dm_num_hash_locks(). And the hash
   function used by both is dm_hash_locks_index().

 - Allow DM targets to require DISCARD, WRITE_ZEROES and SECURE_ERASE to
   be split at the target specified boundary (in terms of
   max_discard_sectors, max_write_zeroes_sectors and
   max_secure_erase_sectors respectively).

 - DM verity error handling fix for check_at_most_once on FEC.

 - Update DM verity target to emit audit events on verification failure
   and more.

 - DM core ->io_hints improvements needed in support of new discard
   support that is added to the DM "zero" and "error" targets.

 - Fix missing kmem_cache_destroy() call in initialization error path of
   both the DM integrity and DM clone targets.

 - A couple fixes for DM flakey, also add "error_reads" feature.

 - Fix DM core's resume to not lock FS when the DM map is NULL;
   otherwise initial table load can race with FS mount that takes
   superblock's ->s_umount rw_semaphore.

 - Various small improvements to both DM core and DM targets.

* tag 'for-6.4/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: (40 commits)
  dm: don't lock fs when the map is NULL in process of resume
  dm flakey: add an "error_reads" option
  dm flakey: remove trailing space in the table line
  dm flakey: fix a crash with invalid table line
  dm ioctl: fix nested locking in table_clear() to remove deadlock concern
  dm: unexport dm_get_queue_limits()
  dm: allow targets to require splitting WRITE_ZEROES and SECURE_ERASE
  dm: add helper macro for simple DM target module init and exit
  dm raid: remove unused d variable
  dm: remove unnecessary (void*) conversions
  dm mirror: add DMERR message if alloc_workqueue fails
  dm: push error reporting down to dm_register_target()
  dm integrity: call kmem_cache_destroy() in dm_integrity_init() error path
  dm clone: call kmem_cache_destroy() in dm_clone_init() error path
  dm error: add discard support
  dm zero: add discard support
  dm table: allow targets without devices to set ->io_hints
  dm verity: emit audit events on verification failure and more
  dm verity: fix error handling for check_at_most_once on FEC
  dm: improve hash_locks sizing and hash function
  ...
parents 9dd6956b 38d11da5
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -39,6 +39,10 @@ Optional feature parameters:
  If no feature parameters are present, during the periods of
  unreliability, all I/O returns errors.

  error_reads:
	All read I/O is failed with an error signalled.
	Write I/O is handled correctly.

  drop_writes:
	All write I/O is silently ignored.
	Read I/O is handled correctly.
+64 −30
Original line number Diff line number Diff line
@@ -18,10 +18,15 @@

#define MIN_CELLS 1024

struct dm_bio_prison {
struct prison_region {
	spinlock_t lock;
	struct rb_root cells;
	struct rb_root cell;
} ____cacheline_aligned_in_smp;

struct dm_bio_prison {
	mempool_t cell_pool;
	unsigned int num_locks;
	struct prison_region regions[];
};

static struct kmem_cache *_cell_cache;
@@ -34,13 +39,20 @@ static struct kmem_cache *_cell_cache;
 */
struct dm_bio_prison *dm_bio_prison_create(void)
{
	struct dm_bio_prison *prison = kzalloc(sizeof(*prison), GFP_KERNEL);
	int ret;
	unsigned int i, num_locks;
	struct dm_bio_prison *prison;

	num_locks = dm_num_hash_locks();
	prison = kzalloc(struct_size(prison, regions, num_locks), GFP_KERNEL);
	if (!prison)
		return NULL;
	prison->num_locks = num_locks;

	spin_lock_init(&prison->lock);
	for (i = 0; i < prison->num_locks; i++) {
		spin_lock_init(&prison->regions[i].lock);
		prison->regions[i].cell = RB_ROOT;
	}

	ret = mempool_init_slab_pool(&prison->cell_pool, MIN_CELLS, _cell_cache);
	if (ret) {
@@ -48,8 +60,6 @@ struct dm_bio_prison *dm_bio_prison_create(void)
		return NULL;
	}

	prison->cells = RB_ROOT;

	return prison;
}
EXPORT_SYMBOL_GPL(dm_bio_prison_create);
@@ -107,14 +117,32 @@ static int cmp_keys(struct dm_cell_key *lhs,
	return 0;
}

static int __bio_detain(struct dm_bio_prison *prison,
static inline unsigned int lock_nr(struct dm_cell_key *key, unsigned int num_locks)
{
	return dm_hash_locks_index((key->block_begin >> BIO_PRISON_MAX_RANGE_SHIFT),
				   num_locks);
}

bool dm_cell_key_has_valid_range(struct dm_cell_key *key)
{
	if (WARN_ON_ONCE(key->block_end - key->block_begin > BIO_PRISON_MAX_RANGE))
		return false;
	if (WARN_ON_ONCE((key->block_begin >> BIO_PRISON_MAX_RANGE_SHIFT) !=
			 (key->block_end - 1) >> BIO_PRISON_MAX_RANGE_SHIFT))
		return false;

	return true;
}
EXPORT_SYMBOL(dm_cell_key_has_valid_range);

static int __bio_detain(struct rb_root *root,
			struct dm_cell_key *key,
			struct bio *inmate,
			struct dm_bio_prison_cell *cell_prealloc,
			struct dm_bio_prison_cell **cell_result)
{
	int r;
	struct rb_node **new = &prison->cells.rb_node, *parent = NULL;
	struct rb_node **new = &root->rb_node, *parent = NULL;

	while (*new) {
		struct dm_bio_prison_cell *cell =
@@ -139,7 +167,7 @@ static int __bio_detain(struct dm_bio_prison *prison,
	*cell_result = cell_prealloc;

	rb_link_node(&cell_prealloc->node, parent, new);
	rb_insert_color(&cell_prealloc->node, &prison->cells);
	rb_insert_color(&cell_prealloc->node, root);

	return 0;
}
@@ -151,10 +179,11 @@ static int bio_detain(struct dm_bio_prison *prison,
		      struct dm_bio_prison_cell **cell_result)
{
	int r;
	unsigned l = lock_nr(key, prison->num_locks);

	spin_lock_irq(&prison->lock);
	r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result);
	spin_unlock_irq(&prison->lock);
	spin_lock_irq(&prison->regions[l].lock);
	r = __bio_detain(&prison->regions[l].cell, key, inmate, cell_prealloc, cell_result);
	spin_unlock_irq(&prison->regions[l].lock);

	return r;
}
@@ -181,11 +210,11 @@ EXPORT_SYMBOL_GPL(dm_get_cell);
/*
 * @inmates must have been initialised prior to this call
 */
static void __cell_release(struct dm_bio_prison *prison,
static void __cell_release(struct rb_root *root,
			   struct dm_bio_prison_cell *cell,
			   struct bio_list *inmates)
{
	rb_erase(&cell->node, &prison->cells);
	rb_erase(&cell->node, root);

	if (inmates) {
		if (cell->holder)
@@ -198,20 +227,22 @@ void dm_cell_release(struct dm_bio_prison *prison,
		     struct dm_bio_prison_cell *cell,
		     struct bio_list *bios)
{
	spin_lock_irq(&prison->lock);
	__cell_release(prison, cell, bios);
	spin_unlock_irq(&prison->lock);
	unsigned l = lock_nr(&cell->key, prison->num_locks);

	spin_lock_irq(&prison->regions[l].lock);
	__cell_release(&prison->regions[l].cell, cell, bios);
	spin_unlock_irq(&prison->regions[l].lock);
}
EXPORT_SYMBOL_GPL(dm_cell_release);

/*
 * Sometimes we don't want the holder, just the additional bios.
 */
static void __cell_release_no_holder(struct dm_bio_prison *prison,
static void __cell_release_no_holder(struct rb_root *root,
				     struct dm_bio_prison_cell *cell,
				     struct bio_list *inmates)
{
	rb_erase(&cell->node, &prison->cells);
	rb_erase(&cell->node, root);
	bio_list_merge(inmates, &cell->bios);
}

@@ -219,11 +250,12 @@ void dm_cell_release_no_holder(struct dm_bio_prison *prison,
			       struct dm_bio_prison_cell *cell,
			       struct bio_list *inmates)
{
	unsigned l = lock_nr(&cell->key, prison->num_locks);
	unsigned long flags;

	spin_lock_irqsave(&prison->lock, flags);
	__cell_release_no_holder(prison, cell, inmates);
	spin_unlock_irqrestore(&prison->lock, flags);
	spin_lock_irqsave(&prison->regions[l].lock, flags);
	__cell_release_no_holder(&prison->regions[l].cell, cell, inmates);
	spin_unlock_irqrestore(&prison->regions[l].lock, flags);
}
EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);

@@ -248,18 +280,19 @@ void dm_cell_visit_release(struct dm_bio_prison *prison,
			   void *context,
			   struct dm_bio_prison_cell *cell)
{
	spin_lock_irq(&prison->lock);
	unsigned l = lock_nr(&cell->key, prison->num_locks);
	spin_lock_irq(&prison->regions[l].lock);
	visit_fn(context, cell);
	rb_erase(&cell->node, &prison->cells);
	spin_unlock_irq(&prison->lock);
	rb_erase(&cell->node, &prison->regions[l].cell);
	spin_unlock_irq(&prison->regions[l].lock);
}
EXPORT_SYMBOL_GPL(dm_cell_visit_release);

static int __promote_or_release(struct dm_bio_prison *prison,
static int __promote_or_release(struct rb_root *root,
				struct dm_bio_prison_cell *cell)
{
	if (bio_list_empty(&cell->bios)) {
		rb_erase(&cell->node, &prison->cells);
		rb_erase(&cell->node, root);
		return 1;
	}

@@ -271,10 +304,11 @@ int dm_cell_promote_or_release(struct dm_bio_prison *prison,
			       struct dm_bio_prison_cell *cell)
{
	int r;
	unsigned l = lock_nr(&cell->key, prison->num_locks);

	spin_lock_irq(&prison->lock);
	r = __promote_or_release(prison, cell);
	spin_unlock_irq(&prison->lock);
	spin_lock_irq(&prison->regions[l].lock);
	r = __promote_or_release(&prison->regions[l].cell, cell);
	spin_unlock_irq(&prison->regions[l].lock);

	return r;
}
+15 −0
Original line number Diff line number Diff line
@@ -34,6 +34,16 @@ struct dm_cell_key {
	dm_block_t block_begin, block_end;
};

/*
 * The range of a key (block_end - block_begin) must not
 * exceed BIO_PRISON_MAX_RANGE.  Also the range must not
 * cross a similarly sized boundary.
 *
 * Must be a power of 2.
 */
#define BIO_PRISON_MAX_RANGE 1024
#define BIO_PRISON_MAX_RANGE_SHIFT 10

/*
 * Treat this as opaque, only in header so callers can manage allocation
 * themselves.
@@ -73,6 +83,11 @@ int dm_get_cell(struct dm_bio_prison *prison,
		struct dm_bio_prison_cell *cell_prealloc,
		struct dm_bio_prison_cell **cell_result);

/*
 * Returns false if key is beyond BIO_PRISON_MAX_RANGE or spans a boundary.
 */
bool dm_cell_key_has_valid_range(struct dm_cell_key *key);

/*
 * An atomic op that combines retrieving or creating a cell, and adding a
 * bio to it.
+1363 −617

File changed.

Preview size limit exceeded, changes collapsed.

+0 −1
Original line number Diff line number Diff line
@@ -3459,7 +3459,6 @@ static int __init dm_cache_init(void)

	r = dm_register_target(&cache_target);
	if (r) {
		DMERR("cache target registration failed: %d", r);
		kmem_cache_destroy(migration_cache);
		return r;
	}
Loading