diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 12:35:15 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 12:35:15 -0700 |
| commit | 7fa7d4c6038bb4b394478ba1b31a6e8d89ed4f60 (patch) | |
| tree | ecc0fbcf2d4190389535bf37058ba09d17b3dcdb | |
| parent | faabe2db712e8310dbe38fb81d33e5313ff0f059 (diff) | |
| parent | 39c5aa3bd8ec3912d2cd0b3fe092642b0d2b0713 (diff) | |
Merge tag 'for-7.3/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper updates from Mikulas Patocka:
- minor cleanups found by Claude Opus 4.6
- small cleanups in dm core, dm-cache, dm-switch, dm-inlinecrypt,
dm-vdo
- improve validation of metadata in dm-pcache
- fix resume-vs-remove ioctl race condition
- fix race condition when issuing table load ioctls concurrently
- fix dm-raid1 and dm-io, so that they work with unaligned bio vectors
- dm-integrity: use keyed markers as discard fillers
- improve metadata validation in dm-array
- fix dm-stats crash on memory allocation failure
- fix dm-dust, so that it works if it is not the first target in a
table
- dm-era: fix superblock refcount leak on snapshot failure
* tag 'for-7.3/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: (46 commits)
dm-era: fix shadowed superblock leak on take-snap failure
dm dust: make badblock messages target-relative
dm-stats: fix a crash if allocation of per-cpu data fails
dm array: reject an array block whose value size is not the caller's
dm array: validate array block headers on read
dm-integrity: replace forgeable discard filler with a keyed sector marker
dm vdo indexer: embed geometry in parent structures
dm vdo indexer: simplify sub-index parameter calculations
dm-pcache: remove unused 'cache' parameter from cache_key_gc()
docs: device-mapper: dm-inlinecrypt: fix 'bellow' spelling
dm-pcache: remove unused miss_read_end_work_fn declaration
dm-io: report non-retryable errors separatedly
dm-io: clone the source bio instead of copying its biovec
dm: fix race when loading and unloading a table
dm: fix resume-vs-remove race
dm-pcache: remove unused 'allocated' variable in cache_data_alloc()
dm-pcache: replace tabs with spaces in comments to fix ASCII diagram alignment
dm-pcache: fix use-after-free and invalid seg operations in kset_replay()
dm-pcache: fix implicit u8 truncation of gc_percent in message handler
dm raid1: reserve space for NUL-terminator in build_constructor_string()
...
48 files changed, 928 insertions, 611 deletions
diff --git a/Documentation/admin-guide/device-mapper/dm-ima.rst b/Documentation/admin-guide/device-mapper/dm-ima.rst index a4aa50a828e0..2a3b50ffbee4 100644 --- a/Documentation/admin-guide/device-mapper/dm-ima.rst +++ b/Documentation/admin-guide/device-mapper/dm-ima.rst @@ -424,7 +424,8 @@ section above) has the following data format for 'integrity' target. target_attributes := <target_name> "," <target_version> "," <dev_name> "," <start> <tag_size> "," <mode> "," [<meta_device> ","] [<block_size> ","] <recalculate> "," - <allow_discards> "," <fix_padding> "," <fix_hmac> "," <legacy_recalculate> "," + <allow_discards> "," <allow_discards_keyed> "," <fix_padding> "," <fix_hmac> "," + <legacy_recalculate> "," <journal_sectors> "," <interleave_sectors> "," <buffer_sectors> ";" target_name := "target_name=integrity" @@ -438,6 +439,7 @@ section above) has the following data format for 'integrity' target. block_size := "block_size=" <N> recalculate := "recalculate=" <yes_no> allow_discards := "allow_discards=" <yes_no> + allow_discards_keyed := "allow_discards_keyed=" <yes_no> fix_padding := "fix_padding=" <yes_no> fix_hmac := "fix_hmac=" <yes_no> legacy_recalculate := "legacy_recalculate=" <yes_no> @@ -455,7 +457,8 @@ section above) has the following data format for 'integrity' target. dm_version=4.45.0; name=integrity1,uuid=,major=253,minor=1,minor_count=1,num_targets=1; target_index=0,target_begin=0,target_len=7856,target_name=integrity,target_version=1.10.0, - dev_name=253:0,start=0,tag_size=32,mode=J,recalculate=n,allow_discards=n,fix_padding=n, + dev_name=253:0,start=0,tag_size=32,mode=J,recalculate=n,allow_discards=n, + allow_discards_keyed=n,fix_padding=n, fix_hmac=n,legacy_recalculate=n,journal_sectors=88,interleave_sectors=32768,buffer_sectors=128; diff --git a/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst b/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst index 76b3aae21eb4..8258a72f00a8 100644 --- a/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst +++ b/Documentation/admin-guide/device-mapper/dm-inlinecrypt.rst @@ -33,7 +33,7 @@ Parameters:: or it can be passed as <key_string> prefixed with single colon character (':') for keys residing in kernel keyring service. You can only use key sizes that are valid for the selected cipher. - Note that the size in bytes of a valid key must be in bellow range. + Note that the size in bytes of a valid key must be in below range. [BLK_CRYPTO_KEY_TYPE_RAW, BLK_CRYPTO_KEY_TYPE_HW_WRAPPED] diff --git a/Documentation/admin-guide/device-mapper/dm-integrity.rst b/Documentation/admin-guide/device-mapper/dm-integrity.rst index c2e18ecc065c..9c21301423c9 100644 --- a/Documentation/admin-guide/device-mapper/dm-integrity.rst +++ b/Documentation/admin-guide/device-mapper/dm-integrity.rst @@ -190,6 +190,19 @@ allow_discards Allow block discard requests (a.k.a. TRIM) for the integrity device. Discards are only allowed to devices using internal hash. + A discarded block is marked with a constant filler tag that anyone + with raw write access to the backing device can forge without the + key. Use allow_discards_keyed instead on new volumes. + +allow_discards_keyed + Like allow_discards, but marks a discarded block with a keyed + checksum of the sector number, HMAC_key(salt || sector), instead of + the constant filler tag, so it can't be forged without the + integrity key. + + Not compatible with volumes that already have discarded blocks + marked the old way; only use on a freshly formatted volume. + fix_padding Use a smaller padding of the tag area that is more space-efficient. If this option is not present, large padding is diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index a458b9fd2fcd..d58eed96a8ff 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -1287,11 +1287,11 @@ static void free_buffer(struct dm_buffer *b) * dm-io completion routine. It just calls b->bio.bi_end_io, pretending * that the request was handled directly with bio interface. */ -static void dmio_complete(unsigned long error, void *context) +static void dmio_complete(unsigned long error, unsigned long unsup, void *context) { struct dm_buffer *b = context; - b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : 0); + b->end_io(b, unlikely(error != 0) ? BLK_STS_IOERR : unlikely(unsup != 0) ? BLK_STS_NOTSUPP : 0); } static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector, @@ -1319,7 +1319,7 @@ static void use_dmio(struct dm_buffer *b, enum req_op op, sector_t sector, io_req.mem.ptr.vma = (char *)b->data + offset; } - r = dm_io(&io_req, 1, ®ion, NULL, ioprio); + r = dm_io(&io_req, 1, ®ion, NULL, NULL, ioprio); if (unlikely(r)) b->end_io(b, errno_to_blk_status(r)); } @@ -2220,7 +2220,7 @@ int dm_bufio_issue_flush(struct dm_bufio_client *c) if (WARN_ON_ONCE(dm_bufio_in_request())) return -EINVAL; - return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT); + return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT); } EXPORT_SYMBOL_GPL(dm_bufio_issue_flush); @@ -2246,7 +2246,7 @@ int dm_bufio_issue_discard(struct dm_bufio_client *c, sector_t block, sector_t c if (WARN_ON_ONCE(dm_bufio_in_request())) return -EINVAL; /* discards are optional */ - return dm_io(&io_req, 1, &io_reg, NULL, IOPRIO_DEFAULT); + return dm_io(&io_req, 1, &io_reg, NULL, NULL, IOPRIO_DEFAULT); } EXPORT_SYMBOL_GPL(dm_bufio_issue_discard); diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 097315a9bf0f..33dbc71b730f 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -16,6 +16,7 @@ #include <linux/dm-kcopyd.h> #include <linux/jiffies.h> #include <linux/init.h> +#include <linux/kstrtox.h> #include <linux/mempool.h> #include <linux/module.h> #include <linux/rwsem.h> @@ -3311,42 +3312,46 @@ struct cblock_range { dm_cblock_t end; }; +static inline dm_cblock_t cblock_succ(dm_cblock_t b) +{ + return to_cblock(from_cblock(b) + 1); +} + /* * A cache block range can take two forms: * * i) A single cblock, eg. '3456' * ii) A begin and end cblock with a dash between, eg. 123-234 */ -static int parse_cblock_range(struct cache *cache, const char *str, +static int parse_cblock_range(struct cache *cache, char *str, struct cblock_range *result) { - char dummy; - uint64_t b, e; + char *blocknr = strsep(&str, "-"); + unsigned int b, e; int r; - /* - * Try and parse form (ii) first. - */ - r = sscanf(str, "%llu-%llu%c", &b, &e, &dummy); + r = kstrtouint(blocknr, 10, &b); + if (r) + goto bad; - if (r == 2) { - result->begin = to_cblock(b); - result->end = to_cblock(e); - return 0; - } + result->begin = to_cblock(b); - /* - * That didn't work, try form (i). - */ - r = sscanf(str, "%llu%c", &b, &dummy); + if (str) { + blocknr = str; - if (r == 1) { - result->begin = to_cblock(b); - result->end = to_cblock(from_cblock(result->begin) + 1u); - return 0; + r = kstrtouint(blocknr, 10, &e); + if (r) + goto bad; + + result->end = to_cblock(e); + } else { + result->end = cblock_succ(result->begin); } - DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), str); + return 0; + +bad: + DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), blocknr); return -EINVAL; } @@ -3377,11 +3382,6 @@ static int validate_cblock_range(struct cache *cache, struct cblock_range *range return 0; } -static inline dm_cblock_t cblock_succ(dm_cblock_t b) -{ - return to_cblock(from_cblock(b) + 1); -} - static int request_invalidation(struct cache *cache, struct cblock_range *range) { int r = 0; @@ -3405,7 +3405,7 @@ static int request_invalidation(struct cache *cache, struct cblock_range *range) } static int process_invalidate_cblocks_message(struct cache *cache, unsigned int count, - const char **cblock_ranges) + char **cblock_ranges) { int r = 0; unsigned int i; @@ -3460,7 +3460,7 @@ static int cache_message(struct dm_target *ti, unsigned int argc, char **argv, } if (!strcasecmp(argv[0], "invalidate_cblocks")) - return process_invalidate_cblocks_message(cache, argc - 1, (const char **) argv + 1); + return process_invalidate_cblocks_message(cache, argc - 1, argv + 1); if (argc != 2) return -EINVAL; diff --git a/drivers/md/dm-dust.c b/drivers/md/dm-dust.c index c7e3077fb1f5..954f4ec5a51c 100644 --- a/drivers/md/dm-dust.c +++ b/drivers/md/dm-dust.c @@ -196,7 +196,6 @@ static int __dust_map_write(struct dust_device *dd, sector_t thisblock) dd->badblock_count--; kfree(bblk); if (!dd->quiet_mode) { - sector_div(thisblock, dd->sect_per_block); DMINFO("block %llu removed from badblocklist by write", (unsigned long long)thisblock); } @@ -224,15 +223,16 @@ static int dust_map_write(struct dust_device *dd, sector_t thisblock, static int dust_map(struct dm_target *ti, struct bio *bio) { struct dust_device *dd = ti->private; + sector_t dust_sector = dm_target_offset(ti, bio->bi_iter.bi_sector); int r; bio_set_dev(bio, dd->dev->bdev); - bio->bi_iter.bi_sector = dd->start + dm_target_offset(ti, bio->bi_iter.bi_sector); + bio->bi_iter.bi_sector = dd->start + dust_sector; if (bio_data_dir(bio) == READ) - r = dust_map_read(dd, bio->bi_iter.bi_sector, dd->fail_read_on_bb); + r = dust_map_read(dd, dust_sector, dd->fail_read_on_bb); else - r = dust_map_write(dd, bio->bi_iter.bi_sector, dd->fail_read_on_bb); + r = dust_map_write(dd, dust_sector, dd->fail_read_on_bb); return r; } @@ -415,7 +415,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, char *result, unsigned int maxlen) { struct dust_device *dd = ti->private; - sector_t size = bdev_nr_sectors(dd->dev->bdev); + sector_t size = dm_sector_div_up(ti->len, dd->sect_per_block); bool invalid_msg = false; int r = -EINVAL; unsigned long long tmp, block; @@ -462,8 +462,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, return r; block = tmp; - sector_div(size, dd->sect_per_block); - if (block > size) { + if (block >= size) { DMERR("selected block value out of range"); return r; } @@ -490,8 +489,7 @@ static int dust_message(struct dm_target *ti, unsigned int argc, char **argv, return r; } wr_fail_cnt = tmp_ui; - sector_div(size, dd->sect_per_block); - if (block > size) { + if (block >= size) { DMERR("selected block value out of range"); return r; } diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 7fe4d19ade4f..ea499adca4ce 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -1034,6 +1034,7 @@ static int metadata_checkpoint(struct era_metadata *md) static int metadata_take_snap(struct era_metadata *md) { int r, inc; + dm_block_t location; struct dm_block *clone; if (md->metadata_snap != SUPERBLOCK_LOCATION) { @@ -1071,7 +1072,9 @@ static int metadata_take_snap(struct era_metadata *md) r = dm_sm_inc_block(md->sm, md->writeset_tree_root); if (r) { DMERR("%s: couldn't inc writeset tree root", __func__); + location = dm_block_location(clone); dm_tm_unlock(md->tm, clone); + dm_sm_dec_block(md->sm, location); return r; } @@ -1079,7 +1082,9 @@ static int metadata_take_snap(struct era_metadata *md) if (r) { DMERR("%s: couldn't inc era tree root", __func__); dm_sm_dec_block(md->sm, md->writeset_tree_root); + location = dm_block_location(clone); dm_tm_unlock(md->tm, clone); + dm_sm_dec_block(md->sm, location); return r; } diff --git a/drivers/md/dm-inlinecrypt.c b/drivers/md/dm-inlinecrypt.c index f50970db0f94..66fa2f9d1fdc 100644 --- a/drivers/md/dm-inlinecrypt.c +++ b/drivers/md/dm-inlinecrypt.c @@ -347,7 +347,6 @@ static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) err = get_key_size(&argv[1]); if (err < 0) { ti->error = "Cannot parse key size"; - err = -EINVAL; goto bad; } ctx->key_size = err; diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 1f2593f113f6..c50feaa98bf9 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -66,6 +66,7 @@ #define SB_VERSION_4 4 #define SB_VERSION_5 5 #define SB_VERSION_6 6 +#define SB_VERSION_7 7 #define SB_SECTORS 8 #define MAX_SECTORS_PER_BLOCK 8 @@ -91,6 +92,7 @@ struct superblock { #define SB_FLAG_FIXED_PADDING 0x8 #define SB_FLAG_FIXED_HMAC 0x10 #define SB_FLAG_INLINE 0x20 +#define SB_FLAG_DISCARD_KEYED 0x40 #define JOURNAL_ENTRY_ROUNDUP 8 @@ -277,6 +279,7 @@ struct dm_integrity_c { bool recalculate_flag; bool reset_recalculate_flag; bool discard; + bool discard_keyed; bool fix_padding; bool fix_hmac; bool legacy_recalculate; @@ -483,7 +486,9 @@ static void wraparound_section(struct dm_integrity_c *ic, unsigned int *sec_ptr) static void sb_set_version(struct dm_integrity_c *ic) { - if (ic->sb->flags & cpu_to_le32(SB_FLAG_INLINE)) + if (ic->sb->flags & cpu_to_le32(SB_FLAG_DISCARD_KEYED)) + ic->sb->version = SB_VERSION_7; + else if (ic->sb->flags & cpu_to_le32(SB_FLAG_INLINE)) ic->sb->version = SB_VERSION_6; else if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) ic->sb->version = SB_VERSION_5; @@ -562,7 +567,7 @@ static int sync_rw_sb(struct dm_integrity_c *ic, blk_opf_t opf) } } - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) return r; @@ -1035,12 +1040,14 @@ static void encrypt_journal(struct dm_integrity_c *ic, bool encrypt, unsigned in return crypt_journal(ic, encrypt, section, n_sections, comp); } -static void complete_journal_io(unsigned long error, void *context) +static void complete_journal_io(unsigned long error, unsigned long unsup, void *context) { struct journal_completion *comp = context; if (unlikely(error != 0)) dm_integrity_io_error(comp->ic, "writing journal", -EIO); + else if (unlikely(unsup != 0)) + dm_integrity_io_error(comp->ic, "writing journal", -EOPNOTSUPP); complete_journal_op(comp); } @@ -1055,7 +1062,7 @@ static void rw_journal_sectors(struct dm_integrity_c *ic, blk_opf_t opf, if (unlikely(dm_integrity_failed(ic))) { if (comp) - complete_journal_io(-1UL, comp); + complete_journal_io(-1UL, -1UL, comp); return; } @@ -1080,13 +1087,13 @@ static void rw_journal_sectors(struct dm_integrity_c *ic, blk_opf_t opf, io_loc.sector = ic->start + SB_SECTORS + sector; io_loc.count = n_sectors; - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { dm_integrity_io_error(ic, (opf & REQ_OP_MASK) == REQ_OP_READ ? "reading journal" : "writing journal", r); if (comp) { WARN_ONCE(1, "asynchronous dm_io failed: %d", r); - complete_journal_io(-1UL, comp); + complete_journal_io(-1UL, -1UL, comp); } } } @@ -1177,7 +1184,7 @@ static void copy_from_journal(struct dm_integrity_c *ic, unsigned int section, u BUG_ON((target | n_sectors | offset) & (unsigned int)(ic->sectors_per_block - 1)); if (unlikely(dm_integrity_failed(ic))) { - fn(-1UL, data); + fn(-1UL, -1UL, data); return; } @@ -1197,10 +1204,10 @@ static void copy_from_journal(struct dm_integrity_c *ic, unsigned int section, u io_loc.sector = target; io_loc.count = n_sectors; - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { WARN_ONCE(1, "asynchronous dm_io failed: %d", r); - fn(-1UL, data); + fn(-1UL, -1UL, data); } } @@ -1414,7 +1421,7 @@ static int dm_integrity_rw_tag(struct dm_integrity_c *ic, unsigned char *tag, se { unsigned int hash_offset = 0; unsigned char mismatch_hash = 0; - unsigned char mismatch_filler = !ic->discard; + unsigned char mismatch_filler = !ic->discard || ic->discard_keyed; do { unsigned char *data, *dp; @@ -1466,7 +1473,7 @@ thorough_test: } hash_offset = 0; mismatch_hash = 0; - mismatch_filler = !ic->discard; + mismatch_filler = !ic->discard || ic->discard_keyed; } } } @@ -1493,12 +1500,14 @@ struct flush_request { struct completion comp; }; -static void flush_notify(unsigned long error, void *fr_) +static void flush_notify(unsigned long error, unsigned long unsup, void *fr_) { struct flush_request *fr = fr_; if (unlikely(error != 0)) dm_integrity_io_error(fr->ic, "flushing disk cache", -EIO); + else if (unlikely(unsup != 0)) + dm_integrity_io_error(fr->ic, "flushing disk cache", -EOPNOTSUPP); complete(&fr->comp); } @@ -1521,7 +1530,7 @@ static void dm_integrity_flush_buffers(struct dm_integrity_c *ic, bool flush_dat fr.io_reg.count = 0; fr.ic = ic; init_completion(&fr.comp); - r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, IOPRIO_DEFAULT); + r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL, NULL, IOPRIO_DEFAULT); BUG_ON(r); } @@ -1642,7 +1651,8 @@ static void integrity_end_io(struct bio *bio) } static void integrity_sector_checksum_shash(struct dm_integrity_c *ic, sector_t sector, - const char *data, unsigned offset, char *result) + const char *data, unsigned offset, + unsigned int len, char *result) { __le64 sector_le = cpu_to_le64(sector); SHASH_DESC_ON_STACK(req, ic->internal_shash); @@ -1671,10 +1681,12 @@ static void integrity_sector_checksum_shash(struct dm_integrity_c *ic, sector_t goto failed; } - r = crypto_shash_update(req, data + offset, ic->sectors_per_block << SECTOR_SHIFT); - if (unlikely(r < 0)) { - dm_integrity_io_error(ic, "crypto_shash_update", r); - goto failed; + if (likely(len)) { + r = crypto_shash_update(req, data + offset, len); + if (unlikely(r < 0)) { + dm_integrity_io_error(ic, "crypto_shash_update", r); + goto failed; + } } r = crypto_shash_final(req, result); @@ -1695,7 +1707,8 @@ failed: } static void integrity_sector_checksum_ahash(struct dm_integrity_c *ic, struct ahash_request **ahash_req, - sector_t sector, struct page *page, unsigned offset, char *result) + sector_t sector, struct page *page, unsigned offset, + unsigned int len, char *result) { __le64 sector_le = cpu_to_le64(sector); struct ahash_request *req; @@ -1704,6 +1717,7 @@ static void integrity_sector_checksum_ahash(struct dm_integrity_c *ic, struct ah int r; unsigned int digest_size; unsigned int nbytes = 0; + unsigned int nents = 1 + (len ? 1 : 0); might_sleep(); @@ -1717,12 +1731,12 @@ static void integrity_sector_checksum_ahash(struct dm_integrity_c *ic, struct ah ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, crypto_req_done, &wait); if (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_HMAC)) { - sg_init_table(sg, 3); + sg_init_table(sg, nents + 1); sg_set_buf(s, (const __u8 *)&ic->sb->salt, SALT_SIZE); nbytes += SALT_SIZE; s++; } else { - sg_init_table(sg, 2); + sg_init_table(sg, nents); } if (likely(!is_vmalloc_addr(§or_le))) { @@ -1735,8 +1749,10 @@ static void integrity_sector_checksum_ahash(struct dm_integrity_c *ic, struct ah nbytes += sizeof(sector_le); s++; - sg_set_page(s, page, ic->sectors_per_block << SECTOR_SHIFT, offset); - nbytes += ic->sectors_per_block << SECTOR_SHIFT; + if (likely(len)) { + sg_set_page(s, page, len, offset); + nbytes += len; + } ahash_request_set_crypt(req, sg, result, nbytes); @@ -1760,10 +1776,40 @@ failed: static void integrity_sector_checksum(struct dm_integrity_c *ic, struct ahash_request **ahash_req, sector_t sector, const char *data, unsigned offset, char *result) { + unsigned int len = ic->sectors_per_block << SECTOR_SHIFT; + if (likely(ic->internal_shash != NULL)) - integrity_sector_checksum_shash(ic, sector, data, offset, result); + integrity_sector_checksum_shash(ic, sector, data, offset, len, result); else - integrity_sector_checksum_ahash(ic, ahash_req, sector, (struct page *)data, offset, result); + integrity_sector_checksum_ahash(ic, ahash_req, sector, (struct page *)data, + offset, len, result); +} + +/* + * Authenticated marker for a discarded block: HMAC_key(salt || sector), with + * no data payload. Because a real data tag's input always covers a full + * block, its length differs from this marker's, so the two can never + * collide structurally, regardless of block content. + */ +static void integrity_discard_checksum(struct dm_integrity_c *ic, struct ahash_request **ahash_req, + sector_t sector, char *result) +{ + if (likely(ic->internal_shash != NULL)) + integrity_sector_checksum_shash(ic, sector, NULL, 0, 0, result); + else + integrity_sector_checksum_ahash(ic, ahash_req, sector, NULL, 0, 0, result); +} + +static void integrity_discard_fill_tags(struct dm_integrity_c *ic, struct ahash_request **ahash_req, + unsigned char *checksums, sector_t *sector, + unsigned int blocks) +{ + unsigned int i; + + for (i = 0; i < blocks; i++) { + integrity_discard_checksum(ic, ahash_req, *sector, checksums + i * ic->tag_size); + *sector += ic->sectors_per_block; + } } static void *integrity_kmap(struct dm_integrity_c *ic, struct page *p) @@ -1792,6 +1838,29 @@ static void *integrity_identity(struct dm_integrity_c *ic, void *data) return virt_to_page(data); } +static int integrity_recheck_verify_tag(struct dm_integrity_io *dio, char *checksum, + char *on_disk_tag, sector_t logical_sector) +{ + struct dm_integrity_c *ic = dio->ic; + int r; + + if (!ic->discard_keyed) + return dm_integrity_rw_tag(ic, checksum, &dio->metadata_block, + &dio->metadata_offset, ic->tag_size, TAG_CMP); + + r = dm_integrity_rw_tag(ic, on_disk_tag, &dio->metadata_block, + &dio->metadata_offset, ic->tag_size, TAG_READ); + if (unlikely(r)) + return r; + + r = crypto_memneq(on_disk_tag, checksum, ic->tag_size); + if (unlikely(r)) { + integrity_discard_checksum(ic, &dio->ahash_req, logical_sector, checksum); + r = crypto_memneq(on_disk_tag, checksum, ic->tag_size); + } + return r; +} + static noinline void integrity_recheck(struct dm_integrity_io *dio, char *checksum) { struct bio *bio = dm_bio_from_per_bio_data(dio, sizeof(struct dm_integrity_io)); @@ -1817,6 +1886,7 @@ static noinline void integrity_recheck(struct dm_integrity_io *dio, char *checks char *mem; char *buffer = page_to_virt(page); unsigned int buffer_offset; + char on_disk_tag[MAX_T(size_t, HASH_MAX_DIGESTSIZE, MAX_TAG_SIZE)]; int r; struct dm_io_request io_req; struct dm_io_region io_loc; @@ -1837,15 +1907,15 @@ static noinline void integrity_recheck(struct dm_integrity_io *dio, char *checks buffer_offset = (sector - io_loc.sector) << SECTOR_SHIFT; io_loc.count = round_up(io_loc.count, alignment); - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { dio->bi_status = errno_to_blk_status(r); goto free_ret; } integrity_sector_checksum(ic, &dio->ahash_req, logical_sector, integrity_identity(ic, buffer), buffer_offset, checksum); - r = dm_integrity_rw_tag(ic, checksum, &dio->metadata_block, - &dio->metadata_offset, ic->tag_size, TAG_CMP); + r = integrity_recheck_verify_tag(dio, checksum, on_disk_tag, + logical_sector); if (r) { if (r > 0) { DMERR_LIMIT("%pg: Checksum failed at sector 0x%llx", @@ -1911,13 +1981,18 @@ static void integrity_metadata(struct work_struct *w) 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; + sector_t sector = dio->range.logical_sector; - memset(checksums, DISCARD_FILLER, max_size); + if (!ic->discard_keyed) + memset(checksums, DISCARD_FILLER, max_size); while (bi_size) { unsigned int this_step_blocks = bi_size >> (SECTOR_SHIFT + ic->sb->log2_sectors_per_block); this_step_blocks = min(this_step_blocks, max_blocks); + if (ic->discard_keyed) + integrity_discard_fill_tags(ic, &dio->ahash_req, checksums, + §or, this_step_blocks); r = dm_integrity_rw_tag(ic, checksums, &dio->metadata_block, &dio->metadata_offset, this_step_blocks * ic->tag_size, TAG_WRITE); if (unlikely(r)) { @@ -2782,7 +2857,8 @@ static void integrity_bio_wait(struct work_struct *w) switch (r) { case DM_MAPIO_KILL: bio->bi_status = BLK_STS_IOERR; - fallthrough; + bio_endio(bio); + return; case DM_MAPIO_REMAPPED: submit_bio_noacct(bio); fallthrough; @@ -2890,7 +2966,7 @@ release_flush_bios: } } -static void complete_copy_from_journal(unsigned long error, void *context) +static void complete_copy_from_journal(unsigned long error, unsigned long unsup, void *context) { struct journal_io *io = context; struct journal_completion *comp = io->comp; @@ -2900,6 +2976,8 @@ static void complete_copy_from_journal(unsigned long error, void *context) mempool_free(io, &ic->journal_io_mempool); if (unlikely(error != 0)) dm_integrity_io_error(ic, "copying from journal", -EIO); + else if (unlikely(unsup != 0)) + dm_integrity_io_error(ic, "copying from journal", -EOPNOTSUPP); complete_journal_op(comp); } @@ -3040,7 +3118,7 @@ static void do_journal_write(struct dm_integrity_c *ic, unsigned int write_start r = dm_integrity_rw_tag(ic, journal_entry_tag(ic, je2), &metadata_block, &metadata_offset, ic->tag_size, TAG_WRITE); if (unlikely(r)) - dm_integrity_io_error(ic, "reading tags", r); + dm_integrity_io_error(ic, "writing tags", r); } atomic_inc(&comp.in_flight); @@ -3215,7 +3293,7 @@ next_chunk: io_loc.sector = get_data_sector(ic, area, offset); io_loc.count = n_sectors; - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) { dm_integrity_io_error(ic, "reading data", r); goto err; @@ -3792,6 +3870,8 @@ static void dm_integrity_resume(struct dm_target *ti) ic->wrote_to_journal = false; flags = ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING); + if (ic->discard_keyed) + flags |= cpu_to_le32(SB_FLAG_DISCARD_KEYED); r = sync_rw_sb(ic, REQ_OP_READ); if (r) dm_integrity_io_error(ic, "reading superblock", r); @@ -3939,7 +4019,8 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type, arg_count += ic->sectors_per_block != 1; arg_count += !!(ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING)); arg_count += ic->reset_recalculate_flag; - arg_count += ic->discard; + arg_count += ic->discard && !ic->discard_keyed; + arg_count += ic->discard_keyed; arg_count += ic->mode != 'I'; /* interleave_sectors */ arg_count += ic->mode == 'J'; /* journal_sectors */ arg_count += ic->mode == 'J'; /* journal_watermark */ @@ -3962,8 +4043,10 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type, DMEMIT(" recalculate"); if (ic->reset_recalculate_flag) DMEMIT(" reset_recalculate"); - if (ic->discard) + if (ic->discard && !ic->discard_keyed) DMEMIT(" allow_discards"); + if (ic->discard_keyed) + DMEMIT(" allow_discards_keyed"); if (ic->mode != 'I') DMEMIT(" interleave_sectors:%u", 1U << ic->sb->log2_interleave_sectors); DMEMIT(" buffer_sectors:%u", 1U << ic->log2_buffer_sectors); @@ -4013,6 +4096,7 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type, DMEMIT(",recalculate=%c", (ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING)) ? 'y' : 'n'); DMEMIT(",allow_discards=%c", ic->discard ? 'y' : 'n'); + DMEMIT(",allow_discards_keyed=%c", ic->discard_keyed ? 'y' : 'n'); DMEMIT(",fix_padding=%c", ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0) ? 'y' : 'n'); DMEMIT(",fix_hmac=%c", @@ -4170,6 +4254,9 @@ static int initialize_superblock(struct dm_integrity_c *ic, get_random_bytes(ic->sb->salt, SALT_SIZE); } + if (ic->discard_keyed) + ic->sb->flags |= cpu_to_le32(SB_FLAG_DISCARD_KEYED); + if (!ic->meta_dev) { if (ic->fix_padding) ic->sb->flags |= cpu_to_le32(SB_FLAG_FIXED_PADDING); @@ -4634,6 +4721,7 @@ retest_commit_id: if (!ic->journal_tree) { *error = "Could not allocate memory for journal tree"; r = -ENOMEM; + goto bad; } bad: kfree(crypt_data); @@ -4827,6 +4915,9 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv ic->reset_recalculate_flag = true; } else if (!strcmp(opt_string, "allow_discards")) { ic->discard = true; + } else if (!strcmp(opt_string, "allow_discards_keyed")) { + ic->discard = true; + ic->discard_keyed = true; } else if (!strcmp(opt_string, "fix_padding")) { ic->fix_padding = true; } else if (!strcmp(opt_string, "fix_hmac")) { @@ -4955,6 +5046,11 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv ti->error = "Discard can be only used with internal hash"; goto bad; } + if (ic->discard_keyed && !ic->internal_hash_alg.key) { + r = -EINVAL; + ti->error = "Keyed discard can only be used with keyed internal hash"; + goto bad; + } ic->autocommit_jiffies = msecs_to_jiffies(sync_msec); ic->autocommit_msec = sync_msec; @@ -5073,7 +5169,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv should_write_sb = true; } - if (!ic->sb->version || ic->sb->version > SB_VERSION_6) { + if (!ic->sb->version || ic->sb->version > SB_VERSION_7) { r = -EINVAL; ti->error = "Unknown version"; goto bad; @@ -5121,6 +5217,11 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv goto bad; } } + if (!ic->discard_keyed && (ic->sb->flags & cpu_to_le32(SB_FLAG_DISCARD_KEYED))) { + r = -EINVAL; + ti->error = "Keyed discard cannot be disabled once enabled"; + goto bad; + } if (!!(ic->sb->flags & cpu_to_le32(SB_FLAG_HAVE_JOURNAL_MAC)) != !!ic->journal_mac_alg.alg_string) { r = -EINVAL; ti->error = "Journal mac mismatch"; @@ -5436,7 +5537,7 @@ static void dm_integrity_dtr(struct dm_target *ti) static struct target_type integrity_target = { .name = "integrity", - .version = {1, 14, 0}, + .version = {1, 15, 0}, .module = THIS_MODULE, .features = DM_TARGET_SINGLETON | DM_TARGET_INTEGRITY, .ctr = dm_integrity_ctr, diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 1db565b37620..777b917be400 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -33,6 +33,7 @@ struct dm_io_client { */ struct io { unsigned long error_bits; + unsigned long unsup_bits; atomic_t count; struct dm_io_client *client; io_notify_fn callback; @@ -119,6 +120,7 @@ static void retrieve_io_and_region_from_bio(struct bio *bio, struct io **io, static void complete_io(struct io *io) { unsigned long error_bits = io->error_bits; + unsigned long unsup_bits = io->unsup_bits; io_notify_fn fn = io->callback; void *context = io->context; @@ -127,13 +129,17 @@ static void complete_io(struct io *io) io->vma_invalidate_size); mempool_free(io, &io->client->pool); - fn(error_bits, context); + fn(error_bits, unsup_bits, context); } static void dec_count(struct io *io, unsigned int region, blk_status_t error) { - if (error) - set_bit(region, &io->error_bits); + if (unlikely(error)) { + if (error == BLK_STS_NOTSUPP || error == BLK_STS_INVAL) + set_bit(region, &io->unsup_bits); + else + set_bit(region, &io->error_bits); + } if (atomic_dec_and_test(&io->count)) complete_io(io); @@ -170,12 +176,11 @@ struct dpages { struct page **p, unsigned long *len, unsigned int *offset); void (*next_page)(struct dpages *dp); - union { - unsigned int context_u; - struct bvec_iter context_bi; - }; + unsigned int context_u; void *context_ptr; + struct bio *orig_bio; + void *vma_invalidate_address; unsigned long vma_invalidate_size; }; @@ -211,44 +216,6 @@ static void list_dp_init(struct dpages *dp, struct page_list *pl, unsigned int o } /* - * Functions for getting the pages from a bvec. - */ -static void bio_get_page(struct dpages *dp, struct page **p, - unsigned long *len, unsigned int *offset) -{ - struct bio_vec bvec = bvec_iter_bvec((struct bio_vec *)dp->context_ptr, - dp->context_bi); - - *p = bvec.bv_page; - *len = bvec.bv_len; - *offset = bvec.bv_offset; - - /* avoid figuring it out again in bio_next_page() */ - dp->context_bi.bi_sector = (sector_t)bvec.bv_len; -} - -static void bio_next_page(struct dpages *dp) -{ - unsigned int len = (unsigned int)dp->context_bi.bi_sector; - - bvec_iter_advance((struct bio_vec *)dp->context_ptr, - &dp->context_bi, len); -} - -static void bio_dp_init(struct dpages *dp, struct bio *bio) -{ - dp->get_page = bio_get_page; - dp->next_page = bio_next_page; - - /* - * We just use bvec iterator to retrieve pages, so it is ok to - * access the bvec table directly here - */ - dp->context_ptr = bio->bi_io_vec; - dp->context_bi = bio->bi_iter; -} - -/* * Functions for getting the pages from a VMA. */ static void vm_get_page(struct dpages *dp, @@ -332,6 +299,21 @@ static void do_region(const blk_opf_t opf, unsigned int region, return; } + if (dp->orig_bio) { + bio = bio_alloc_clone(where->bdev, dp->orig_bio, GFP_NOIO, + &io->client->bios); + bio->bi_iter.bi_sector = where->sector; + bio->bi_iter.bi_size = where->count << SECTOR_SHIFT; + bio->bi_opf = opf; + bio->bi_end_io = endio; + bio->bi_ioprio = ioprio; + store_io_and_region_in_bio(bio, io, region); + + atomic_inc(&io->count); + submit_bio(bio); + return; + } + /* * where->count may be zero if op holds a flush and we need to * send a zero-sized flush. @@ -418,6 +400,7 @@ static void async_io(struct dm_io_client *client, unsigned int num_regions, io = mempool_alloc(&client->pool, GFP_NOIO); io->error_bits = 0; + io->unsup_bits = 0; atomic_set(&io->count, 1); /* see dispatch_io() */ io->client = client; io->callback = fn; @@ -431,20 +414,23 @@ static void async_io(struct dm_io_client *client, unsigned int num_regions, struct sync_io { unsigned long error_bits; + unsigned long unsup_bits; struct completion wait; }; -static void sync_io_complete(unsigned long error, void *context) +static void sync_io_complete(unsigned long error, unsigned long unsup, void *context) { struct sync_io *sio = context; sio->error_bits = error; + sio->unsup_bits = unsup; complete(&sio->wait); } static int sync_io(struct dm_io_client *client, unsigned int num_regions, struct dm_io_region *where, blk_opf_t opf, struct dpages *dp, - unsigned long *error_bits, unsigned short ioprio) + unsigned long *error_bits, unsigned long *unsup_bits, + unsigned short ioprio) { struct sync_io sio; @@ -457,8 +443,10 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions, if (error_bits) *error_bits = sio.error_bits; + if (unsup_bits) + *unsup_bits = sio.unsup_bits; - return sio.error_bits ? -EIO : 0; + return sio.error_bits ? -EIO : sio.unsup_bits ? -EOPNOTSUPP : 0; } static int dp_init(struct dm_io_request *io_req, struct dpages *dp, @@ -468,6 +456,7 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp, dp->vma_invalidate_address = NULL; dp->vma_invalidate_size = 0; + dp->orig_bio = NULL; switch (io_req->mem.type) { case DM_IO_PAGE_LIST: @@ -475,7 +464,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp, break; case DM_IO_BIO: - bio_dp_init(dp, io_req->mem.ptr.bio); + /* + * The destination bios clone this bio's biovec directly, so + * there are no per-page accessors to set up here. + */ + dp->orig_bio = io_req->mem.ptr.bio; break; case DM_IO_VMA: @@ -500,7 +493,7 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp, int dm_io(struct dm_io_request *io_req, unsigned int num_regions, struct dm_io_region *where, unsigned long *sync_error_bits, - unsigned short ioprio) + unsigned long *sync_unsup_bits, unsigned short ioprio) { int r; struct dpages dp; @@ -516,7 +509,8 @@ int dm_io(struct dm_io_request *io_req, unsigned int num_regions, if (!io_req->notify.fn) return sync_io(io_req->client, num_regions, where, - io_req->bi_opf, &dp, sync_error_bits, ioprio); + io_req->bi_opf, &dp, sync_error_bits, + sync_unsup_bits, ioprio); async_io(io_req->client, num_regions, where, io_req->bi_opf, &dp, io_req->notify.fn, io_req->notify.context, ioprio); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 61af2a437a05..a6b8e97755cd 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -54,10 +54,8 @@ struct hash_cell { }; struct vers_iter { - size_t param_size; struct dm_target_versions *vers, *old_vers; char *end; - uint32_t flags; }; @@ -694,118 +692,108 @@ static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_ { struct rb_node *n; struct hash_cell *hc; - size_t len, needed = 0; - struct gendisk *disk; - struct dm_name_list *orig_nl, *nl, *old_nl = NULL; + size_t len; + struct dm_name_list *nl, *old_nl = NULL; + void *result_start, *result_limit; uint32_t *event_nr; - down_write(&_hash_lock); - - /* - * Loop through all the devices working out how much - * space we need. - */ - for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { - hc = container_of(n, struct hash_cell, name_node); - if (!filter_device(hc, param->name, param->uuid)) - continue; - needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1); - needed += align_val(sizeof(uint32_t) * 2); - if (param->flags & DM_UUID_FLAG && hc->uuid) - needed += align_val(strlen(hc->uuid) + 1); - } - /* * Grab our output buffer. */ - nl = orig_nl = get_result_buffer(param, param_size, &len); - if (len < needed || len < sizeof(nl->dev)) { - param->flags |= DM_BUFFER_FULL_FLAG; - goto out; - } - param->data_size = param->data_start + needed; + nl = result_start = get_result_buffer(param, param_size, &len); + result_limit = result_start + len; - nl->dev = 0; /* Flags no data */ + if (len >= sizeof(*nl)) + nl->dev = 0; /* Flags no data */ + + down_write(&_hash_lock); /* - * Now loop through filling out the names. + * Loop through filling out the names. */ for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { - void *uuid_ptr; + void *next_nl; hc = container_of(n, struct hash_cell, name_node); if (!filter_device(hc, param->name, param->uuid)) continue; - if (old_nl) - old_nl->next = (uint32_t) ((void *) nl - - (void *) old_nl); - disk = dm_disk(hc->md); - nl->dev = huge_encode_dev(disk_devt(disk)); - nl->next = 0; - strcpy(nl->name, hc->name); - old_nl = nl; - event_nr = align_ptr(nl->name + strlen(hc->name) + 1); + len = strlen(hc->name); + event_nr = align_ptr(nl->name + len + 1); + next_nl = event_nr + 2; + if (next_nl > result_limit) + break; + + ((u64 *)event_nr)[-1] = 0; + memcpy(nl->name, hc->name, len); + + nl->dev = huge_encode_dev(disk_devt(dm_disk(hc->md))); + event_nr[0] = dm_get_event_nr(hc->md); event_nr[1] = 0; - uuid_ptr = align_ptr(event_nr + 2); + if (param->flags & DM_UUID_FLAG) { if (hc->uuid) { + len = strlen(hc->uuid); + next_nl = align_ptr(next_nl + len + 1); + if (next_nl > result_limit) + break; event_nr[1] |= DM_NAME_LIST_FLAG_HAS_UUID; - strcpy(uuid_ptr, hc->uuid); - uuid_ptr = align_ptr(uuid_ptr + strlen(hc->uuid) + 1); + ((u64 *)next_nl)[-1] = 0; + memcpy(event_nr + 2, hc->uuid, len); } else { event_nr[1] |= DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID; } } - nl = uuid_ptr; + nl->next = next_nl - (void *)nl; + old_nl = nl; + nl = next_nl; } - /* - * If mismatch happens, security may be compromised due to buffer - * overflow, so it's better to crash. - */ - BUG_ON((char *)nl - (char *)orig_nl != needed); - out: - up_write(&_hash_lock); - return 0; -} + if (old_nl) + old_nl->next = 0; -static void list_version_get_needed(struct target_type *tt, void *needed_param) -{ - size_t *needed = needed_param; + if (n) + param->flags |= DM_BUFFER_FULL_FLAG; + else + param->data_size = param->data_start + ((void *)nl - result_start); - *needed += sizeof(struct dm_target_versions); - *needed += strlen(tt->name) + 1; - *needed += ALIGN_MASK; + up_write(&_hash_lock); + return 0; } static void list_version_get_info(struct target_type *tt, void *param) { struct vers_iter *info = param; + struct dm_target_versions *vers = info->vers; + size_t name_len = strlen(tt->name); + + if (!vers) + return; + + info->old_vers = vers; + info->vers = align_ptr((void *)(info->vers + 1) + name_len + 1); - /* Check space - it might have changed since the first iteration */ - if ((char *)info->vers + sizeof(struct dm_target_versions) + strlen(tt->name) + 1 > info->end) { - info->flags = DM_BUFFER_FULL_FLAG; + /* Check space */ + if ((char *)info->vers > info->end) { + info->vers = NULL; return; } - if (info->old_vers) - info->old_vers->next = (uint32_t) ((void *)info->vers - (void *)info->old_vers); + /* Zero padding and terminate vers->name[] */ + ((u64 *)info->vers)[-1] = 0; - info->vers->version[0] = tt->version[0]; - info->vers->version[1] = tt->version[1]; - info->vers->version[2] = tt->version[2]; - info->vers->next = 0; - strcpy(info->vers->name, tt->name); + vers->next = (char *)info->vers - (char *)vers; - info->old_vers = info->vers; - info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1); + vers->version[0] = tt->version[0]; + vers->version[1] = tt->version[1]; + vers->version[2] = tt->version[2]; + memcpy(vers->name, tt->name, name_len); } static int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name) { - size_t len, needed = 0; + size_t len; struct dm_target_versions *vers; struct vers_iter iter_info; struct target_type *tt = NULL; @@ -817,40 +805,30 @@ static int __list_versions(struct dm_ioctl *param, size_t param_size, const char } /* - * Loop through all the devices working out how much - * space we need. - */ - if (!tt) - dm_target_iterate(list_version_get_needed, &needed); - else - list_version_get_needed(tt, &needed); - - /* * Grab our output buffer. */ vers = get_result_buffer(param, param_size, &len); - if (len < needed) { - param->flags |= DM_BUFFER_FULL_FLAG; - goto out; - } - param->data_size = param->data_start + needed; - iter_info.param_size = param_size; iter_info.old_vers = NULL; iter_info.vers = vers; - iter_info.flags = 0; - iter_info.end = (char *)vers + needed; + iter_info.end = (char *)vers + len; /* - * Now loop through filling out the names & versions. + * Loop through filling out the names & versions. */ if (!tt) dm_target_iterate(list_version_get_info, &iter_info); else list_version_get_info(tt, &iter_info); - param->flags |= iter_info.flags; - out: + if (iter_info.vers) { + if (iter_info.old_vers) + iter_info.old_vers->next = 0; + param->data_size = param->data_start + ((char *)iter_info.vers - (char *)vers); + } else { + param->flags |= DM_BUFFER_FULL_FLAG; + } + if (tt) dm_put_target_type(tt); return 0; @@ -1444,10 +1422,6 @@ static void retrieve_status(struct dm_table *table, outptr += sizeof(struct dm_target_spec); remaining = len - (outptr - outbuf); - if (remaining <= 0) { - param->flags |= DM_BUFFER_FULL_FLAG; - break; - } /* Get the status/table string from the target driver */ if (ti->type->status) { @@ -1998,33 +1972,32 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) { static const struct { - int cmd; int flags; ioctl_fn fn; } _ioctls[] = { - {DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */ - {DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all}, - {DM_LIST_DEVICES_CMD, 0, list_devices}, - - {DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create}, - {DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove}, - {DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename}, - {DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend}, - {DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status}, - {DM_DEV_WAIT_CMD, 0, dev_wait}, - - {DM_TABLE_LOAD_CMD, 0, table_load}, - {DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear}, - {DM_TABLE_DEPS_CMD, 0, table_deps}, - {DM_TABLE_STATUS_CMD, 0, table_status}, - - {DM_LIST_VERSIONS_CMD, 0, list_versions}, - - {DM_TARGET_MSG_CMD, 0, target_message}, - {DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry}, - {DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll}, - {DM_GET_TARGET_VERSION_CMD, 0, get_target_version}, - {DM_MPATH_PROBE_PATHS_CMD, 0, NULL}, /* block device ioctl */ + [DM_VERSION_CMD] = {0, NULL}, /* version is dealt with elsewhere */ + [DM_REMOVE_ALL_CMD] = {IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all}, + [DM_LIST_DEVICES_CMD] = {0, list_devices}, + + [DM_DEV_CREATE_CMD] = {IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create}, + [DM_DEV_REMOVE_CMD] = {IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove}, + [DM_DEV_RENAME_CMD] = {IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename}, + [DM_DEV_SUSPEND_CMD] = {IOCTL_FLAGS_NO_PARAMS, dev_suspend}, + [DM_DEV_STATUS_CMD] = {IOCTL_FLAGS_NO_PARAMS, dev_status}, + [DM_DEV_WAIT_CMD] = {0, dev_wait}, + + [DM_TABLE_LOAD_CMD] = {0, table_load}, + [DM_TABLE_CLEAR_CMD] = {IOCTL_FLAGS_NO_PARAMS, table_clear}, + [DM_TABLE_DEPS_CMD] = {0, table_deps}, + [DM_TABLE_STATUS_CMD] = {0, table_status}, + + [DM_LIST_VERSIONS_CMD] = {0, list_versions}, + + [DM_TARGET_MSG_CMD] = {0, target_message}, + [DM_DEV_SET_GEOMETRY_CMD] = {0, dev_set_geometry}, + [DM_DEV_ARM_POLL_CMD] = {IOCTL_FLAGS_NO_PARAMS, dev_arm_poll}, + [DM_GET_TARGET_VERSION_CMD] = {0, get_target_version}, + [DM_MPATH_PROBE_PATHS_CMD] = {0, NULL}, /* block device ioctl */ }; if (unlikely(cmd >= ARRAY_SIZE(_ioctls))) @@ -2273,12 +2246,9 @@ static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u) static int dm_open(struct inode *inode, struct file *filp) { - int r; struct dm_file *priv; - r = nonseekable_open(inode, filp); - if (unlikely(r)) - return r; + nonseekable_open(inode, filp); priv = filp->private_data = kmalloc_obj(struct dm_file); if (!priv) diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index 96c8b8ff61c2..5c9c24a3dcd9 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -517,16 +517,16 @@ static int run_complete_job(struct kcopyd_job *job) return 0; } -static void complete_io(unsigned long error, void *context) +static void complete_io(unsigned long error, unsigned long unsup, void *context) { struct kcopyd_job *job = context; struct dm_kcopyd_client *kc = job->kc; io_job_finish(kc->throttle); - if (error) { + if (unlikely((error | unsup) != 0)) { if (op_is_write(job->op)) - job->write_err |= error; + job->write_err |= error | unsup; else job->read_err = 1; @@ -578,9 +578,9 @@ static int run_io_job(struct kcopyd_job *job) io_job_start(job->kc->throttle); if (job->op == REQ_OP_READ) - r = dm_io(&io_req, 1, &job->source, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &job->source, NULL, NULL, IOPRIO_DEFAULT); else - r = dm_io(&io_req, job->num_dests, job->dests, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, job->num_dests, job->dests, NULL, NULL, IOPRIO_DEFAULT); return r; } diff --git a/drivers/md/dm-log-userspace-base.c b/drivers/md/dm-log-userspace-base.c index 9d5918eb0a30..3ca35aa4e057 100644 --- a/drivers/md/dm-log-userspace-base.c +++ b/drivers/md/dm-log-userspace-base.c @@ -139,6 +139,7 @@ static int build_constructor_string(struct dm_target *ti, str_size += strlen(argv[i]) + 1; /* +1 for space between args */ str_size += 20; /* Max number of chars in a printed u64 number */ + str_size++; /* For NUL-terminator */ str = kzalloc(str_size, GFP_KERNEL); if (!str) { diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 2ddeb4250c59..4c114fc5ef83 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -300,7 +300,7 @@ static int rw_header(struct log_c *lc, enum req_op op) { lc->io_req.bi_opf = op; - return dm_io(&lc->io_req, 1, &lc->header_location, NULL, IOPRIO_DEFAULT); + return dm_io(&lc->io_req, 1, &lc->header_location, NULL, NULL, IOPRIO_DEFAULT); } static int flush_header(struct log_c *lc) @@ -313,7 +313,7 @@ static int flush_header(struct log_c *lc) lc->io_req.bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; - return dm_io(&lc->io_req, 1, &null_location, NULL, IOPRIO_DEFAULT); + return dm_io(&lc->io_req, 1, &null_location, NULL, NULL, IOPRIO_DEFAULT); } static int read_header(struct log_c *log) diff --git a/drivers/md/dm-pcache/cache.c b/drivers/md/dm-pcache/cache.c index bb1ada31e483..b0b3e21677de 100644 --- a/drivers/md/dm-pcache/cache.c +++ b/drivers/md/dm-pcache/cache.c @@ -118,7 +118,14 @@ int cache_pos_decode(struct pcache_cache *cache, if (!latest_addr) return -EIO; + if (!cache_seg_id_valid(cache, latest.cache_seg_id)) + return -EIO; + pos->cache_seg = &cache->segments[latest.cache_seg_id]; + + if (latest.seg_off >= pos->cache_seg->segment.data_size) + return -EIO; + pos->seg_off = latest.seg_off; *seq = latest.header.seq; *index = (latest_addr - pos_onmedia); @@ -155,6 +162,7 @@ static int cache_init(struct dm_pcache *pcache) cache->cache_dev = &pcache->cache_dev; cache->n_segs = cache_dev->seg_num; atomic_set(&cache->gc_errors, 0); + atomic_set(&cache->writeback_errors, 0); spin_lock_init(&cache->seg_map_lock); spin_lock_init(&cache->key_head_lock); @@ -194,6 +202,7 @@ static int cache_tail_init(struct pcache_cache *cache) { struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); bool new_cache = !(cache->cache_info.flags & PCACHE_CACHE_FLAGS_INIT_DONE); + int ret; if (new_cache) { __set_bit(0, cache->seg_map); @@ -210,6 +219,12 @@ static int cache_tail_init(struct pcache_cache *cache) pcache_dev_err(pcache, "Corrupted key tail or dirty tail.\n"); return -EIO; } + + ret = cache_verify_dirty_tail(cache); + if (ret) { + pcache_dev_err(pcache, "dirty tail chain does not terminate (crafted cache image?)\n"); + return ret; + } } return 0; @@ -247,6 +262,13 @@ static int get_seg_id(struct pcache_cache *cache, } else { *seg_id = cache->cache_info.seg_id; } + + if (*seg_id >= cache_dev->seg_num) { + pcache_dev_err(pcache, "invalid segment id %u from cache device (seg_num %u)\n", + *seg_id, cache_dev->seg_num); + ret = -EIO; + goto err; + } } return 0; err: @@ -262,6 +284,13 @@ static int cache_segs_init(struct pcache_cache *cache) int ret; u32 i; + if (cache_info->n_segs > cache->cache_dev->seg_num) { + pcache_dev_err(CACHE_TO_PCACHE(cache), + "cache_info n_segs %u exceeds cache device segments %u\n", + cache_info->n_segs, cache->cache_dev->seg_num); + return -EIO; + } + for (i = 0; i < cache_info->n_segs; i++) { ret = get_seg_id(cache, prev_cache_seg, new_cache, &seg_id); if (ret) diff --git a/drivers/md/dm-pcache/cache.h b/drivers/md/dm-pcache/cache.h index 27613b56be54..e49cdfd16aaf 100644 --- a/drivers/md/dm-pcache/cache.h +++ b/drivers/md/dm-pcache/cache.h @@ -180,6 +180,7 @@ struct pcache_cache { u32 advance; int ret; } writeback_ctx; + atomic_t writeback_errors; char gc_kset_onmedia_buf[PCACHE_KSET_ONMEDIA_SIZE_MAX]; struct delayed_work gc_work; @@ -273,7 +274,7 @@ struct pcache_cache_subtree_walk_ctx { struct list_head *submit_req_list; /* - * |--------| key_tmp + * |--------| key_tmp * |====| key */ int (*before)(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp, @@ -281,7 +282,7 @@ struct pcache_cache_subtree_walk_ctx { /* * |----------| key_tmp - * |=====| key + * |=====| key */ int (*after)(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp, struct pcache_cache_subtree_walk_ctx *ctx); @@ -340,7 +341,6 @@ void cache_seg_set_next_seg(struct pcache_cache_segment *cache_seg, u32 seg_id); /* cache request*/ int pcache_cache_flush(struct pcache_cache *cache); -void miss_read_end_work_fn(struct work_struct *work); int pcache_cache_handle_req(struct pcache_cache *cache, struct pcache_request *pcache_req); /* gc */ @@ -421,6 +421,20 @@ static inline bool cache_seg_is_ctrl_seg(u32 cache_seg_id) } /** + * cache_seg_id_valid - Validate a cache segment id read from the cache device. + * @cache: Pointer to the pcache_cache structure. + * @cache_seg_id: Segment id decoded from on-media metadata. + * + * On-media segment ids are only protected by a CRC, which an attacker who can + * format the cache device computes over their chosen value. Reject any id that + * would index cache->segments[] out of bounds before it is dereferenced. + */ +static inline bool cache_seg_id_valid(struct pcache_cache *cache, u32 cache_seg_id) +{ + return cache_seg_id < cache->cache_info.n_segs; +} + +/** * cache_key_cutfront - Cuts a specified length from the front of a cache key. * @key: Pointer to pcache_cache_key structure. * @cut_len: Length to cut from the front. @@ -491,6 +505,27 @@ static inline u32 cache_key_data_crc(struct pcache_cache_key *key) return crc32c(PCACHE_CRC_SEED, data, key->len); } +/** + * kset_onmedia_valid - Validate a kset header read from the cache device. + * @kset_onmedia: Pointer to the kset copied from on-media metadata. + * + * The magic and CRC are attacker-computable (fixed public seed). A non-last + * kset stores key_num keys inline, and cache_kset_crc() and the replay loop + * read struct_size(.., data, key_num) bytes from a buffer sized for + * PCACHE_KSET_KEYS_MAX keys, so key_num must be bounded before any such use. + */ +static inline bool kset_onmedia_valid(struct pcache_cache_kset_onmedia *kset_onmedia) +{ + if (kset_onmedia->magic != PCACHE_KSET_MAGIC) + return false; + + if (!(kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) && + kset_onmedia->key_num > PCACHE_KSET_KEYS_MAX) + return false; + + return true; +} + static inline u32 cache_kset_crc(struct pcache_cache_kset_onmedia *kset_onmedia) { u32 crc_size; @@ -630,6 +665,8 @@ static inline int cache_decode_dirty_tail(struct pcache_cache *cache) &cache->dirty_tail_index); } +int cache_verify_dirty_tail(struct pcache_cache *cache); + int pcache_cache_init(void); void pcache_cache_exit(void); #endif /* _PCACHE_CACHE_H */ diff --git a/drivers/md/dm-pcache/cache_dev.c b/drivers/md/dm-pcache/cache_dev.c index ece689e6ce59..f0259353ee39 100644 --- a/drivers/md/dm-pcache/cache_dev.c +++ b/drivers/md/dm-pcache/cache_dev.c @@ -242,6 +242,8 @@ int cache_dev_start(struct dm_pcache *pcache) struct pcache_cache_dev *cache_dev = &pcache->cache_dev; struct pcache_sb sb; bool format = false; + u32 seg_num; + u64 max_segs; int ret; mutex_init(&cache_dev->seg_lock); @@ -269,7 +271,25 @@ int cache_dev_start(struct dm_pcache *pcache) goto dax_release; cache_dev->sb_flags = le32_to_cpu(sb.flags); - ret = cache_dev_init(cache_dev, le32_to_cpu(sb.seg_num)); + + /* + * seg_num is read from the crc32c-only superblock, so whoever supplies + * the cache device controls it. It is the ceiling every later on-media + * segment id is validated against, so bound it against what the device + * physically holds before it is trusted, or a forged seg_num lets a + * segment id address past the DAX mapping. + */ + seg_num = le32_to_cpu(sb.seg_num); + max_segs = (bdev_nr_bytes(cache_dev->dm_dev->bdev) - PCACHE_SEGMENTS_OFF) / + PCACHE_SEG_SIZE; + if (seg_num == 0 || seg_num > max_segs || seg_num > PCACHE_CACHE_SEGS_MAX) { + pcache_dev_err(pcache, "invalid seg_num %u from cache device (device holds %llu, max %u)\n", + seg_num, max_segs, (u32)PCACHE_CACHE_SEGS_MAX); + ret = -EIO; + goto dax_release; + } + + ret = cache_dev_init(cache_dev, seg_num); if (ret) goto dax_release; diff --git a/drivers/md/dm-pcache/cache_gc.c b/drivers/md/dm-pcache/cache_gc.c index 94f8b276a021..9cebef16d75e 100644 --- a/drivers/md/dm-pcache/cache_gc.c +++ b/drivers/md/dm-pcache/cache_gc.c @@ -6,14 +6,13 @@ /** * cache_key_gc - Releases the reference of a cache key segment. - * @cache: Pointer to the pcache_cache structure. * @key: Pointer to the cache key to be garbage collected. * * This function decrements the reference count of the cache segment * associated with the given key. If the reference count drops to zero, * the segment may be invalidated and reused. */ -static void cache_key_gc(struct pcache_cache *cache, struct pcache_cache_key *key) +static void cache_key_gc(struct pcache_cache_key *key) { cache_seg_put(key->cache_pos.cache_seg); } @@ -37,18 +36,18 @@ static bool need_gc(struct pcache_cache *cache, struct pcache_cache_pos *dirty_t kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->gc_kset_onmedia_buf; - to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - key_tail->seg_off); + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(key_tail)); ret = copy_mc_to_kernel(kset_onmedia, key_addr, to_copy); if (ret) { pcache_dev_err(pcache, "error to read kset: %d", ret); return false; } - /* Check if kset_onmedia is corrupted */ - if (kset_onmedia->magic != PCACHE_KSET_MAGIC) { - pcache_dev_debug(pcache, "gc error: magic is not as expected. key_tail: %u:%u magic: %llx, expected: %llx\n", + /* Reject a corrupted or out-of-bounds kset before reading its keys */ + if (!kset_onmedia_valid(kset_onmedia)) { + pcache_dev_debug(pcache, "gc error: invalid kset. key_tail: %u:%u magic: %llx, key_num: %u\n", key_tail->cache_seg->cache_seg_id, key_tail->seg_off, - kset_onmedia->magic, PCACHE_KSET_MAGIC); + kset_onmedia->magic, kset_onmedia->key_num); return false; } @@ -74,11 +73,17 @@ static bool need_gc(struct pcache_cache *cache, struct pcache_cache_pos *dirty_t * @cache: Pointer to the pcache_cache structure. * @kset_onmedia: Pointer to the kset_onmedia structure for the last kset. */ -static void last_kset_gc(struct pcache_cache *cache, struct pcache_cache_kset_onmedia *kset_onmedia) +static int last_kset_gc(struct pcache_cache *cache, struct pcache_cache_kset_onmedia *kset_onmedia) { struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); struct pcache_cache_segment *cur_seg, *next_seg; + if (!cache_seg_id_valid(cache, kset_onmedia->next_cache_seg_id)) { + pcache_dev_err(pcache, "invalid next_cache_seg_id %u in gc (n_segs %u)\n", + kset_onmedia->next_cache_seg_id, cache->n_segs); + return -EIO; + } + cur_seg = cache->key_tail.cache_seg; next_seg = &cache->segments[kset_onmedia->next_cache_seg_id]; @@ -94,6 +99,8 @@ static void last_kset_gc(struct pcache_cache *cache, struct pcache_cache_kset_on spin_lock(&cache->seg_map_lock); __clear_bit(cur_seg->cache_seg_id, cache->seg_map); spin_unlock(&cache->seg_map_lock); + + return 0; } void pcache_cache_gc_fn(struct work_struct *work) @@ -130,10 +137,19 @@ void pcache_cache_gc_fn(struct work_struct *work) if (dirty_tail.cache_seg == key_tail.cache_seg) break; - last_kset_gc(cache, kset_onmedia); + ret = last_kset_gc(cache, kset_onmedia); + if (ret) { + atomic_inc(&cache->gc_errors); + return; + } continue; } + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(&key_tail)) { + atomic_inc(&cache->gc_errors); + return; + } + for (i = 0; i < kset_onmedia->key_num; i++) { struct pcache_cache_key key_tmp = { 0 }; @@ -152,7 +168,7 @@ void pcache_cache_gc_fn(struct work_struct *work) return; } - cache_key_gc(cache, key); + cache_key_gc(key); } pcache_dev_debug(pcache, "gc advance: %u:%u %u\n", diff --git a/drivers/md/dm-pcache/cache_key.c b/drivers/md/dm-pcache/cache_key.c index e068e878231b..195157bef0f9 100644 --- a/drivers/md/dm-pcache/cache_key.c +++ b/drivers/md/dm-pcache/cache_key.c @@ -90,13 +90,36 @@ int cache_key_decode(struct pcache_cache *cache, struct pcache_cache_key *key) { struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); + u64 dev_bytes = (u64)cache->dev_size << SECTOR_SHIFT; key->off = key_onmedia->off; key->len = key_onmedia->len; + if (key_onmedia->len == 0 || + key_onmedia->len > dev_bytes || + key_onmedia->off > dev_bytes - key_onmedia->len) { + pcache_dev_err(pcache, "key off %llu + len %u exceeds device size\n", + key_onmedia->off, key_onmedia->len); + return -EIO; + } + + if (!cache_seg_id_valid(cache, key_onmedia->cache_seg_id)) { + pcache_dev_err(pcache, "invalid cache_seg_id %u in cache key (n_segs %u)\n", + key_onmedia->cache_seg_id, cache->n_segs); + return -EIO; + } + key->cache_pos.cache_seg = &cache->segments[key_onmedia->cache_seg_id]; key->cache_pos.seg_off = key_onmedia->cache_seg_off; + if ((u64)key->cache_pos.seg_off + key->len > + key->cache_pos.cache_seg->segment.data_size) { + pcache_dev_err(pcache, "key seg_off %u + len %u exceeds segment data size %u\n", + key->cache_pos.seg_off, key->len, + key->cache_pos.cache_seg->segment.data_size); + return -EIO; + } + key->seg_gen = key_onmedia->seg_gen; key->flags = key_onmedia->flags; @@ -265,7 +288,7 @@ int cache_subtree_walk(struct pcache_cache_subtree_walk_ctx *ctx) /* * If key_tmp starts after the end of key, stop traversing. - * |--------| + * |--------| * |====| */ if (cache_key_lstart(key_tmp) >= cache_key_lend(key)) { @@ -728,18 +751,17 @@ static int kset_replay(struct pcache_cache *cache, struct pcache_cache_kset_onme goto err; } - __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map); - /* Check if the segment generation is valid for insertion. */ if (key->seg_gen < key->cache_pos.cache_seg->gen) { cache_key_put(key); - } else { - cache_subtree = get_subtree(&cache->req_key_tree, key->off); - spin_lock(&cache_subtree->tree_lock); - cache_key_insert(&cache->req_key_tree, key, true); - spin_unlock(&cache_subtree->tree_lock); + continue; } + __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map); + cache_subtree = get_subtree(&cache->req_key_tree, key->off); + spin_lock(&cache_subtree->tree_lock); + cache_key_insert(&cache->req_key_tree, key, true); + spin_unlock(&cache_subtree->tree_lock); cache_seg_get(key->cache_pos.cache_seg); } @@ -754,7 +776,7 @@ int cache_replay(struct pcache_cache *cache) struct pcache_cache_pos pos_tail; struct pcache_cache_pos *pos; struct pcache_cache_kset_onmedia *kset_onmedia; - u32 to_copy, count = 0; + u32 to_copy, count = 0, last_hops = 0; int ret = 0; kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL); @@ -771,14 +793,14 @@ int cache_replay(struct pcache_cache *cache) __set_bit(pos->cache_seg->cache_seg_id, cache->seg_map); while (true) { - to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - pos->seg_off); + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(pos)); ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(pos), to_copy); if (ret) { ret = -EIO; goto out; } - if (kset_onmedia->magic != PCACHE_KSET_MAGIC || + if (!kset_onmedia_valid(kset_onmedia) || kset_onmedia->crc != cache_kset_crc(kset_onmedia)) { break; } @@ -789,6 +811,16 @@ int cache_replay(struct pcache_cache *cache) pcache_dev_debug(pcache, "last kset replay, next: %u\n", kset_onmedia->next_cache_seg_id); + if (!cache_seg_id_valid(cache, kset_onmedia->next_cache_seg_id)) { + ret = -EIO; + goto out; + } + + if (++last_hops > cache->n_segs) { + ret = -EIO; + goto out; + } + next_seg = &cache->segments[kset_onmedia->next_cache_seg_id]; pos->cache_seg = next_seg; @@ -799,6 +831,11 @@ int cache_replay(struct pcache_cache *cache) } /* Replay the kset and check for errors. */ + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(pos)) { + ret = -EIO; + goto out; + } + ret = kset_replay(cache, kset_onmedia); if (ret) goto out; @@ -820,6 +857,75 @@ out: return ret; } +/* + * cache_verify_dirty_tail - reject a persisted dirty_tail whose last-kset + * chain does not terminate. + * + * dirty_tail is decoded independently of the key_tail chain cache_replay() + * walks, so replay's hop cap does not cover it. A crafted chain that loops + * back on itself makes the writeback worker re-arm forever; walk it once here + * with the same cap and fail the load if it does not end within n_segs hops. + */ +int cache_verify_dirty_tail(struct pcache_cache *cache) +{ + struct pcache_cache_pos pos; + struct pcache_cache_kset_onmedia *kset_onmedia; + u32 to_copy, last_hops = 0, count = 0; + int ret = 0; + + kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL); + if (!kset_onmedia) + return -ENOMEM; + + cache_pos_copy(&pos, &cache->dirty_tail); + + while (true) { + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(&pos)); + ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(&pos), to_copy); + if (ret) { + ret = -EIO; + goto out; + } + + /* A missing, short or corrupt kset is the normal end of the chain. */ + if (!kset_onmedia_valid(kset_onmedia) || + kset_onmedia->crc != cache_kset_crc(kset_onmedia)) { + ret = 0; + goto out; + } + + if (kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) { + if (!cache_seg_id_valid(cache, kset_onmedia->next_cache_seg_id)) { + ret = -EIO; + goto out; + } + + if (++last_hops > cache->n_segs) { + ret = -EIO; + goto out; + } + + pos.cache_seg = &cache->segments[kset_onmedia->next_cache_seg_id]; + pos.seg_off = 0; + continue; + } + + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(&pos)) { + ret = -EIO; + goto out; + } + + cache_pos_advance(&pos, get_kset_onmedia_size(kset_onmedia)); + if (++count > 512) { + cond_resched(); + count = 0; + } + } +out: + kfree(kset_onmedia); + return ret; +} + int cache_tree_init(struct pcache_cache *cache, struct pcache_cache_tree *cache_tree, u32 n_subtrees) { int ret; diff --git a/drivers/md/dm-pcache/cache_req.c b/drivers/md/dm-pcache/cache_req.c index 7854a30e07b7..b2cb3c7615d4 100644 --- a/drivers/md/dm-pcache/cache_req.c +++ b/drivers/md/dm-pcache/cache_req.c @@ -39,13 +39,11 @@ static int cache_data_alloc(struct pcache_cache *cache, struct pcache_cache_key struct pcache_cache_pos *head_pos; struct pcache_cache_segment *cache_seg; u32 seg_remain; - u32 allocated = 0, to_alloc; int ret = 0; preempt_disable(); data_head = get_data_head(cache); again: - to_alloc = key->len - allocated; if (!data_head->head_pos.cache_seg) { seg_remain = 0; } else { @@ -57,10 +55,9 @@ again: seg_remain = cache_seg_remain(head_pos); } - if (seg_remain > to_alloc) { + if (seg_remain > key->len) { /* If remaining space in segment is sufficient for the cache key, allocate it. */ - cache_pos_advance(head_pos, to_alloc); - allocated += to_alloc; + cache_pos_advance(head_pos, key->len); cache_seg_get(cache_seg); } else if (seg_remain) { /* If remaining space is not enough, allocate the remaining space and adjust the cache key length. */ @@ -317,7 +314,7 @@ static struct pcache_backing_dev_req *get_pre_alloc_req(struct pcache_cache_subt * * The scenario handled here: * - * |--------| key_tmp (existing cached range) + * |--------| key_tmp (existing cached range) * |====| key (requested range, preceding key_tmp) * * Since `key` is before `key_tmp`, it signifies that the requested data @@ -352,7 +349,7 @@ static int read_before(struct pcache_cache_key *key, struct pcache_cache_key *ke * During cache_subtree_walk, this function manages a scenario where part of the * requested data range overlaps with an existing cache node (`key_tmp`). * - * |----------------| key_tmp (existing cached range) + * |----------------| key_tmp (existing cached range) * |===========| key (requested range, overlapping the tail of key_tmp) */ static int read_overlap_tail(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp, @@ -474,8 +471,8 @@ static int read_overlap_contain(struct pcache_cache_key *key, struct pcache_cach } /* - * |-----------| key_tmp (existing cached range) - * |====| key (requested range, fully within key_tmp) + * |-----------| key_tmp (existing cached range) + * |====| key (requested range, fully within key_tmp) * * If `key_tmp` contains valid cached data, this function copies the relevant * portion to the request's bio. Otherwise, it sends a backing request to @@ -524,8 +521,8 @@ static int read_overlap_contained(struct pcache_cache_key *key, struct pcache_ca } /* - * |--------| key_tmp (existing cached range) - * |==========| key (requested range, overlapping the head of key_tmp) + * |--------| key_tmp (existing cached range) + * |==========| key (requested range, overlapping the head of key_tmp) */ static int read_overlap_head(struct pcache_cache_key *key, struct pcache_cache_key *key_tmp, struct pcache_cache_subtree_walk_ctx *ctx) diff --git a/drivers/md/dm-pcache/cache_segment.c b/drivers/md/dm-pcache/cache_segment.c index 9d92e2b067ed..c698ebbc626d 100644 --- a/drivers/md/dm-pcache/cache_segment.c +++ b/drivers/md/dm-pcache/cache_segment.c @@ -243,8 +243,16 @@ struct pcache_cache_segment *get_cache_segment(struct pcache_cache *cache) spin_lock(&cache->seg_map_lock); again: - seg_id = find_next_zero_bit(cache->seg_map, cache->n_segs, cache->last_cache_seg); - if (seg_id == cache->n_segs) { + /* + * Only allocate initialized segments. cache_segs_init() initializes + * cache_info.n_segs of the cache->n_segs device segments; a forged + * smaller cache_info.n_segs leaves the rest as zeroed structs whose data + * pointer is NULL. Bounding the search to cache_info.n_segs keeps such a + * segment from reaching cache_kset_close(), which writes through it. + */ + seg_id = find_next_zero_bit(cache->seg_map, cache->cache_info.n_segs, + cache->last_cache_seg); + if (seg_id == cache->cache_info.n_segs) { /* reset the hint of ->last_cache_seg and retry */ if (cache->last_cache_seg) { cache->last_cache_seg = 0; diff --git a/drivers/md/dm-pcache/cache_writeback.c b/drivers/md/dm-pcache/cache_writeback.c index 87a82b3fe836..c8a4c8110a58 100644 --- a/drivers/md/dm-pcache/cache_writeback.c +++ b/drivers/md/dm-pcache/cache_writeback.c @@ -48,18 +48,18 @@ static inline bool is_cache_clean(struct pcache_cache *cache, struct pcache_cach addr = cache_pos_addr(dirty_tail); kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->wb_kset_onmedia_buf; - to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - dirty_tail->seg_off); + to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, cache_seg_remain(dirty_tail)); ret = copy_mc_to_kernel(kset_onmedia, addr, to_copy); if (ret) { pcache_dev_err(pcache, "error to read kset: %d", ret); return true; } - /* Check if the magic number matches the expected value */ - if (kset_onmedia->magic != PCACHE_KSET_MAGIC) { - pcache_dev_debug(pcache, "dirty_tail: %u:%u magic: %llx, not expected: %llx\n", + /* Reject a corrupted or out-of-bounds kset before reading its keys */ + if (!kset_onmedia_valid(kset_onmedia)) { + pcache_dev_debug(pcache, "dirty_tail: %u:%u invalid kset magic: %llx, key_num: %u\n", dirty_tail->cache_seg->cache_seg_id, dirty_tail->seg_off, - kset_onmedia->magic, PCACHE_KSET_MAGIC); + kset_onmedia->magic, kset_onmedia->key_num); return true; } @@ -196,12 +196,18 @@ clear_tree: return ret; } -static void last_kset_writeback(struct pcache_cache *cache, +static int last_kset_writeback(struct pcache_cache *cache, struct pcache_cache_kset_onmedia *last_kset_onmedia) { struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); struct pcache_cache_segment *next_seg; + if (!cache_seg_id_valid(cache, last_kset_onmedia->next_cache_seg_id)) { + pcache_dev_err(pcache, "invalid next_cache_seg_id %u in writeback (n_segs %u)\n", + last_kset_onmedia->next_cache_seg_id, cache->n_segs); + return -EIO; + } + pcache_dev_debug(pcache, "last kset, next: %u\n", last_kset_onmedia->next_cache_seg_id); next_seg = &cache->segments[last_kset_onmedia->next_cache_seg_id]; @@ -211,6 +217,8 @@ static void last_kset_writeback(struct pcache_cache *cache, cache->dirty_tail.seg_off = 0; cache_encode_dirty_tail(cache); mutex_unlock(&cache->dirty_tail_lock); + + return 0; } void cache_writeback_fn(struct work_struct *work) @@ -229,6 +237,9 @@ void cache_writeback_fn(struct work_struct *work) if (pcache_is_stopping(pcache)) goto unlock; + if (atomic_read(&cache->writeback_errors)) + goto unlock; + kset_onmedia = (struct pcache_cache_kset_onmedia *)cache->wb_kset_onmedia_buf; mutex_lock(&cache->dirty_tail_lock); @@ -241,15 +252,24 @@ void cache_writeback_fn(struct work_struct *work) } if (kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) { - last_kset_writeback(cache, kset_onmedia); + ret = last_kset_writeback(cache, kset_onmedia); + if (ret) { + atomic_inc(&cache->writeback_errors); + goto unlock; + } delay = 0; goto queue_work; } + if (get_kset_onmedia_size(kset_onmedia) > cache_seg_remain(&dirty_tail)) { + atomic_inc(&cache->writeback_errors); + goto unlock; + } + ret = cache_kset_insert_tree(cache, kset_onmedia); if (ret) { - delay = PCACHE_CACHE_WRITEBACK_INTERVAL; - goto queue_work; + atomic_inc(&cache->writeback_errors); + goto unlock; } cache_wb_tree_writeback(cache, get_kset_onmedia_size(kset_onmedia)); diff --git a/drivers/md/dm-pcache/dm_pcache.c b/drivers/md/dm-pcache/dm_pcache.c index d5cfd162c063..645fc27d82ba 100644 --- a/drivers/md/dm-pcache/dm_pcache.c +++ b/drivers/md/dm-pcache/dm_pcache.c @@ -439,13 +439,13 @@ static int dm_pcache_message(struct dm_target *ti, unsigned int argc, char **argv, char *result, unsigned int maxlen) { struct dm_pcache *pcache = ti->private; - unsigned long val; + u8 val; if (argc != 2) goto err; if (!strcasecmp(argv[0], "gc_percent")) { - if (kstrtoul(argv[1], 10, &val)) + if (kstrtou8(argv[1], 10, &val)) goto err; return pcache_cache_set_gc_percent(&pcache->cache, val); diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index de5c00704e69..da2a5e2002ec 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -258,7 +258,7 @@ out: static int mirror_flush(struct dm_target *ti) { struct mirror_set *ms = ti->private; - unsigned long error_bits; + unsigned long error_bits, unsup_bits; unsigned int i; struct dm_io_region io[MAX_NR_MIRRORS]; @@ -277,8 +277,8 @@ static int mirror_flush(struct dm_target *ti) } error_bits = -1; - dm_io(&io_req, ms->nr_mirrors, io, &error_bits, IOPRIO_DEFAULT); - if (unlikely(error_bits != 0)) { + dm_io(&io_req, ms->nr_mirrors, io, &error_bits, &unsup_bits, IOPRIO_DEFAULT); + if (unlikely((error_bits | unsup_bits) != 0)) { for (i = 0; i < ms->nr_mirrors; i++) if (test_bit(i, &error_bits)) fail_mirror(ms->mirror + i, @@ -511,7 +511,7 @@ static void hold_bio(struct mirror_set *ms, struct bio *bio) * Reads *--------------------------------------------------------------- */ -static void read_callback(unsigned long error, void *context) +static void read_callback(unsigned long error, unsigned long unsup, void *context) { struct bio *bio = context; struct mirror *m; @@ -520,6 +520,8 @@ static void read_callback(unsigned long error, void *context) bio_set_m(bio, NULL); if (likely(!error)) { + if (unlikely(unsup != 0)) + bio->bi_status = BLK_STS_INVAL; bio_endio(bio); return; } @@ -553,7 +555,7 @@ static void read_async_bio(struct mirror *m, struct bio *bio) map_region(&io, m, bio); bio_set_m(bio, m); - BUG_ON(dm_io(&io_req, 1, &io, NULL, IOPRIO_DEFAULT)); + BUG_ON(dm_io(&io_req, 1, &io, NULL, NULL, IOPRIO_DEFAULT)); } static inline int region_in_sync(struct mirror_set *ms, region_t region, @@ -600,7 +602,7 @@ static void do_reads(struct mirror_set *ms, struct bio_list *reads) * NOSYNC: increment pending, just write to the default mirror *--------------------------------------------------------------------- */ -static void write_callback(unsigned long error, void *context) +static void write_callback(unsigned long error, unsigned long unsup, void *context) { unsigned int i; struct bio *bio = context; @@ -617,7 +619,7 @@ static void write_callback(unsigned long error, void *context) * This way we handle both writes to SYNC and NOSYNC * regions with the same code. */ - if (likely(!error)) { + if (likely(!(error | unsup))) { bio_endio(bio); return; } @@ -632,6 +634,12 @@ static void write_callback(unsigned long error, void *context) return; } + if (!error && unsup) { + bio->bi_status = BLK_STS_INVAL; + bio_endio(bio); + return; + } + for (i = 0; i < ms->nr_mirrors; i++) if (test_bit(i, &error)) fail_mirror(ms->mirror + i, DM_RAID1_WRITE_ERROR); @@ -680,7 +688,7 @@ static void do_write(struct mirror_set *ms, struct bio *bio) */ bio_set_m(bio, get_default_mirror(ms)); - BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, IOPRIO_DEFAULT)); + BUG_ON(dm_io(&io_req, ms->nr_mirrors, io, NULL, NULL, IOPRIO_DEFAULT)); } static void do_writes(struct mirror_set *ms, struct bio_list *writes) @@ -1262,7 +1270,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, return DM_ENDIO_DONE; } - if (*error == BLK_STS_NOTSUPP) + if (*error == BLK_STS_NOTSUPP || *error == BLK_STS_INVAL) goto out; if (bio->bi_opf & REQ_RAHEAD) diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index aa239ccda270..e86d929b0e95 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -223,7 +223,7 @@ static void do_metadata(struct work_struct *work) { struct mdata_req *req = container_of(work, struct mdata_req, work); - req->result = dm_io(req->io_req, 1, req->where, NULL, IOPRIO_DEFAULT); + req->result = dm_io(req->io_req, 1, req->where, NULL, NULL, IOPRIO_DEFAULT); } /* @@ -247,7 +247,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, blk_opf_t opf, struct mdata_req req; if (!metadata) - return dm_io(&io_req, 1, &where, NULL, IOPRIO_DEFAULT); + return dm_io(&io_req, 1, &where, NULL, NULL, IOPRIO_DEFAULT); req.where = &where; req.io_req = &io_req; diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 1489fda9d24a..c8a9ca1eeae7 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -911,9 +911,7 @@ static int init_hash_tables(struct dm_snapshot *s) static void merge_shutdown(struct dm_snapshot *s) { - clear_bit_unlock(RUNNING_MERGE, &s->state_bits); - smp_mb__after_atomic(); - wake_up_bit(&s->state_bits, RUNNING_MERGE); + clear_and_wake_up_bit(RUNNING_MERGE, &s->state_bits); } static struct bio *__release_queued_bios_after_merge(struct dm_snapshot *s) diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c index 5df710061a11..beabbe3b39f3 100644 --- a/drivers/md/dm-stats.c +++ b/drivers/md/dm-stats.c @@ -178,8 +178,10 @@ static void dm_stat_free(struct rcu_head *head) kfree(s->program_id); kfree(s->aux_data); for_each_possible_cpu(cpu) { - dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size); - dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size); + if (s->stat_percpu[cpu]) { + dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size); + dm_kvfree(s->stat_percpu[cpu], s->percpu_alloc_size); + } } dm_kvfree(s->stat_shared[0].tmp.histogram, s->histogram_alloc_size); dm_kvfree(s, s->shared_alloc_size); diff --git a/drivers/md/dm-switch.c b/drivers/md/dm-switch.c index 5952f02de1e6..e5b507b4fa7b 100644 --- a/drivers/md/dm-switch.c +++ b/drivers/md/dm-switch.c @@ -184,7 +184,7 @@ static void switch_region_table_write(struct switch_ctx *sctx, unsigned long reg pte = sctx->region_table[region_index]; pte &= ~((((region_table_slot_t)1 << sctx->region_table_entry_bits) - 1) << bit); pte |= (region_table_slot_t)value << bit; - sctx->region_table[region_index] = pte; + WRITE_ONCE(sctx->region_table[region_index], pte); } /* diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index dc2eff6b739d..a483f49dd4ce 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -2035,7 +2035,7 @@ int dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, limits->features &= ~BLK_FEAT_NOWAIT; /* - * The current polling impementation does not support request based + * The current polling implementation does not support request based * stacking. */ if (!__table_type_bio_based(t->type)) diff --git a/drivers/md/dm-vdo/indexer/config.c b/drivers/md/dm-vdo/indexer/config.c index 4a2cc66cfd60..ccd26af37f7c 100644 --- a/drivers/md/dm-vdo/indexer/config.c +++ b/drivers/md/dm-vdo/indexer/config.c @@ -29,7 +29,7 @@ static bool are_matching_configurations(struct uds_configuration *saved_config, struct index_geometry *saved_geometry, struct uds_configuration *user) { - struct index_geometry *geometry = user->geometry; + const struct index_geometry *geometry = &user->geometry; bool result = true; if (saved_geometry->record_pages_per_chapter != geometry->record_pages_per_chapter) { @@ -141,8 +141,8 @@ int uds_validate_config_contents(struct buffered_reader *reader, return UDS_CORRUPT_DATA; if (is_version(INDEX_CONFIG_VERSION_6_02, version_buffer)) { - user_config->geometry->remapped_virtual = 0; - user_config->geometry->remapped_physical = 0; + user_config->geometry.remapped_virtual = 0; + user_config->geometry.remapped_physical = 0; } else { u8 remapping[sizeof(u64) + sizeof(u64)]; @@ -153,9 +153,9 @@ int uds_validate_config_contents(struct buffered_reader *reader, offset = 0; decode_u64_le(remapping, &offset, - &user_config->geometry->remapped_virtual); + &user_config->geometry.remapped_virtual); decode_u64_le(remapping, &offset, - &user_config->geometry->remapped_physical); + &user_config->geometry.remapped_physical); } if (!are_matching_configurations(&config, &geometry, user_config)) { @@ -175,7 +175,7 @@ int uds_write_config_contents(struct buffered_writer *writer, struct uds_configuration *config, u32 version) { int result; - struct index_geometry *geometry = config->geometry; + const struct index_geometry *geometry = &config->geometry; u8 buffer[sizeof(struct uds_configuration_8_02)]; size_t offset = 0; @@ -329,13 +329,10 @@ int uds_make_configuration(const struct uds_parameters *params, if (result != VDO_SUCCESS) return result; - result = uds_make_index_geometry(DEFAULT_BYTES_PER_PAGE, record_pages_per_chapter, - chapters_per_volume, sparse_chapters_per_volume, - 0, 0, &config->geometry); - if (result != UDS_SUCCESS) { - uds_free_configuration(config); - return result; - } + config->geometry = + uds_init_index_geometry(DEFAULT_BYTES_PER_PAGE, record_pages_per_chapter, + chapters_per_volume, sparse_chapters_per_volume, + 0, 0); config->zone_count = normalize_zone_count(params->zone_count); config->read_threads = normalize_read_threads(params->read_threads); @@ -355,22 +352,21 @@ int uds_make_configuration(const struct uds_parameters *params, void uds_free_configuration(struct uds_configuration *config) { if (config != NULL) { - uds_free_index_geometry(config->geometry); vdo_free(config); } } void uds_log_configuration(struct uds_configuration *config) { - struct index_geometry *geometry = config->geometry; + const struct index_geometry geometry = config->geometry; vdo_log_debug("Configuration:"); - vdo_log_debug(" Record pages per chapter: %10u", geometry->record_pages_per_chapter); - vdo_log_debug(" Chapters per volume: %10u", geometry->chapters_per_volume); - vdo_log_debug(" Sparse chapters per volume: %10u", geometry->sparse_chapters_per_volume); + vdo_log_debug(" Record pages per chapter: %10u", geometry.record_pages_per_chapter); + vdo_log_debug(" Chapters per volume: %10u", geometry.chapters_per_volume); + vdo_log_debug(" Sparse chapters per volume: %10u", geometry.sparse_chapters_per_volume); vdo_log_debug(" Cache size (chapters): %10u", config->cache_chapters); vdo_log_debug(" Volume index mean delta: %10u", config->volume_index_mean_delta); - vdo_log_debug(" Bytes per page: %10zu", geometry->bytes_per_page); + vdo_log_debug(" Bytes per page: %10zu", geometry.bytes_per_page); vdo_log_debug(" Sparse sample rate: %10u", config->sparse_sample_rate); vdo_log_debug(" Nonce: %llu", (unsigned long long) config->nonce); } diff --git a/drivers/md/dm-vdo/indexer/config.h b/drivers/md/dm-vdo/indexer/config.h index 08507dc2f7a1..cffbf5364d16 100644 --- a/drivers/md/dm-vdo/indexer/config.h +++ b/drivers/md/dm-vdo/indexer/config.h @@ -37,7 +37,7 @@ struct uds_configuration { /* Parameters for the volume */ /* The volume layout */ - struct index_geometry *geometry; + struct index_geometry geometry; /* Index owner's nonce */ u64 nonce; diff --git a/drivers/md/dm-vdo/indexer/geometry.c b/drivers/md/dm-vdo/indexer/geometry.c index 49f122a223d5..66c91d47ad24 100644 --- a/drivers/md/dm-vdo/indexer/geometry.c +++ b/drivers/md/dm-vdo/indexer/geometry.c @@ -53,75 +53,51 @@ * chapter it was moved to. */ -int uds_make_index_geometry(size_t bytes_per_page, u32 record_pages_per_chapter, - u32 chapters_per_volume, u32 sparse_chapters_per_volume, - u64 remapped_virtual, u64 remapped_physical, - struct index_geometry **geometry_ptr) +struct index_geometry uds_init_index_geometry(size_t bytes_per_page, u32 record_pages_per_chapter, + u32 chapters_per_volume, u32 sparse_chapters_per_volume, + u64 remapped_virtual, u64 remapped_physical) { - int result; - struct index_geometry *geometry; - - result = vdo_allocate(1, "geometry", &geometry); - if (result != VDO_SUCCESS) - return result; - - geometry->bytes_per_page = bytes_per_page; - geometry->record_pages_per_chapter = record_pages_per_chapter; - geometry->chapters_per_volume = chapters_per_volume; - geometry->sparse_chapters_per_volume = sparse_chapters_per_volume; - geometry->dense_chapters_per_volume = chapters_per_volume - sparse_chapters_per_volume; - geometry->remapped_virtual = remapped_virtual; - geometry->remapped_physical = remapped_physical; - - geometry->records_per_page = bytes_per_page / BYTES_PER_RECORD; - geometry->records_per_chapter = geometry->records_per_page * record_pages_per_chapter; - geometry->records_per_volume = (u64) geometry->records_per_chapter * chapters_per_volume; - - geometry->chapter_mean_delta = 1 << DEFAULT_CHAPTER_MEAN_DELTA_BITS; - geometry->chapter_payload_bits = bits_per(record_pages_per_chapter - 1); + struct index_geometry geometry = { + .bytes_per_page = bytes_per_page, + .record_pages_per_chapter = record_pages_per_chapter, + .chapters_per_volume = chapters_per_volume, + .sparse_chapters_per_volume = sparse_chapters_per_volume, + .dense_chapters_per_volume = chapters_per_volume - sparse_chapters_per_volume, + .remapped_virtual = remapped_virtual, + .remapped_physical = remapped_physical, + }; + + geometry.records_per_page = bytes_per_page / BYTES_PER_RECORD; + geometry.records_per_chapter = geometry.records_per_page * record_pages_per_chapter; + geometry.records_per_volume = (u64) geometry.records_per_chapter * chapters_per_volume; + + geometry.chapter_mean_delta = 1 << DEFAULT_CHAPTER_MEAN_DELTA_BITS; + geometry.chapter_payload_bits = bits_per(record_pages_per_chapter - 1); /* * We want 1 delta list for every 64 records in the chapter. * The "| 077" ensures that the chapter_delta_list_bits computation * does not underflow. */ - geometry->chapter_delta_list_bits = - bits_per((geometry->records_per_chapter - 1) | 077) - 6; - geometry->delta_lists_per_chapter = 1 << geometry->chapter_delta_list_bits; + geometry.chapter_delta_list_bits = bits_per((geometry.records_per_chapter - 1) | 077) - 6; + geometry.delta_lists_per_chapter = 1 << geometry.chapter_delta_list_bits; /* We need enough address bits to achieve the desired mean delta. */ - geometry->chapter_address_bits = + geometry.chapter_address_bits = (DEFAULT_CHAPTER_MEAN_DELTA_BITS - - geometry->chapter_delta_list_bits + - bits_per(geometry->records_per_chapter - 1)); - geometry->index_pages_per_chapter = - uds_get_delta_index_page_count(geometry->records_per_chapter, - geometry->delta_lists_per_chapter, - geometry->chapter_mean_delta, - geometry->chapter_payload_bits, + geometry.chapter_delta_list_bits + + bits_per(geometry.records_per_chapter - 1)); + geometry.index_pages_per_chapter = + uds_get_delta_index_page_count(geometry.records_per_chapter, + geometry.delta_lists_per_chapter, + geometry.chapter_mean_delta, + geometry.chapter_payload_bits, bytes_per_page); - geometry->pages_per_chapter = geometry->index_pages_per_chapter + record_pages_per_chapter; - geometry->pages_per_volume = geometry->pages_per_chapter * chapters_per_volume; - geometry->bytes_per_volume = - bytes_per_page * (geometry->pages_per_volume + HEADER_PAGES_PER_VOLUME); + geometry.pages_per_chapter = geometry.index_pages_per_chapter + record_pages_per_chapter; + geometry.pages_per_volume = geometry.pages_per_chapter * chapters_per_volume; + geometry.bytes_per_volume = + bytes_per_page * (geometry.pages_per_volume + HEADER_PAGES_PER_VOLUME); - *geometry_ptr = geometry; - return UDS_SUCCESS; -} - -int uds_copy_index_geometry(struct index_geometry *source, - struct index_geometry **geometry_ptr) -{ - return uds_make_index_geometry(source->bytes_per_page, - source->record_pages_per_chapter, - source->chapters_per_volume, - source->sparse_chapters_per_volume, - source->remapped_virtual, source->remapped_physical, - geometry_ptr); -} - -void uds_free_index_geometry(struct index_geometry *geometry) -{ - vdo_free(geometry); + return geometry; } u32 __must_check uds_map_to_physical_chapter(const struct index_geometry *geometry, diff --git a/drivers/md/dm-vdo/indexer/geometry.h b/drivers/md/dm-vdo/indexer/geometry.h index a2ecdb238cf2..e311608d0de8 100644 --- a/drivers/md/dm-vdo/indexer/geometry.h +++ b/drivers/md/dm-vdo/indexer/geometry.h @@ -95,16 +95,9 @@ enum { HEADER_PAGES_PER_VOLUME = 1, }; -int __must_check uds_make_index_geometry(size_t bytes_per_page, u32 record_pages_per_chapter, - u32 chapters_per_volume, - u32 sparse_chapters_per_volume, u64 remapped_virtual, - u64 remapped_physical, - struct index_geometry **geometry_ptr); - -int __must_check uds_copy_index_geometry(struct index_geometry *source, - struct index_geometry **geometry_ptr); - -void uds_free_index_geometry(struct index_geometry *geometry); +struct index_geometry uds_init_index_geometry(size_t bytes_per_page, u32 record_pages_per_chapter, + u32 chapters_per_volume, u32 sparse_chapters_per_volume, + u64 remapped_virtual, u64 remapped_physical); u32 __must_check uds_map_to_physical_chapter(const struct index_geometry *geometry, u64 virtual_chapter); diff --git a/drivers/md/dm-vdo/indexer/index-layout.c b/drivers/md/dm-vdo/indexer/index-layout.c index 2d529250000e..f5b30f8dd2b5 100644 --- a/drivers/md/dm-vdo/indexer/index-layout.c +++ b/drivers/md/dm-vdo/indexer/index-layout.c @@ -222,7 +222,7 @@ static int __must_check compute_sizes(const struct uds_configuration *config, struct save_layout_sizes *sls) { int result; - struct index_geometry *geometry = config->geometry; + const struct index_geometry *geometry = &config->geometry; memset(sls, 0, sizeof(*sls)); sls->save_count = MAX_SAVES; diff --git a/drivers/md/dm-vdo/indexer/index.c b/drivers/md/dm-vdo/indexer/index.c index 793bd32c1179..af1ab6cf5fcd 100644 --- a/drivers/md/dm-vdo/indexer/index.c +++ b/drivers/md/dm-vdo/indexer/index.c @@ -77,7 +77,7 @@ struct chapter_writer { static bool is_zone_chapter_sparse(const struct index_zone *zone, u64 virtual_chapter) { - return uds_is_chapter_sparse(zone->index->volume->geometry, + return uds_is_chapter_sparse(&zone->index->volume->geometry, zone->oldest_virtual_chapter, zone->newest_virtual_chapter, virtual_chapter); } @@ -154,7 +154,7 @@ static int simulate_index_zone_barrier_message(struct index_zone *zone, u64 sparse_virtual_chapter; if ((zone->index->zone_count > 1) || - !uds_is_sparse_index_geometry(zone->index->volume->geometry)) + !uds_is_sparse_index_geometry(&zone->index->volume->geometry)) return UDS_SUCCESS; sparse_virtual_chapter = triage_index_request(zone->index, request); @@ -278,7 +278,7 @@ static int open_next_chapter(struct index_zone *zone) } expiring = zone->oldest_virtual_chapter; - expire_chapters = uds_chapters_to_expire(zone->index->volume->geometry, + expire_chapters = uds_chapters_to_expire(&zone->index->volume->geometry, zone->newest_virtual_chapter); zone->oldest_virtual_chapter += expire_chapters; @@ -353,7 +353,7 @@ static int search_sparse_cache_in_zone(struct index_zone *zone, struct uds_reque request->virtual_chapter = virtual_chapter; volume = zone->index->volume; - chapter = uds_map_to_physical_chapter(volume->geometry, virtual_chapter); + chapter = uds_map_to_physical_chapter(&volume->geometry, virtual_chapter); return uds_search_cached_record_page(volume, request, chapter, record_page_number, found); } @@ -470,7 +470,7 @@ static int search_index_zone(struct index_zone *zone, struct uds_request *reques found = true; } else if (request->location == UDS_LOCATION_UNAVAILABLE) { found = false; - } else if (uds_is_sparse_index_geometry(zone->index->volume->geometry) && + } else if (uds_is_sparse_index_geometry(&zone->index->volume->geometry) && !uds_is_volume_index_sample(zone->index->volume_index, &request->record_name)) { result = search_sparse_cache_in_zone(zone, request, NO_CHAPTER, @@ -720,7 +720,7 @@ static void close_chapters(void *arg) mutex_lock(&writer->mutex); index->newest_virtual_chapter++; index->oldest_virtual_chapter += - uds_chapters_to_expire(index->volume->geometry, + uds_chapters_to_expire(&index->volume->geometry, index->newest_virtual_chapter); writer->result = result; writer->zones_to_write = 0; @@ -762,7 +762,7 @@ static int make_chapter_writer(struct uds_index *index, int result; struct chapter_writer *writer; size_t collated_records_size = - (sizeof(struct uds_volume_record) * index->volume->geometry->records_per_chapter); + (sizeof(struct uds_volume_record) * index->volume->geometry.records_per_chapter); result = vdo_allocate_extended(index->zone_count, chapters, "Chapter Writer", &writer); if (result != VDO_SUCCESS) @@ -780,7 +780,7 @@ static int make_chapter_writer(struct uds_index *index, } result = uds_make_open_chapter_index(&writer->open_chapter_index, - index->volume->geometry, + &index->volume->geometry, index->volume->nonce); if (result != UDS_SUCCESS) { free_chapter_writer(writer); @@ -824,7 +824,7 @@ static int rebuild_index_page_map(struct uds_index *index, u64 vcn) { int result; struct delta_index_page *chapter_index_page; - struct index_geometry *geometry = index->volume->geometry; + struct index_geometry *geometry = &index->volume->geometry; u32 chapter = uds_map_to_physical_chapter(geometry, vcn); u32 expected_list_number = 0; u32 index_page_number; @@ -980,7 +980,7 @@ static int replay_chapter(struct uds_index *index, u64 virtual, bool sparse) return -EBUSY; } - geometry = index->volume->geometry; + geometry = &index->volume->geometry; physical_chapter = uds_map_to_physical_chapter(geometry, virtual); uds_prefetch_volume_chapter(index->volume, physical_chapter); uds_set_volume_index_open_chapter(index->volume_index, virtual); @@ -1046,7 +1046,7 @@ static int replay_volume(struct uds_index *index) */ old_map_update = index->volume->index_page_map->last_update; for (virtual = from_virtual; virtual < upto_virtual; virtual++) { - will_be_sparse = uds_is_chapter_sparse(index->volume->geometry, + will_be_sparse = uds_is_chapter_sparse(&index->volume->geometry, from_virtual, upto_virtual, virtual); result = replay_chapter(index, virtual, will_be_sparse); @@ -1073,7 +1073,7 @@ static int rebuild_index(struct uds_index *index) u64 lowest; u64 highest; bool is_empty = false; - u32 chapters_per_volume = index->volume->geometry->chapters_per_volume; + u32 chapters_per_volume = index->volume->geometry.chapters_per_volume; index->volume->lookup_mode = LOOKUP_FOR_REBUILD; result = uds_find_volume_chapter_boundaries(index->volume, &lowest, &highest, @@ -1125,14 +1125,14 @@ static int make_index_zone(struct uds_index *index, unsigned int zone_number) if (result != VDO_SUCCESS) return result; - result = uds_make_open_chapter(index->volume->geometry, index->zone_count, + result = uds_make_open_chapter(&index->volume->geometry, index->zone_count, &zone->open_chapter); if (result != UDS_SUCCESS) { free_index_zone(zone); return result; } - result = uds_make_open_chapter(index->volume->geometry, index->zone_count, + result = uds_make_open_chapter(&index->volume->geometry, index->zone_count, &zone->writing_chapter); if (result != UDS_SUCCESS) { free_index_zone(zone); @@ -1202,7 +1202,7 @@ int uds_make_index(struct uds_configuration *config, enum uds_open_index_type op index->load_context = load_context; index->callback = callback; - result = initialize_index_queues(index, config->geometry); + result = initialize_index_queues(index, &config->geometry); if (result != UDS_SUCCESS) { uds_free_index(index); return result; diff --git a/drivers/md/dm-vdo/indexer/open-chapter.c b/drivers/md/dm-vdo/indexer/open-chapter.c index 89b91c600bfd..2977305a2b8f 100644 --- a/drivers/md/dm-vdo/indexer/open-chapter.c +++ b/drivers/md/dm-vdo/indexer/open-chapter.c @@ -343,7 +343,7 @@ int uds_save_open_chapter(struct uds_index *index, struct buffered_writer *write return uds_flush_buffered_writer(writer); } -u64 uds_compute_saved_open_chapter_size(struct index_geometry *geometry) +u64 uds_compute_saved_open_chapter_size(const struct index_geometry *geometry) { unsigned int records_per_chapter = geometry->records_per_chapter; diff --git a/drivers/md/dm-vdo/indexer/open-chapter.h b/drivers/md/dm-vdo/indexer/open-chapter.h index ea6d7336aea0..72a776d46adc 100644 --- a/drivers/md/dm-vdo/indexer/open-chapter.h +++ b/drivers/md/dm-vdo/indexer/open-chapter.h @@ -74,6 +74,6 @@ int __must_check uds_save_open_chapter(struct uds_index *index, int __must_check uds_load_open_chapter(struct uds_index *index, struct buffered_reader *reader); -u64 uds_compute_saved_open_chapter_size(struct index_geometry *geometry); +u64 uds_compute_saved_open_chapter_size(const struct index_geometry *geometry); #endif /* UDS_OPEN_CHAPTER_H */ diff --git a/drivers/md/dm-vdo/indexer/volume-index.c b/drivers/md/dm-vdo/indexer/volume-index.c index e78d2725ce8b..2da4c492ace9 100644 --- a/drivers/md/dm-vdo/indexer/volume-index.c +++ b/drivers/md/dm-vdo/indexer/volume-index.c @@ -60,6 +60,12 @@ * the index. */ +enum sub_index_parameters_slot { + DENSE = 0, + HOOK = 0, + NON_HOOK = 1, +}; + struct sub_index_parameters { /* The number of bits in address mask */ u8 address_bits; @@ -77,16 +83,10 @@ struct sub_index_parameters { size_t memory_size; /* The number of bytes the index should keep free at all times */ size_t target_free_bytes; -}; - -struct split_config { - /* The hook subindex configuration */ - struct uds_configuration hook_config; - struct index_geometry hook_geometry; - - /* The non-hook subindex configuration */ - struct uds_configuration non_hook_config; - struct index_geometry non_hook_geometry; + /* The mean delta for the volume index */ + u32 volume_index_mean_delta; + /* The number of threads used to process index requests */ + unsigned int zone_count; }; struct chapter_range { @@ -196,8 +196,11 @@ unsigned int uds_get_volume_index_zone(const struct volume_index *volume_index, #define DELTA_LIST_SIZE 256 -static int compute_volume_sub_index_parameters(const struct uds_configuration *config, - struct sub_index_parameters *params) +static int compute_sub_index_parameters(const struct uds_configuration *config, + u64 records_per_chapter, + u32 chapters_per_volume, + bool reduced, + struct sub_index_parameters *params) { u64 entries_in_volume_index, address_span; u32 chapters_in_volume_index, invalid_chapters; @@ -207,17 +210,15 @@ static int compute_volume_sub_index_parameters(const struct uds_configuration *c u64 index_size_in_bits; size_t expected_index_size; u64 min_delta_lists = MAX_ZONES * MAX_ZONES; - struct index_geometry *geometry = config->geometry; - u64 records_per_chapter = geometry->records_per_chapter; - params->chapter_count = geometry->chapters_per_volume; + params->chapter_count = chapters_per_volume; /* * Make sure that the number of delta list records in the volume index does not change when * the volume is reduced by one chapter. This preserves the mapping from name to volume * index delta list. */ rounded_chapters = params->chapter_count; - if (uds_is_reduced_index_geometry(geometry)) + if (reduced) rounded_chapters += 1; delta_list_records = records_per_chapter * rounded_chapters; address_count = config->volume_index_mean_delta * DELTA_LIST_SIZE; @@ -274,9 +275,46 @@ static int compute_volume_sub_index_parameters(const struct uds_configuration *c params->memory_size = expected_index_size * 106 / 100; params->target_free_bytes = expected_index_size / 20; + params->volume_index_mean_delta = config->volume_index_mean_delta; + params->zone_count = config->zone_count; return UDS_SUCCESS; } +static int compute_volume_sub_index_parameters(const struct uds_configuration *config, + struct sub_index_parameters *params) +{ + const struct index_geometry *geometry = &config->geometry; + u64 sample_records; + u64 dense_chapters; + int result; + bool reduced = uds_is_reduced_index_geometry(geometry); + + if (!uds_is_sparse_index_geometry(&config->geometry)) { + return compute_sub_index_parameters(config, + geometry->records_per_chapter, + geometry->chapters_per_volume, + reduced, + ¶ms[DENSE]); + } + + dense_chapters = geometry->chapters_per_volume - geometry->sparse_chapters_per_volume; + sample_records = geometry->records_per_chapter / config->sparse_sample_rate; + + result = compute_sub_index_parameters(config, + sample_records, + geometry->chapters_per_volume, + reduced, + ¶ms[HOOK]); + if (result != UDS_SUCCESS) + return result; + + return compute_sub_index_parameters(config, + geometry->records_per_chapter - sample_records, + dense_chapters, + reduced, + ¶ms[NON_HOOK]); +} + static void uninitialize_volume_sub_index(struct volume_sub_index *sub_index) { vdo_free(vdo_forget(sub_index->flush_chapters)); @@ -298,73 +336,32 @@ void uds_free_volume_index(struct volume_index *volume_index) } -static int compute_volume_sub_index_save_bytes(const struct uds_configuration *config, - size_t *bytes) +static size_t compute_volume_sub_index_save_bytes(struct sub_index_parameters *params) { - struct sub_index_parameters params = { .address_bits = 0 }; - int result; - - result = compute_volume_sub_index_parameters(config, ¶ms); - if (result != UDS_SUCCESS) - return result; - - *bytes = (sizeof(struct sub_index_data) + params.list_count * sizeof(u64) + - uds_compute_delta_index_save_bytes(params.list_count, - params.memory_size)); - return UDS_SUCCESS; -} - -/* This function is only useful if the configuration includes sparse chapters. */ -static void split_configuration(const struct uds_configuration *config, - struct split_config *split) -{ - u64 sample_rate, sample_records; - u64 dense_chapters, sparse_chapters; - - /* Start with copies of the base configuration. */ - split->hook_config = *config; - split->hook_geometry = *config->geometry; - split->hook_config.geometry = &split->hook_geometry; - split->non_hook_config = *config; - split->non_hook_geometry = *config->geometry; - split->non_hook_config.geometry = &split->non_hook_geometry; - - sample_rate = config->sparse_sample_rate; - sparse_chapters = config->geometry->sparse_chapters_per_volume; - dense_chapters = config->geometry->chapters_per_volume - sparse_chapters; - sample_records = config->geometry->records_per_chapter / sample_rate; - - /* Adjust the number of records indexed for each chapter. */ - split->hook_geometry.records_per_chapter = sample_records; - split->non_hook_geometry.records_per_chapter -= sample_records; - - /* Adjust the number of chapters indexed. */ - split->hook_geometry.sparse_chapters_per_volume = 0; - split->non_hook_geometry.sparse_chapters_per_volume = 0; - split->non_hook_geometry.chapters_per_volume = dense_chapters; + return (sizeof(struct sub_index_data) + params->list_count * sizeof(u64) + + uds_compute_delta_index_save_bytes(params->list_count, + params->memory_size)); } static int compute_volume_index_save_bytes(const struct uds_configuration *config, size_t *bytes) { - size_t hook_bytes, non_hook_bytes; - struct split_config split; int result; + struct sub_index_parameters parameters[2] = { + { .address_bits = 0 }, + { .address_bits = 0 }, + }; - if (!uds_is_sparse_index_geometry(config->geometry)) - return compute_volume_sub_index_save_bytes(config, bytes); - - split_configuration(config, &split); - result = compute_volume_sub_index_save_bytes(&split.hook_config, &hook_bytes); + result = compute_volume_sub_index_parameters(config, parameters); if (result != UDS_SUCCESS) return result; - result = compute_volume_sub_index_save_bytes(&split.non_hook_config, - &non_hook_bytes); - if (result != UDS_SUCCESS) - return result; + *bytes = compute_volume_sub_index_save_bytes(¶meters[HOOK]); + if (uds_is_sparse_index_geometry(&config->geometry)) { + *bytes += compute_volume_sub_index_save_bytes(¶meters[NON_HOOK]); + *bytes += sizeof(struct volume_index_data); + } - *bytes = sizeof(struct volume_index_data) + hook_bytes + non_hook_bytes; return UDS_SUCCESS; } @@ -1170,48 +1167,43 @@ void uds_get_volume_index_stats(const struct volume_index *volume_index, stats->early_flushes += sparse_stats.early_flushes; } -static int initialize_volume_sub_index(const struct uds_configuration *config, +static int initialize_volume_sub_index(struct sub_index_parameters *params, u64 volume_nonce, u8 tag, struct volume_sub_index *sub_index) { - struct sub_index_parameters params = { .address_bits = 0 }; - unsigned int zone_count = config->zone_count; + unsigned int zone_count = params->zone_count; u64 available_bytes = 0; unsigned int z; int result; - result = compute_volume_sub_index_parameters(config, ¶ms); - if (result != UDS_SUCCESS) - return result; - - sub_index->address_bits = params.address_bits; - sub_index->address_mask = (1u << params.address_bits) - 1; - sub_index->chapter_bits = params.chapter_bits; - sub_index->chapter_mask = (1u << params.chapter_bits) - 1; - sub_index->chapter_count = params.chapter_count; - sub_index->list_count = params.list_count; + sub_index->address_bits = params->address_bits; + sub_index->address_mask = (1u << params->address_bits) - 1; + sub_index->chapter_bits = params->chapter_bits; + sub_index->chapter_mask = (1u << params->chapter_bits) - 1; + sub_index->chapter_count = params->chapter_count; + sub_index->list_count = params->list_count; sub_index->zone_count = zone_count; - sub_index->chapter_zone_bits = params.chapter_size_in_bits / zone_count; + sub_index->chapter_zone_bits = params->chapter_size_in_bits / zone_count; sub_index->volume_nonce = volume_nonce; result = uds_initialize_delta_index(&sub_index->delta_index, zone_count, - params.list_count, params.mean_delta, - params.chapter_bits, params.memory_size, + params->list_count, params->mean_delta, + params->chapter_bits, params->memory_size, tag); if (result != UDS_SUCCESS) return result; for (z = 0; z < sub_index->delta_index.zone_count; z++) available_bytes += sub_index->delta_index.delta_zones[z].size; - available_bytes -= params.target_free_bytes; + available_bytes -= params->target_free_bytes; sub_index->max_zone_bits = (available_bytes * BITS_PER_BYTE) / zone_count; sub_index->memory_size = (sub_index->delta_index.memory_size + sizeof(struct volume_sub_index) + - (params.list_count * sizeof(u64)) + + (params->list_count * sizeof(u64)) + (zone_count * sizeof(struct volume_sub_index_zone))); /* The following arrays are initialized to all zeros. */ - result = vdo_allocate(params.list_count, "first chapter to flush", + result = vdo_allocate(params->list_count, "first chapter to flush", &sub_index->flush_chapters); if (result != VDO_SUCCESS) return result; @@ -1222,10 +1214,13 @@ static int initialize_volume_sub_index(const struct uds_configuration *config, int uds_make_volume_index(const struct uds_configuration *config, u64 volume_nonce, struct volume_index **volume_index_ptr) { - struct split_config split; unsigned int zone; struct volume_index *volume_index; int result; + struct sub_index_parameters parameters[2] = { + { .address_bits = 0 }, + { .address_bits = 0 }, + }; result = vdo_allocate(1, "volume index", &volume_index); if (result != VDO_SUCCESS) @@ -1233,8 +1228,12 @@ int uds_make_volume_index(const struct uds_configuration *config, u64 volume_non volume_index->zone_count = config->zone_count; - if (!uds_is_sparse_index_geometry(config->geometry)) { - result = initialize_volume_sub_index(config, volume_nonce, 'm', + result = compute_volume_sub_index_parameters(config, parameters); + if (result != UDS_SUCCESS) + return result; + + if (!uds_is_sparse_index_geometry(&config->geometry)) { + result = initialize_volume_sub_index(¶meters[DENSE], volume_nonce, 'm', &volume_index->vi_non_hook); if (result != UDS_SUCCESS) { uds_free_volume_index(volume_index); @@ -1257,8 +1256,7 @@ int uds_make_volume_index(const struct uds_configuration *config, u64 volume_non for (zone = 0; zone < config->zone_count; zone++) mutex_init(&volume_index->zones[zone].hook_mutex); - split_configuration(config, &split); - result = initialize_volume_sub_index(&split.non_hook_config, volume_nonce, 'd', + result = initialize_volume_sub_index(¶meters[NON_HOOK], volume_nonce, 'd', &volume_index->vi_non_hook); if (result != UDS_SUCCESS) { uds_free_volume_index(volume_index); @@ -1266,7 +1264,7 @@ int uds_make_volume_index(const struct uds_configuration *config, u64 volume_non "Error creating non hook volume index"); } - result = initialize_volume_sub_index(&split.hook_config, volume_nonce, 's', + result = initialize_volume_sub_index(¶meters[HOOK], volume_nonce, 's', &volume_index->vi_hook); if (result != UDS_SUCCESS) { uds_free_volume_index(volume_index); diff --git a/drivers/md/dm-vdo/indexer/volume.c b/drivers/md/dm-vdo/indexer/volume.c index af97c0cbeede..78c3729f1f65 100644 --- a/drivers/md/dm-vdo/indexer/volume.c +++ b/drivers/md/dm-vdo/indexer/volume.c @@ -83,17 +83,17 @@ union invalidate_counter { }; }; -static inline u32 map_to_page_number(struct index_geometry *geometry, u32 physical_page) +static inline u32 map_to_page_number(const struct index_geometry *geometry, u32 physical_page) { return (physical_page - HEADER_PAGES_PER_VOLUME) % geometry->pages_per_chapter; } -static inline u32 map_to_chapter_number(struct index_geometry *geometry, u32 physical_page) +static inline u32 map_to_chapter_number(const struct index_geometry *geometry, u32 physical_page) { return (physical_page - HEADER_PAGES_PER_VOLUME) / geometry->pages_per_chapter; } -static inline bool is_record_page(struct index_geometry *geometry, u32 physical_page) +static inline bool is_record_page(const struct index_geometry *geometry, u32 physical_page) { return map_to_page_number(geometry, physical_page) >= geometry->index_pages_per_chapter; } @@ -422,7 +422,7 @@ static int init_chapter_index_page(const struct volume *volume, u8 *index_page, u32 ci_chapter; u32 lowest_list; u32 highest_list; - struct index_geometry *geometry = volume->geometry; + const struct index_geometry *geometry = &volume->geometry; int result; result = uds_initialize_chapter_index_page(chapter_index_page, geometry, @@ -459,8 +459,8 @@ static int init_chapter_index_page(const struct volume *volume, u8 *index_page, static int initialize_index_page(const struct volume *volume, u32 physical_page, struct cached_page *page) { - u32 chapter = map_to_chapter_number(volume->geometry, physical_page); - u32 index_page_number = map_to_page_number(volume->geometry, physical_page); + u32 chapter = map_to_chapter_number(&volume->geometry, physical_page); + u32 index_page_number = map_to_page_number(&volume->geometry, physical_page); return init_chapter_index_page(volume, dm_bufio_get_block_data(page->buffer), chapter, index_page_number, &page->index_page); @@ -510,16 +510,16 @@ static int search_page(struct cached_page *page, const struct volume *volume, enum uds_index_region location; u16 record_page_number; - if (is_record_page(volume->geometry, physical_page)) { + if (is_record_page(&volume->geometry, physical_page)) { if (search_record_page(dm_bufio_get_block_data(page->buffer), - &request->record_name, volume->geometry, + &request->record_name, &volume->geometry, &request->old_metadata)) location = UDS_LOCATION_RECORD_PAGE_LOOKUP; else location = UDS_LOCATION_UNAVAILABLE; } else { result = uds_search_chapter_index_page(&page->index_page, - volume->geometry, + &volume->geometry, &request->record_name, &record_page_number); if (result != UDS_SUCCESS) @@ -571,7 +571,7 @@ static int process_entry(struct volume *volume, struct queued_read *entry) return UDS_SUCCESS; } - if (!is_record_page(volume->geometry, page_number)) { + if (!is_record_page(&volume->geometry, page_number)) { result = initialize_index_page(volume, page_number, page); if (result != UDS_SUCCESS) { vdo_log_warning("Error initializing chapter index page"); @@ -708,7 +708,7 @@ static int read_page_locked(struct volume *volume, u32 physical_page, return result; } - if (!is_record_page(volume->geometry, physical_page)) { + if (!is_record_page(&volume->geometry, physical_page)) { result = initialize_index_page(volume, physical_page, page); if (result != UDS_SUCCESS) { if (volume->lookup_mode != LOOKUP_FOR_REBUILD) @@ -807,7 +807,7 @@ static int get_volume_page(struct volume *volume, u32 chapter, u32 page_number, struct cached_page **page_ptr) { int result; - u32 physical_page = map_to_physical_page(volume->geometry, chapter, page_number); + u32 physical_page = map_to_physical_page(&volume->geometry, chapter, page_number); mutex_lock(&volume->read_threads_mutex); result = get_volume_page_locked(volume, physical_page, page_ptr); @@ -850,7 +850,7 @@ static int search_cached_index_page(struct volume *volume, struct uds_request *r int result; struct cached_page *page = NULL; unsigned int zone_number = request->zone_number; - u32 physical_page = map_to_physical_page(volume->geometry, chapter, + u32 physical_page = map_to_physical_page(&volume->geometry, chapter, index_page_number); /* @@ -867,7 +867,7 @@ static int search_cached_index_page(struct volume *volume, struct uds_request *r return result; } - result = uds_search_chapter_index_page(&page->index_page, volume->geometry, + result = uds_search_chapter_index_page(&page->index_page, &volume->geometry, &request->record_name, record_page_number); end_pending_search(&volume->page_cache, zone_number); @@ -882,7 +882,7 @@ int uds_search_cached_record_page(struct volume *volume, struct uds_request *req u32 chapter, u16 record_page_number, bool *found) { struct cached_page *record_page; - struct index_geometry *geometry = volume->geometry; + const struct index_geometry *geometry = &volume->geometry; unsigned int zone_number = request->zone_number; int result; u32 physical_page, page_number; @@ -899,7 +899,7 @@ int uds_search_cached_record_page(struct volume *volume, struct uds_request *req page_number = geometry->index_pages_per_chapter + record_page_number; - physical_page = map_to_physical_page(volume->geometry, chapter, page_number); + physical_page = map_to_physical_page(&volume->geometry, chapter, page_number); /* * Make sure the invalidate counter is updated before we try and read the mapping. This @@ -925,7 +925,7 @@ int uds_search_cached_record_page(struct volume *volume, struct uds_request *req void uds_prefetch_volume_chapter(const struct volume *volume, u32 chapter) { - const struct index_geometry *geometry = volume->geometry; + const struct index_geometry *geometry = &volume->geometry; u32 physical_page = map_to_physical_page(geometry, chapter, 0); dm_bufio_prefetch(volume->client, physical_page, geometry->pages_per_chapter); @@ -937,7 +937,7 @@ int uds_read_chapter_index_from_volume(const struct volume *volume, u64 virtual_ { int result; u32 i; - const struct index_geometry *geometry = volume->geometry; + const struct index_geometry *geometry = &volume->geometry; u32 physical_chapter = uds_map_to_physical_chapter(geometry, virtual_chapter); u32 physical_page = map_to_physical_page(geometry, physical_chapter, 0); @@ -969,7 +969,7 @@ int uds_search_volume_page_cache(struct volume *volume, struct uds_request *requ { int result; u32 physical_chapter = - uds_map_to_physical_chapter(volume->geometry, request->virtual_chapter); + uds_map_to_physical_chapter(&volume->geometry, request->virtual_chapter); u32 index_page_number; u16 record_page_number; @@ -996,7 +996,7 @@ int uds_search_volume_page_cache_for_rebuild(struct volume *volume, u64 virtual_chapter, bool *found) { int result; - struct index_geometry *geometry = volume->geometry; + struct index_geometry *geometry = &volume->geometry; struct cached_page *page; u32 physical_chapter = uds_map_to_physical_chapter(geometry, virtual_chapter); u32 index_page_number; @@ -1049,13 +1049,13 @@ static void invalidate_page(struct page_cache *cache, u32 physical_page) void uds_forget_chapter(struct volume *volume, u64 virtual_chapter) { u32 physical_chapter = - uds_map_to_physical_chapter(volume->geometry, virtual_chapter); - u32 first_page = map_to_physical_page(volume->geometry, physical_chapter, 0); + uds_map_to_physical_chapter(&volume->geometry, virtual_chapter); + u32 first_page = map_to_physical_page(&volume->geometry, physical_chapter, 0); u32 i; vdo_log_debug("forgetting chapter %llu", (unsigned long long) virtual_chapter); mutex_lock(&volume->read_threads_mutex); - for (i = 0; i < volume->geometry->pages_per_chapter; i++) + for (i = 0; i < volume->geometry.pages_per_chapter; i++) invalidate_page(&volume->page_cache, first_page + i); mutex_unlock(&volume->read_threads_mutex); } @@ -1070,7 +1070,7 @@ static int donate_index_page_locked(struct volume *volume, u32 physical_chapter, int result; struct cached_page *page = NULL; u32 physical_page = - map_to_physical_page(volume->geometry, physical_chapter, + map_to_physical_page(&volume->geometry, physical_chapter, index_page_number); page = select_victim_in_cache(&volume->page_cache); @@ -1097,7 +1097,7 @@ static int donate_index_page_locked(struct volume *volume, u32 physical_chapter, static int write_index_pages(struct volume *volume, u32 physical_chapter_number, struct open_chapter_index *chapter_index) { - struct index_geometry *geometry = volume->geometry; + struct index_geometry *geometry = &volume->geometry; struct dm_buffer *page_buffer; u32 first_index_page = map_to_physical_page(geometry, physical_chapter_number, 0); u32 delta_list_number = 0; @@ -1184,7 +1184,7 @@ static int encode_record_page(const struct volume *volume, { int result; u32 i; - u32 records_per_page = volume->geometry->records_per_page; + u32 records_per_page = volume->geometry.records_per_page; const struct uds_volume_record **record_pointers = volume->record_pointers; for (i = 0; i < records_per_page; i++) @@ -1208,7 +1208,7 @@ static int write_record_pages(struct volume *volume, u32 physical_chapter_number const struct uds_volume_record *records) { u32 record_page_number; - struct index_geometry *geometry = volume->geometry; + struct index_geometry *geometry = &volume->geometry; struct dm_buffer *page_buffer; const struct uds_volume_record *next_record = records; u32 first_record_page = map_to_physical_page(geometry, physical_chapter_number, @@ -1248,7 +1248,7 @@ int uds_write_chapter(struct volume *volume, struct open_chapter_index *chapter_ { int result; u32 physical_chapter_number = - uds_map_to_physical_chapter(volume->geometry, + uds_map_to_physical_chapter(&volume->geometry, chapter_index->virtual_chapter_number); result = write_index_pages(volume, physical_chapter_number, chapter_index); @@ -1269,7 +1269,7 @@ int uds_write_chapter(struct volume *volume, struct open_chapter_index *chapter_ static void probe_chapter(struct volume *volume, u32 chapter_number, u64 *virtual_chapter_number) { - const struct index_geometry *geometry = volume->geometry; + const struct index_geometry *geometry = &volume->geometry; u32 expected_list_number = 0; u32 i; u64 vcn = BAD_CHAPTER; @@ -1353,7 +1353,7 @@ static void find_real_end_of_volume(struct volume *volume, u32 limit, u32 *limit static int find_chapter_limits(struct volume *volume, u32 chapter_limit, u64 *lowest_vcn, u64 *highest_vcn) { - struct index_geometry *geometry = volume->geometry; + struct index_geometry *geometry = &volume->geometry; u64 zero_vcn; u64 lowest = BAD_CHAPTER; u64 highest = BAD_CHAPTER; @@ -1451,7 +1451,7 @@ static int find_chapter_limits(struct volume *volume, u32 chapter_limit, u64 *lo int uds_find_volume_chapter_boundaries(struct volume *volume, u64 *lowest_vcn, u64 *highest_vcn, bool *is_empty) { - u32 chapter_limit = volume->geometry->chapters_per_volume; + u32 chapter_limit = volume->geometry.chapters_per_volume; find_real_end_of_volume(volume, chapter_limit, &chapter_limit); if (chapter_limit == 0) { @@ -1486,7 +1486,7 @@ int __must_check uds_replace_volume_storage(struct volume *volume, if (volume->client != NULL) dm_bufio_client_destroy(vdo_forget(volume->client)); - return uds_open_volume_bufio(layout, volume->geometry->bytes_per_page, + return uds_open_volume_bufio(layout, volume->geometry.bytes_per_page, volume->reserved_buffers, &volume->client); } @@ -1552,13 +1552,8 @@ int uds_make_volume(const struct uds_configuration *config, struct index_layout volume->nonce = uds_get_volume_nonce(layout); - result = uds_copy_index_geometry(config->geometry, &volume->geometry); - if (result != UDS_SUCCESS) { - uds_free_volume(volume); - return vdo_log_warning_strerror(result, - "failed to allocate geometry: error"); - } - geometry = volume->geometry; + volume->geometry = config->geometry; + geometry = &volume->geometry; /* * Reserve a buffer for each entry in the page cache, one for the chapter writer, and one @@ -1685,7 +1680,6 @@ void uds_free_volume(struct volume *volume) uds_free_index_page_map(volume->index_page_map); uds_free_radix_sorter(volume->radix_sorter); - vdo_free(volume->geometry); vdo_free(volume->record_pointers); vdo_free(volume); } diff --git a/drivers/md/dm-vdo/indexer/volume.h b/drivers/md/dm-vdo/indexer/volume.h index 8679a5e55347..0cba3146688d 100644 --- a/drivers/md/dm-vdo/indexer/volume.h +++ b/drivers/md/dm-vdo/indexer/volume.h @@ -97,7 +97,7 @@ struct page_cache { }; struct volume { - struct index_geometry *geometry; + struct index_geometry geometry; struct dm_bufio_client *client; u64 nonce; size_t cache_size; diff --git a/drivers/md/dm-vdo/repair.c b/drivers/md/dm-vdo/repair.c index bfed62260280..e5263a5716f6 100644 --- a/drivers/md/dm-vdo/repair.c +++ b/drivers/md/dm-vdo/repair.c @@ -1696,6 +1696,7 @@ void vdo_repair(struct vdo_completion *parent) struct vdo *vdo = parent->vdo; struct recovery_journal *journal = vdo->recovery_journal; physical_block_number_t pbn = journal->origin; + block_count_t i; block_count_t remaining = journal->size; block_count_t vio_count = DIV_ROUND_UP(remaining, MAX_BLOCKS_PER_VIO); page_count_t page_count = min_t(page_count_t, @@ -1749,9 +1750,8 @@ void vdo_repair(struct vdo_completion *parent) remaining -= blocks; } - for (vio_count = 0; vio_count < repair->vio_count; - vio_count++, pbn += MAX_BLOCKS_PER_VIO) { - vdo_submit_metadata_vio(&repair->vios[vio_count], pbn, read_journal_endio, + for (i = 0; i < vio_count; i++, pbn += MAX_BLOCKS_PER_VIO) { + vdo_submit_metadata_vio(&repair->vios[i], pbn, read_journal_endio, handle_journal_load_error, REQ_OP_READ); } } diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 1b0763091254..cb105ccc47ec 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -316,9 +316,7 @@ static int verity_verify_level(struct dm_verity *v, struct dm_verity_io *io, else if (verity_handle_err(v, DM_VERITY_BLOCK_TYPE_METADATA, hash_block)) { - struct bio *bio; io->had_mismatch = true; - bio = dm_bio_from_per_bio_data(io, v->ti->per_io_data_size); dm_audit_log_bio(DM_MSG_PREFIX, "verify-metadata", bio, block, 0); r = -EIO; @@ -395,7 +393,7 @@ static noinline int verity_recheck(struct dm_verity *v, struct dm_verity_io *io, io_loc.bdev = v->data_dev->bdev; io_loc.sector = cur_block << (v->data_dev_block_bits - SECTOR_SHIFT); io_loc.count = 1 << (v->data_dev_block_bits - SECTOR_SHIFT); - r = dm_io(&io_req, 1, &io_loc, NULL, IOPRIO_DEFAULT); + r = dm_io(&io_req, 1, &io_loc, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) goto free_ret; diff --git a/drivers/md/dm-writecache.c b/drivers/md/dm-writecache.c index 302b60793da2..978646f71f99 100644 --- a/drivers/md/dm-writecache.c +++ b/drivers/md/dm-writecache.c @@ -474,12 +474,14 @@ struct io_notify { atomic_t count; }; -static void writecache_notify_io(unsigned long error, void *context) +static void writecache_notify_io(unsigned long error, unsigned long unsup, void *context) { struct io_notify *endio = context; if (unlikely(error != 0)) writecache_error(endio->wc, -EIO, "error writing metadata"); + else if (unlikely(unsup != 0)) + writecache_error(endio->wc, -EOPNOTSUPP, "error writing metadata"); BUG_ON(atomic_read(&endio->count) <= 0); if (atomic_dec_and_test(&endio->count)) complete(&endio->c); @@ -530,11 +532,11 @@ static void ssd_commit_flushed(struct dm_writecache *wc, bool wait_for_ios) req.notify.context = &endio; /* writing via async dm-io (implied by notify.fn above) won't return an error */ - (void) dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + (void) dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); i = j; } - writecache_notify_io(0, &endio); + writecache_notify_io(0, 0, &endio); wait_for_completion_io(&endio.c); if (wait_for_ios) @@ -567,7 +569,7 @@ static void ssd_commit_superblock(struct dm_writecache *wc) req.notify.fn = NULL; req.notify.context = NULL; - r = dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + r = dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) writecache_error(wc, r, "error writing superblock"); } @@ -595,7 +597,7 @@ static void writecache_disk_flush(struct dm_writecache *wc, struct dm_dev *dev) req.client = wc->dm_io; req.notify.fn = NULL; - r = dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + r = dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); if (unlikely(r)) writecache_error(wc, r, "error flushing metadata: %d", r); } @@ -989,7 +991,7 @@ static int writecache_read_metadata(struct dm_writecache *wc, sector_t n_sectors req.client = wc->dm_io; req.notify.fn = NULL; - return dm_io(&req, 1, ®ion, NULL, IOPRIO_DEFAULT); + return dm_io(&req, 1, ®ion, NULL, NULL, IOPRIO_DEFAULT); } static void writecache_resume(struct dm_target *ti) diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index f4f81c79a080..094b63215882 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -519,9 +519,7 @@ static void dmz_mblock_bio_end_io(struct bio *bio) else flag = DMZ_META_READING; - clear_bit_unlock(flag, &mblk->state); - smp_mb__after_atomic(); - wake_up_bit(&mblk->state, flag); + clear_and_wake_up_bit(flag, &mblk->state); bio_put(bio); } @@ -1910,9 +1908,7 @@ void dmz_unlock_zone_reclaim(struct dm_zone *zone) WARN_ON(dmz_is_active(zone)); WARN_ON(!dmz_in_reclaim(zone)); - clear_bit_unlock(DMZ_RECLAIM, &zone->flags); - smp_mb__after_atomic(); - wake_up_bit(&zone->flags, DMZ_RECLAIM); + clear_and_wake_up_bit(DMZ_RECLAIM, &zone->flags); } /* diff --git a/drivers/md/dm-zoned-reclaim.c b/drivers/md/dm-zoned-reclaim.c index ad9c7bc21d54..c041413c729e 100644 --- a/drivers/md/dm-zoned-reclaim.c +++ b/drivers/md/dm-zoned-reclaim.c @@ -106,9 +106,7 @@ static void dmz_reclaim_kcopy_end(int read_err, unsigned long write_err, else zrc->kc_err = 0; - clear_bit_unlock(DMZ_RECLAIM_KCOPY, &zrc->flags); - smp_mb__after_atomic(); - wake_up_bit(&zrc->flags, DMZ_RECLAIM_KCOPY); + clear_and_wake_up_bit(DMZ_RECLAIM_KCOPY, &zrc->flags); } /* diff --git a/drivers/md/dm.c b/drivers/md/dm.c index d413bfaf3527..eb9e32995b79 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2630,9 +2630,10 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t) */ mutex_lock(&md->table_devices_lock); r = add_disk(md->disk); - mutex_unlock(&md->table_devices_lock); - if (r) + if (r) { + mutex_unlock(&md->table_devices_lock); return r; + } /* * Register the holder relationship for devices added before the disk @@ -2643,18 +2644,21 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t) if (r) goto out_undo_holders; } + mutex_unlock(&md->table_devices_lock); r = dm_sysfs_init(md); if (r) - goto out_undo_holders; + goto lock_out_undo_holders; md->type = type; + return 0; +lock_out_undo_holders: + mutex_lock(&md->table_devices_lock); out_undo_holders: list_for_each_entry_continue_reverse(td, &md->table_devices, list) bd_unlink_disk_holder(td->dm_dev.bdev, md->disk); - mutex_lock(&md->table_devices_lock); del_gendisk(md->disk); mutex_unlock(&md->table_devices_lock); return r; @@ -3140,7 +3144,7 @@ retry: r = -EINVAL; mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING); - if (!dm_suspended_md(md)) + if (!dm_suspended_md(md) || test_bit(DMF_FREEING, &md->flags)) goto out; if (dm_suspended_internally_md(md)) { diff --git a/drivers/md/persistent-data/dm-array.c b/drivers/md/persistent-data/dm-array.c index 8f8792e55806..961fa3c1439a 100644 --- a/drivers/md/persistent-data/dm-array.c +++ b/drivers/md/persistent-data/dm-array.c @@ -38,6 +38,14 @@ struct array_block { */ #define CSUM_XOR 595846735 +/* + * Each array block can hold this many values. + */ +static uint32_t calc_max_entries(size_t value_size, size_t size_of_block) +{ + return (size_of_block - sizeof(struct array_block)) / value_size; +} + static void array_block_prepare_for_write(const struct dm_block_validator *v, struct dm_block *b, size_t size_of_block) @@ -55,6 +63,7 @@ static int array_block_check(const struct dm_block_validator *v, size_t size_of_block) { struct array_block *bh_le = dm_block_data(b); + uint32_t nr_entries, max_entries, value_size; __le32 csum_disk; if (dm_block_location(b) != le64_to_cpu(bh_le->blocknr)) { @@ -74,6 +83,26 @@ static int array_block_check(const struct dm_block_validator *v, return -EILSEQ; } + nr_entries = le32_to_cpu(bh_le->nr_entries); + max_entries = le32_to_cpu(bh_le->max_entries); + value_size = le32_to_cpu(bh_le->value_size); + + if (!value_size) { + DMERR_LIMIT("%s failed: value_size is zero", __func__); + return -EILSEQ; + } + + if (max_entries != calc_max_entries(value_size, size_of_block)) { + DMERR_LIMIT("%s failed: max_entries %u invalid for value_size %u", + __func__, max_entries, value_size); + return -EILSEQ; + } + + if (nr_entries > max_entries) { + DMERR_LIMIT("%s failed: too many entries", __func__); + return -EILSEQ; + } + return 0; } @@ -139,14 +168,6 @@ static void dec_ablock_entries(struct dm_array_info *info, struct array_block *a } /* - * Each array block can hold this many values. - */ -static uint32_t calc_max_entries(size_t value_size, size_t size_of_block) -{ - return (size_of_block - sizeof(struct array_block)) / value_size; -} - -/* * Allocate a new array block. The caller will need to unlock block. */ static int alloc_ablock(struct dm_array_info *info, size_t size_of_block, @@ -225,6 +246,14 @@ static int get_ablock(struct dm_array_info *info, dm_block_t b, return r; *ab = dm_block_data(*block); + if (le32_to_cpu((*ab)->value_size) != info->value_type.size) { + DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__, + le32_to_cpu((*ab)->value_size), + info->value_type.size); + dm_tm_unlock(info->btree_info.tm, *block); + return -EILSEQ; + } + return 0; } @@ -287,6 +316,14 @@ static int __shadow_ablock(struct dm_array_info *info, dm_block_t b, return r; *ab = dm_block_data(*block); + if (le32_to_cpu((*ab)->value_size) != info->value_type.size) { + DMERR_LIMIT("%s failed: value_size %u != wanted %u", __func__, + le32_to_cpu((*ab)->value_size), + info->value_type.size); + dm_tm_unlock(info->btree_info.tm, *block); + return -EILSEQ; + } + if (inc) inc_ablock_entries(info, *ab); diff --git a/include/linux/dm-io.h b/include/linux/dm-io.h index 7b2968612b7e..674683894064 100644 --- a/include/linux/dm-io.h +++ b/include/linux/dm-io.h @@ -27,7 +27,7 @@ struct page_list { struct page *page; }; -typedef void (*io_notify_fn)(unsigned int long error, void *context); +typedef void (*io_notify_fn)(unsigned long int error, unsigned long int unsup, void *context); enum dm_io_mem_type { DM_IO_PAGE_LIST,/* Page list */ @@ -80,8 +80,8 @@ void dm_io_client_destroy(struct dm_io_client *client); * error occurred doing io to the corresponding region. */ int dm_io(struct dm_io_request *io_req, unsigned int num_regions, - struct dm_io_region *region, unsigned int long *sync_error_bits, - unsigned short ioprio); + struct dm_io_region *region, unsigned long int *sync_error_bits, + unsigned long int *sync_unsup_bits, unsigned short ioprio); #endif /* __KERNEL__ */ #endif /* _LINUX_DM_IO_H */ |
