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

Pull device mapper fixes from Mikulas Patocka:

 - fix a dm-crypt race condition that could make errors not being reported

 - dm-cache:
    - fix rwsem being locked and unlocked from different processes
    - fix demotion statistics

 - dm-integrity:
    - set the 'stable writes' flag
    - fix a buffer overflow introduced in this merge window
    - fix an infinite loop if tag size is greater than 64

 - fix NULL pointer dereference in dm-integrity data-recovery mode

 - remove a bogus restriction on the dm-ebs starting sector offset

* tag 'for-7.3/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm-ebs: fix incorrect device offset check in ebs_ctr()
  dm-integrity: fix NULL pointer dereference when the 'R' flag is used
  dm cache: fix demotion stats in passthrough mode
  dm-integrity: fix infinite loop on discard with large tag size
  dm-integrity: fix buffer overflow with keyed discard
  dm-integrity: require stable writes for internal hash modes
  dm cache: fix issue with background work locking
  dm-crypt: fix a tiny race condition in crypt_dec_pending
This commit is contained in:
Linus Torvalds
2026-09-03 08:30:45 -07:00
4 changed files with 44 additions and 34 deletions
+31 -28
View File
@@ -340,8 +340,6 @@ struct cache {
struct list_head invalidation_requests;
sector_t migration_threshold;
wait_queue_head_t migration_wait;
atomic_t nr_allocated_migrations;
/*
* The number of in flight migrations that are performing
@@ -397,7 +395,11 @@ struct cache {
bool loaded_mappings:1;
bool loaded_discards:1;
struct rw_semaphore background_work_lock;
/* background work management */
bool background_work_allowed;
unsigned background_work_nr;
spinlock_t background_work_lock;
wait_queue_head_t background_work_wait;
struct batcher committer;
struct work_struct commit_ws;
@@ -488,19 +490,13 @@ static struct dm_cache_migration *alloc_migration(struct cache *cache)
memset(mg, 0, sizeof(*mg));
mg->cache = cache;
atomic_inc(&cache->nr_allocated_migrations);
return mg;
}
static void free_migration(struct dm_cache_migration *mg)
{
struct cache *cache = mg->cache;
if (atomic_dec_and_test(&cache->nr_allocated_migrations))
wake_up(&cache->migration_wait);
mempool_free(mg, &cache->migration_pool);
mempool_free(mg, &mg->cache->migration_pool);
}
/*----------------------------------------------------------------*/
@@ -1030,34 +1026,39 @@ static void calc_discard_block_range(struct cache *cache, struct bio *bio,
static void prevent_background_work(struct cache *cache)
{
lockdep_off();
down_write(&cache->background_work_lock);
lockdep_on();
spin_lock_irq(&cache->background_work_lock);
cache->background_work_allowed = false;
wait_event_lock_irq(cache->background_work_wait,
cache->background_work_nr == 0,
cache->background_work_lock);
spin_unlock_irq(&cache->background_work_lock);
}
static void allow_background_work(struct cache *cache)
{
lockdep_off();
up_write(&cache->background_work_lock);
lockdep_on();
spin_lock_irq(&cache->background_work_lock);
cache->background_work_allowed = true;
spin_unlock_irq(&cache->background_work_lock);
}
static bool background_work_begin(struct cache *cache)
{
bool r;
lockdep_off();
r = down_read_trylock(&cache->background_work_lock);
lockdep_on();
spin_lock_irq(&cache->background_work_lock);
r = cache->background_work_allowed;
if (r)
cache->background_work_nr++;
spin_unlock_irq(&cache->background_work_lock);
return r;
}
static void background_work_end(struct cache *cache)
{
lockdep_off();
up_read(&cache->background_work_lock);
lockdep_on();
spin_lock_irq(&cache->background_work_lock);
if (--cache->background_work_nr == 0)
wake_up(&cache->background_work_wait);
spin_unlock_irq(&cache->background_work_lock);
}
/*----------------------------------------------------------------*/
@@ -1462,6 +1463,9 @@ static void invalidate_complete(struct dm_cache_migration *mg, bool success)
struct bio_list bios;
struct cache *cache = mg->cache;
if (success)
atomic_inc(&cache->stats.demotion);
bio_list_init(&bios);
if (mg->cell) {
if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios))
@@ -1733,7 +1737,6 @@ static int map_bio(struct cache *cache, struct bio *bio, dm_oblock_t block,
if (passthrough_mode(cache)) {
if (bio_data_dir(bio) == WRITE) {
bio_drop_shared_lock(cache, bio);
atomic_inc(&cache->stats.demotion);
invalidate_start(cache, cblock, block, bio);
return DM_MAPIO_SUBMITTED;
} else
@@ -2507,9 +2510,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)
spin_lock_init(&cache->lock);
bio_list_init(&cache->deferred_bios);
atomic_set(&cache->nr_allocated_migrations, 0);
atomic_set(&cache->nr_io_migrations, 0);
init_waitqueue_head(&cache->migration_wait);
r = -ENOMEM;
atomic_set(&cache->nr_dirty, 0);
@@ -2592,8 +2593,10 @@ static int cache_create(struct cache_args *ca, struct cache **result)
issue_op, cache, cache->wq);
dm_iot_init(&cache->tracker);
init_rwsem(&cache->background_work_lock);
prevent_background_work(cache);
init_waitqueue_head(&cache->background_work_wait);
spin_lock_init(&cache->background_work_lock);
cache->background_work_allowed = false;
cache->background_work_nr = 0;
*result = cache;
return 0;
+1 -2
View File
@@ -1745,7 +1745,6 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
{
struct crypt_config *cc = io->cc;
struct bio *base_bio = io->base_bio;
blk_status_t error = io->error;
if (!atomic_dec_and_test(&io->io_pending))
return;
@@ -1767,7 +1766,7 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
else
kfree(io->integrity_metadata);
base_bio->bi_status = error;
base_bio->bi_status = io->error;
bio_endio(base_bio);
}
+1 -2
View File
@@ -265,8 +265,7 @@ static int ebs_ctr(struct dm_target *ti, unsigned int argc, char **argv)
r = -EINVAL;
if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 ||
tmp != (sector_t)tmp ||
(sector_t)tmp >= ti->len) {
tmp != (sector_t)tmp) {
ti->error = "Invalid device offset sector";
goto bad;
}
+11 -2
View File
@@ -1979,8 +1979,8 @@ static void integrity_metadata(struct work_struct *w)
if (unlikely(dio->op == REQ_OP_DISCARD)) {
unsigned int bi_size = dio->bio_details.bi_iter.bi_size;
unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : HASH_MAX_DIGESTSIZE;
unsigned int max_blocks = max_size / ic->tag_size;
unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : sizeof(checksums_onstack);
unsigned int max_blocks = (max_size - extra_space) / ic->tag_size;
sector_t sector = dio->range.logical_sector;
if (!ic->discard_keyed)
@@ -3875,6 +3875,10 @@ static void dm_integrity_resume(struct dm_target *ti)
r = sync_rw_sb(ic, REQ_OP_READ);
if (r)
dm_integrity_io_error(ic, "reading superblock", r);
if (ic->mode == 'R')
goto skip_writes;
if ((ic->sb->flags & flags) != flags) {
ic->sb->flags |= flags;
r = sync_rw_sb(ic, REQ_OP_WRITE | REQ_FUA);
@@ -3984,6 +3988,7 @@ static void dm_integrity_resume(struct dm_target *ti)
}
}
skip_writes:
ic->reboot_notifier.notifier_call = dm_integrity_reboot;
ic->reboot_notifier.next = NULL;
ic->reboot_notifier.priority = INT_MAX - 1; /* be notified after md and before hardware drivers */
@@ -4130,6 +4135,10 @@ static void dm_integrity_io_hints(struct dm_target *ti, struct queue_limits *lim
limits->dma_alignment = limits->logical_block_size - 1;
limits->discard_granularity = ic->sectors_per_block << SECTOR_SHIFT;
if (ic->internal_hash &&
(ic->mode == 'D' || ic->mode == 'B' || ic->mode == 'I'))
limits->features |= BLK_FEAT_STABLE_WRITES;
if (!ic->internal_hash) {
struct blk_integrity *bi = &limits->integrity;