Commit ffc1c20c authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull device mapper updates from Mike Snitzer:

 - Add DM writecache "cleaner" policy feature that allows cache to be
   flushed while userspace monitors for completion to then discommision
   use of caching.

 - Optimize DM writecache superblock writing and also yield CPU while
   initializing writecache on large PMEM devices to avoid CPU stalls.

 - Various fixes to DM integrity target while preparing for the ability
   to resize a DM integrity device. In addition to resize support, add
   optional discard support with the "allow_discards" feature.

 - Fix DM clone target's discard handling and overflow bugs which could
   cause data corruption.

 - Fix memory leak in destructor for DM verity FEC support.

 - Fix DM zoned target's redundant increment of nr_rnd_zones.

 - Small cleanup in DM crypt to use crypt_integrity_aead() helper.

* tag 'for-5.7/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm clone metadata: Fix return type of dm_clone_nr_of_hydrated_regions()
  dm clone: Add missing casts to prevent overflows and data corruption
  dm clone: Add overflow check for number of regions
  dm clone: Fix handling of partial region discards
  dm writecache: add cond_resched to avoid CPU hangs
  dm integrity: improve discard in journal mode
  dm integrity: add optional discard support
  dm integrity: allow resize of the integrity device
  dm integrity: factor out get_provided_data_sectors()
  dm integrity: don't replay journal data past the end of the device
  dm integrity: remove sector type casts
  dm integrity: fix a crash with unusually large tag size
  dm zoned: remove duplicate nr_rnd_zones increase in dmz_init_zone()
  dm verity fec: fix memory leak in verity_fec_dtr
  dm writecache: optimize superblock write
  dm writecache: implement gradual cleanup
  dm writecache: implement the "cleaner" policy
  dm writecache: do direct write if the cache is full
  dm integrity: print device name in integrity_metadata() error message
  dm crypt: use crypt_integrity_aead() helper
parents f365ab31 81d5553d
...@@ -656,7 +656,7 @@ bool dm_clone_is_range_hydrated(struct dm_clone_metadata *cmd, ...@@ -656,7 +656,7 @@ bool dm_clone_is_range_hydrated(struct dm_clone_metadata *cmd,
return (bit >= (start + nr_regions)); return (bit >= (start + nr_regions));
} }
unsigned long dm_clone_nr_of_hydrated_regions(struct dm_clone_metadata *cmd) unsigned int dm_clone_nr_of_hydrated_regions(struct dm_clone_metadata *cmd)
{ {
return bitmap_weight(cmd->region_map, cmd->nr_regions); return bitmap_weight(cmd->region_map, cmd->nr_regions);
} }
...@@ -850,6 +850,12 @@ int dm_clone_set_region_hydrated(struct dm_clone_metadata *cmd, unsigned long re ...@@ -850,6 +850,12 @@ int dm_clone_set_region_hydrated(struct dm_clone_metadata *cmd, unsigned long re
struct dirty_map *dmap; struct dirty_map *dmap;
unsigned long word, flags; unsigned long word, flags;
if (unlikely(region_nr >= cmd->nr_regions)) {
DMERR("Region %lu out of range (total number of regions %lu)",
region_nr, cmd->nr_regions);
return -ERANGE;
}
word = region_nr / BITS_PER_LONG; word = region_nr / BITS_PER_LONG;
spin_lock_irqsave(&cmd->bitmap_lock, flags); spin_lock_irqsave(&cmd->bitmap_lock, flags);
...@@ -879,6 +885,13 @@ int dm_clone_cond_set_range(struct dm_clone_metadata *cmd, unsigned long start, ...@@ -879,6 +885,13 @@ int dm_clone_cond_set_range(struct dm_clone_metadata *cmd, unsigned long start,
struct dirty_map *dmap; struct dirty_map *dmap;
unsigned long word, region_nr; unsigned long word, region_nr;
if (unlikely(start >= cmd->nr_regions || (start + nr_regions) < start ||
(start + nr_regions) > cmd->nr_regions)) {
DMERR("Invalid region range: start %lu, nr_regions %lu (total number of regions %lu)",
start, nr_regions, cmd->nr_regions);
return -ERANGE;
}
spin_lock_irq(&cmd->bitmap_lock); spin_lock_irq(&cmd->bitmap_lock);
if (cmd->read_only) { if (cmd->read_only) {
......
...@@ -156,7 +156,7 @@ bool dm_clone_is_range_hydrated(struct dm_clone_metadata *cmd, ...@@ -156,7 +156,7 @@ bool dm_clone_is_range_hydrated(struct dm_clone_metadata *cmd,
/* /*
* Returns the number of hydrated regions. * Returns the number of hydrated regions.
*/ */
unsigned long dm_clone_nr_of_hydrated_regions(struct dm_clone_metadata *cmd); unsigned int dm_clone_nr_of_hydrated_regions(struct dm_clone_metadata *cmd);
/* /*
* Returns the first unhydrated region with region_nr >= @start * Returns the first unhydrated region with region_nr >= @start
......
...@@ -282,7 +282,7 @@ static bool bio_triggers_commit(struct clone *clone, struct bio *bio) ...@@ -282,7 +282,7 @@ static bool bio_triggers_commit(struct clone *clone, struct bio *bio)
/* Get the address of the region in sectors */ /* Get the address of the region in sectors */
static inline sector_t region_to_sector(struct clone *clone, unsigned long region_nr) static inline sector_t region_to_sector(struct clone *clone, unsigned long region_nr)
{ {
return (region_nr << clone->region_shift); return ((sector_t)region_nr << clone->region_shift);
} }
/* Get the region number of the bio */ /* Get the region number of the bio */
...@@ -293,10 +293,17 @@ static inline unsigned long bio_to_region(struct clone *clone, struct bio *bio) ...@@ -293,10 +293,17 @@ static inline unsigned long bio_to_region(struct clone *clone, struct bio *bio)
/* Get the region range covered by the bio */ /* Get the region range covered by the bio */
static void bio_region_range(struct clone *clone, struct bio *bio, static void bio_region_range(struct clone *clone, struct bio *bio,
unsigned long *rs, unsigned long *re) unsigned long *rs, unsigned long *nr_regions)
{ {
unsigned long end;
*rs = dm_sector_div_up(bio->bi_iter.bi_sector, clone->region_size); *rs = dm_sector_div_up(bio->bi_iter.bi_sector, clone->region_size);
*re = bio_end_sector(bio) >> clone->region_shift; end = bio_end_sector(bio) >> clone->region_shift;
if (*rs >= end)
*nr_regions = 0;
else
*nr_regions = end - *rs;
} }
/* Check whether a bio overwrites a region */ /* Check whether a bio overwrites a region */
...@@ -454,7 +461,7 @@ static void trim_bio(struct bio *bio, sector_t sector, unsigned int len) ...@@ -454,7 +461,7 @@ static void trim_bio(struct bio *bio, sector_t sector, unsigned int len)
static void complete_discard_bio(struct clone *clone, struct bio *bio, bool success) static void complete_discard_bio(struct clone *clone, struct bio *bio, bool success)
{ {
unsigned long rs, re; unsigned long rs, nr_regions;
/* /*
* If the destination device supports discards, remap and trim the * If the destination device supports discards, remap and trim the
...@@ -463,9 +470,9 @@ static void complete_discard_bio(struct clone *clone, struct bio *bio, bool succ ...@@ -463,9 +470,9 @@ static void complete_discard_bio(struct clone *clone, struct bio *bio, bool succ
*/ */
if (test_bit(DM_CLONE_DISCARD_PASSDOWN, &clone->flags) && success) { if (test_bit(DM_CLONE_DISCARD_PASSDOWN, &clone->flags) && success) {
remap_to_dest(clone, bio); remap_to_dest(clone, bio);
bio_region_range(clone, bio, &rs, &re); bio_region_range(clone, bio, &rs, &nr_regions);
trim_bio(bio, rs << clone->region_shift, trim_bio(bio, region_to_sector(clone, rs),
(re - rs) << clone->region_shift); nr_regions << clone->region_shift);
generic_make_request(bio); generic_make_request(bio);
} else } else
bio_endio(bio); bio_endio(bio);
...@@ -473,12 +480,21 @@ static void complete_discard_bio(struct clone *clone, struct bio *bio, bool succ ...@@ -473,12 +480,21 @@ static void complete_discard_bio(struct clone *clone, struct bio *bio, bool succ
static void process_discard_bio(struct clone *clone, struct bio *bio) static void process_discard_bio(struct clone *clone, struct bio *bio)
{ {
unsigned long rs, re; unsigned long rs, nr_regions;
bio_region_range(clone, bio, &rs, &re); bio_region_range(clone, bio, &rs, &nr_regions);
BUG_ON(re > clone->nr_regions); if (!nr_regions) {
bio_endio(bio);
return;
}
if (unlikely(rs == re)) { if (WARN_ON(rs >= clone->nr_regions || (rs + nr_regions) < rs ||
(rs + nr_regions) > clone->nr_regions)) {
DMERR("%s: Invalid range (%lu + %lu, total regions %lu) for discard (%llu + %u)",
clone_device_name(clone), rs, nr_regions,
clone->nr_regions,
(unsigned long long)bio->bi_iter.bi_sector,
bio_sectors(bio));
bio_endio(bio); bio_endio(bio);
return; return;
} }
...@@ -487,7 +503,7 @@ static void process_discard_bio(struct clone *clone, struct bio *bio) ...@@ -487,7 +503,7 @@ static void process_discard_bio(struct clone *clone, struct bio *bio)
* The covered regions are already hydrated so we just need to pass * The covered regions are already hydrated so we just need to pass
* down the discard. * down the discard.
*/ */
if (dm_clone_is_range_hydrated(clone->cmd, rs, re - rs)) { if (dm_clone_is_range_hydrated(clone->cmd, rs, nr_regions)) {
complete_discard_bio(clone, bio, true); complete_discard_bio(clone, bio, true);
return; return;
} }
...@@ -788,11 +804,14 @@ static void hydration_copy(struct dm_clone_region_hydration *hd, unsigned int nr ...@@ -788,11 +804,14 @@ static void hydration_copy(struct dm_clone_region_hydration *hd, unsigned int nr
struct dm_io_region from, to; struct dm_io_region from, to;
struct clone *clone = hd->clone; struct clone *clone = hd->clone;
if (WARN_ON(!nr_regions))
return;
region_size = clone->region_size; region_size = clone->region_size;
region_start = hd->region_nr; region_start = hd->region_nr;
region_end = region_start + nr_regions - 1; region_end = region_start + nr_regions - 1;
total_size = (nr_regions - 1) << clone->region_shift; total_size = region_to_sector(clone, nr_regions - 1);
if (region_end == clone->nr_regions - 1) { if (region_end == clone->nr_regions - 1) {
/* /*
...@@ -1169,7 +1188,7 @@ static void process_deferred_discards(struct clone *clone) ...@@ -1169,7 +1188,7 @@ static void process_deferred_discards(struct clone *clone)
int r = -EPERM; int r = -EPERM;
struct bio *bio; struct bio *bio;
struct blk_plug plug; struct blk_plug plug;
unsigned long rs, re; unsigned long rs, nr_regions;
struct bio_list discards = BIO_EMPTY_LIST; struct bio_list discards = BIO_EMPTY_LIST;
spin_lock_irq(&clone->lock); spin_lock_irq(&clone->lock);
...@@ -1185,14 +1204,13 @@ static void process_deferred_discards(struct clone *clone) ...@@ -1185,14 +1204,13 @@ static void process_deferred_discards(struct clone *clone)
/* Update the metadata */ /* Update the metadata */
bio_list_for_each(bio, &discards) { bio_list_for_each(bio, &discards) {
bio_region_range(clone, bio, &rs, &re); bio_region_range(clone, bio, &rs, &nr_regions);
/* /*
* A discard request might cover regions that have been already * A discard request might cover regions that have been already
* hydrated. There is no need to update the metadata for these * hydrated. There is no need to update the metadata for these
* regions. * regions.
*/ */
r = dm_clone_cond_set_range(clone->cmd, rs, re - rs); r = dm_clone_cond_set_range(clone->cmd, rs, nr_regions);
if (unlikely(r)) if (unlikely(r))
break; break;
} }
...@@ -1455,7 +1473,7 @@ static void clone_status(struct dm_target *ti, status_type_t type, ...@@ -1455,7 +1473,7 @@ static void clone_status(struct dm_target *ti, status_type_t type,
goto error; goto error;
} }
DMEMIT("%u %llu/%llu %llu %lu/%lu %u ", DMEMIT("%u %llu/%llu %llu %u/%lu %u ",
DM_CLONE_METADATA_BLOCK_SIZE, DM_CLONE_METADATA_BLOCK_SIZE,
(unsigned long long)(nr_metadata_blocks - nr_free_metadata_blocks), (unsigned long long)(nr_metadata_blocks - nr_free_metadata_blocks),
(unsigned long long)nr_metadata_blocks, (unsigned long long)nr_metadata_blocks,
...@@ -1775,6 +1793,7 @@ static int copy_ctr_args(struct clone *clone, int argc, const char **argv, char ...@@ -1775,6 +1793,7 @@ static int copy_ctr_args(struct clone *clone, int argc, const char **argv, char
static int clone_ctr(struct dm_target *ti, unsigned int argc, char **argv) static int clone_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{ {
int r; int r;
sector_t nr_regions;
struct clone *clone; struct clone *clone;
struct dm_arg_set as; struct dm_arg_set as;
...@@ -1816,7 +1835,16 @@ static int clone_ctr(struct dm_target *ti, unsigned int argc, char **argv) ...@@ -1816,7 +1835,16 @@ static int clone_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto out_with_source_dev; goto out_with_source_dev;
clone->region_shift = __ffs(clone->region_size); clone->region_shift = __ffs(clone->region_size);
clone->nr_regions = dm_sector_div_up(ti->len, clone->region_size); nr_regions = dm_sector_div_up(ti->len, clone->region_size);
/* Check for overflow */
if (nr_regions != (unsigned long)nr_regions) {
ti->error = "Too many regions. Consider increasing the region size";
r = -EOVERFLOW;
goto out_with_source_dev;
}
clone->nr_regions = nr_regions;
r = validate_nr_regions(clone->nr_regions, &ti->error); r = validate_nr_regions(clone->nr_regions, &ti->error);
if (r) if (r)
......
...@@ -230,6 +230,8 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io); ...@@ -230,6 +230,8 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io);
static struct scatterlist *crypt_get_sg_data(struct crypt_config *cc, static struct scatterlist *crypt_get_sg_data(struct crypt_config *cc,
struct scatterlist *sg); struct scatterlist *sg);
static bool crypt_integrity_aead(struct crypt_config *cc);
/* /*
* Use this to access cipher attributes that are independent of the key. * Use this to access cipher attributes that are independent of the key.
*/ */
...@@ -346,7 +348,7 @@ static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti, ...@@ -346,7 +348,7 @@ static int crypt_iv_benbi_ctr(struct crypt_config *cc, struct dm_target *ti,
unsigned bs; unsigned bs;
int log; int log;
if (test_bit(CRYPT_MODE_INTEGRITY_AEAD, &cc->cipher_flags)) if (crypt_integrity_aead(cc))
bs = crypto_aead_blocksize(any_tfm_aead(cc)); bs = crypto_aead_blocksize(any_tfm_aead(cc));
else else
bs = crypto_skcipher_blocksize(any_tfm(cc)); bs = crypto_skcipher_blocksize(any_tfm(cc));
...@@ -712,7 +714,7 @@ static int crypt_iv_random_gen(struct crypt_config *cc, u8 *iv, ...@@ -712,7 +714,7 @@ static int crypt_iv_random_gen(struct crypt_config *cc, u8 *iv,
static int crypt_iv_eboiv_ctr(struct crypt_config *cc, struct dm_target *ti, static int crypt_iv_eboiv_ctr(struct crypt_config *cc, struct dm_target *ti,
const char *opts) const char *opts)
{ {
if (test_bit(CRYPT_MODE_INTEGRITY_AEAD, &cc->cipher_flags)) { if (crypt_integrity_aead(cc)) {
ti->error = "AEAD transforms not supported for EBOIV"; ti->error = "AEAD transforms not supported for EBOIV";
return -EINVAL; return -EINVAL;
} }
......
This diff is collapsed.
...@@ -551,6 +551,7 @@ void verity_fec_dtr(struct dm_verity *v) ...@@ -551,6 +551,7 @@ void verity_fec_dtr(struct dm_verity *v)
mempool_exit(&f->rs_pool); mempool_exit(&f->rs_pool);
mempool_exit(&f->prealloc_pool); mempool_exit(&f->prealloc_pool);
mempool_exit(&f->extra_pool); mempool_exit(&f->extra_pool);
mempool_exit(&f->output_pool);
kmem_cache_destroy(f->cache); kmem_cache_destroy(f->cache);
if (f->data_bufio) if (f->data_bufio)
......
This diff is collapsed.
...@@ -1109,7 +1109,6 @@ static int dmz_init_zone(struct blk_zone *blkz, unsigned int idx, void *data) ...@@ -1109,7 +1109,6 @@ static int dmz_init_zone(struct blk_zone *blkz, unsigned int idx, void *data)
switch (blkz->type) { switch (blkz->type) {
case BLK_ZONE_TYPE_CONVENTIONAL: case BLK_ZONE_TYPE_CONVENTIONAL:
set_bit(DMZ_RND, &zone->flags); set_bit(DMZ_RND, &zone->flags);
zmd->nr_rnd_zones++;
break; break;
case BLK_ZONE_TYPE_SEQWRITE_REQ: case BLK_ZONE_TYPE_SEQWRITE_REQ:
case BLK_ZONE_TYPE_SEQWRITE_PREF: case BLK_ZONE_TYPE_SEQWRITE_PREF:
......
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