Skip to content

Commit

Permalink
Merge tag 'for-6.4/dm-changes' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…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
  ...
  • Loading branch information
Linus Torvalds committed Apr 26, 2023
2 parents 9dd6956 + 38d11da commit 48dc810
Show file tree
Hide file tree
Showing 41 changed files with 1,773 additions and 1,080 deletions.
4 changes: 4 additions & 0 deletions Documentation/admin-guide/device-mapper/dm-flakey.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
94 changes: 64 additions & 30 deletions drivers/md/dm-bio-prison-v1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,22 +39,27 @@ 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) {
kfree(prison);
return NULL;
}

prison->cells = RB_ROOT;

return prison;
}
EXPORT_SYMBOL_GPL(dm_bio_prison_create);
Expand Down Expand Up @@ -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 =
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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)
Expand All @@ -198,32 +227,35 @@ 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);
}

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);

Expand All @@ -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;
}

Expand All @@ -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;
}
Expand Down
15 changes: 15 additions & 0 deletions drivers/md/dm-bio-prison-v1.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 48dc810

Please sign in to comment.