Commit 0364249d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

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

Pull device mapper updates from Mike Snitzer:

 - Update DM core to directly call the map function for both the linear
   and stripe targets; which are provided by DM core

 - Various updates to use new safer string functions

 - Update DM core to respect REQ_NOWAIT flag in normal bios so that
   memory allocations are always attempted with GFP_NOWAIT

 - Add Mikulas Patocka to MAINTAINERS as a DM maintainer!

 - Improve DM delay target's handling of short delays (< 50ms) by using
   a kthread to check expiration of IOs rather than timers and a wq

 - Update the DM error target so that it works with zoned storage. This
   helps xfstests to provide proper IO error handling coverage when
   testing a filesystem with native zoned storage support

 - Update both DM crypt and integrity targets to improve performance by
   using crypto_shash_digest() rather than init+update+final sequence

 - Fix DM crypt target by backfilling missing memory allocation
   accounting for compound pages

* tag 'for-6.7/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm crypt: account large pages in cc->n_allocated_pages
  dm integrity: use crypto_shash_digest() in sb_mac()
  dm crypt: use crypto_shash_digest() in crypt_iv_tcw_whitening()
  dm error: Add support for zoned block devices
  dm delay: for short delays, use kthread instead of timers and wq
  MAINTAINERS: add Mikulas Patocka as a DM maintainer
  dm: respect REQ_NOWAIT flag in normal bios issued to DM
  dm: enhance alloc_multiple_bios() to be more versatile
  dm: make __send_duplicate_bios return unsigned int
  dm log userspace: replace deprecated strncpy with strscpy
  dm ioctl: replace deprecated strncpy with strscpy_pad
  dm crypt: replace open-coded kmemdup_nul
  dm cache metadata: replace deprecated strncpy with strscpy
  dm: shortcut the calls to linear_map and stripe_map
parents 39714efc 9793c269
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6028,6 +6028,7 @@ F: include/linux/devm-helpers.h
DEVICE-MAPPER  (LVM)
M:	Alasdair Kergon <agk@redhat.com>
M:	Mike Snitzer <snitzer@kernel.org>
M:	Mikulas Patocka <mpatocka@redhat.com>
M:	dm-devel@lists.linux.dev
L:	dm-devel@lists.linux.dev
S:	Maintained
+3 −3
Original line number Diff line number Diff line
@@ -597,7 +597,7 @@ static void read_superblock_fields(struct dm_cache_metadata *cmd,
	cmd->discard_nr_blocks = to_dblock(le64_to_cpu(disk_super->discard_nr_blocks));
	cmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
	cmd->cache_blocks = to_cblock(le32_to_cpu(disk_super->cache_blocks));
	strncpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name));
	strscpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name));
	cmd->policy_version[0] = le32_to_cpu(disk_super->policy_version[0]);
	cmd->policy_version[1] = le32_to_cpu(disk_super->policy_version[1]);
	cmd->policy_version[2] = le32_to_cpu(disk_super->policy_version[2]);
@@ -707,7 +707,7 @@ static int __commit_transaction(struct dm_cache_metadata *cmd,
	disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
	disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
	disk_super->cache_blocks = cpu_to_le32(from_cblock(cmd->cache_blocks));
	strncpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name));
	strscpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name));
	disk_super->policy_version[0] = cpu_to_le32(cmd->policy_version[0]);
	disk_super->policy_version[1] = cpu_to_le32(cmd->policy_version[1]);
	disk_super->policy_version[2] = cpu_to_le32(cmd->policy_version[2]);
@@ -1726,7 +1726,7 @@ static int write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *po
	    (strlen(policy_name) > sizeof(cmd->policy_name) - 1))
		return -EINVAL;

	strncpy(cmd->policy_name, policy_name, sizeof(cmd->policy_name));
	strscpy(cmd->policy_name, policy_name, sizeof(cmd->policy_name));
	memcpy(cmd->policy_version, policy_version, sizeof(cmd->policy_version));

	hint_size = dm_cache_policy_get_hint_size(policy);
+14 −12
Original line number Diff line number Diff line
@@ -652,13 +652,7 @@ static int crypt_iv_tcw_whitening(struct crypt_config *cc,
	/* calculate crc32 for every 32bit part and xor it */
	desc->tfm = tcw->crc32_tfm;
	for (i = 0; i < 4; i++) {
		r = crypto_shash_init(desc);
		if (r)
			goto out;
		r = crypto_shash_update(desc, &buf[i * 4], 4);
		if (r)
			goto out;
		r = crypto_shash_final(desc, &buf[i * 4]);
		r = crypto_shash_digest(desc, &buf[i * 4], 4, &buf[i * 4]);
		if (r)
			goto out;
	}
@@ -1699,11 +1693,17 @@ static struct bio *crypt_alloc_buffer(struct dm_crypt_io *io, unsigned int size)
		order = min(order, remaining_order);

		while (order > 0) {
			if (unlikely(percpu_counter_read_positive(&cc->n_allocated_pages) +
					(1 << order) > dm_crypt_pages_per_client))
				goto decrease_order;
			pages = alloc_pages(gfp_mask
				| __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN | __GFP_COMP,
				order);
			if (likely(pages != NULL))
			if (likely(pages != NULL)) {
				percpu_counter_add(&cc->n_allocated_pages, 1 << order);
				goto have_pages;
			}
decrease_order:
			order--;
		}

@@ -1741,13 +1741,16 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone)

	if (clone->bi_vcnt > 0) { /* bio_for_each_folio_all crashes with an empty bio */
		bio_for_each_folio_all(fi, clone) {
			if (folio_test_large(fi.folio))
			if (folio_test_large(fi.folio)) {
				percpu_counter_sub(&cc->n_allocated_pages,
						1 << folio_order(fi.folio));
				folio_put(fi.folio);
			else
			} else {
				mempool_free(&fi.folio->page, &cc->page_pool);
			}
		}
	}
}

static void crypt_io_init(struct dm_crypt_io *io, struct crypt_config *cc,
			  struct bio *bio, sector_t sector)
@@ -2859,10 +2862,9 @@ static int crypt_ctr_auth_cipher(struct crypt_config *cc, char *cipher_api)
	if (!start || !end || ++start > end)
		return -EINVAL;

	mac_alg = kzalloc(end - start + 1, GFP_KERNEL);
	mac_alg = kmemdup_nul(start, end - start, GFP_KERNEL);
	if (!mac_alg)
		return -ENOMEM;
	strncpy(mac_alg, start, end - start);

	mac = crypto_alloc_ahash(mac_alg, 0, CRYPTO_ALG_ALLOCATES_MEMORY);
	kfree(mac_alg);
+88 −15
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/kthread.h>

#include <linux/device-mapper.h>

@@ -31,6 +32,7 @@ struct delay_c {
	struct workqueue_struct *kdelayd_wq;
	struct work_struct flush_expired_bios;
	struct list_head delayed_bios;
	struct task_struct *worker;
	atomic_t may_delay;

	struct delay_class read;
@@ -66,6 +68,44 @@ static void queue_timeout(struct delay_c *dc, unsigned long expires)
	mutex_unlock(&dc->timer_lock);
}

static inline bool delay_is_fast(struct delay_c *dc)
{
	return !!dc->worker;
}

static void flush_delayed_bios_fast(struct delay_c *dc, bool flush_all)
{
	struct dm_delay_info *delayed, *next;

	mutex_lock(&delayed_bios_lock);
	list_for_each_entry_safe(delayed, next, &dc->delayed_bios, list) {
		if (flush_all || time_after_eq(jiffies, delayed->expires)) {
			struct bio *bio = dm_bio_from_per_bio_data(delayed,
						sizeof(struct dm_delay_info));
			list_del(&delayed->list);
			dm_submit_bio_remap(bio, NULL);
			delayed->class->ops--;
		}
	}
	mutex_unlock(&delayed_bios_lock);
}

static int flush_worker_fn(void *data)
{
	struct delay_c *dc = data;

	while (1) {
		flush_delayed_bios_fast(dc, false);
		if (unlikely(list_empty(&dc->delayed_bios))) {
			set_current_state(TASK_INTERRUPTIBLE);
			schedule();
		} else
			cond_resched();
	}

	return 0;
}

static void flush_bios(struct bio *bio)
{
	struct bio *n;
@@ -78,7 +118,7 @@ static void flush_bios(struct bio *bio)
	}
}

static struct bio *flush_delayed_bios(struct delay_c *dc, int flush_all)
static struct bio *flush_delayed_bios(struct delay_c *dc, bool flush_all)
{
	struct dm_delay_info *delayed, *next;
	unsigned long next_expires = 0;
@@ -115,7 +155,10 @@ static void flush_expired_bios(struct work_struct *work)
	struct delay_c *dc;

	dc = container_of(work, struct delay_c, flush_expired_bios);
	flush_bios(flush_delayed_bios(dc, 0));
	if (delay_is_fast(dc))
		flush_delayed_bios_fast(dc, false);
	else
		flush_bios(flush_delayed_bios(dc, false));
}

static void delay_dtr(struct dm_target *ti)
@@ -131,7 +174,10 @@ static void delay_dtr(struct dm_target *ti)
		dm_put_device(ti, dc->write.dev);
	if (dc->flush.dev)
		dm_put_device(ti, dc->flush.dev);
	if (dc->worker)
		kthread_stop(dc->worker);

	if (!delay_is_fast(dc))
		mutex_destroy(&dc->timer_lock);

	kfree(dc);
@@ -175,6 +221,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
	struct delay_c *dc;
	int ret;
	unsigned int max_delay;

	if (argc != 3 && argc != 6 && argc != 9) {
		ti->error = "Requires exactly 3, 6 or 9 arguments";
@@ -188,16 +235,14 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
	}

	ti->private = dc;
	timer_setup(&dc->delay_timer, handle_delayed_timer, 0);
	INIT_WORK(&dc->flush_expired_bios, flush_expired_bios);
	INIT_LIST_HEAD(&dc->delayed_bios);
	mutex_init(&dc->timer_lock);
	atomic_set(&dc->may_delay, 1);
	dc->argc = argc;

	ret = delay_class_ctr(ti, &dc->read, argv);
	if (ret)
		goto bad;
	max_delay = dc->read.delay;

	if (argc == 3) {
		ret = delay_class_ctr(ti, &dc->write, argv);
@@ -206,6 +251,8 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
		ret = delay_class_ctr(ti, &dc->flush, argv);
		if (ret)
			goto bad;
		max_delay = max(max_delay, dc->write.delay);
		max_delay = max(max_delay, dc->flush.delay);
		goto out;
	}

@@ -216,20 +263,38 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
		ret = delay_class_ctr(ti, &dc->flush, argv + 3);
		if (ret)
			goto bad;
		max_delay = max(max_delay, dc->flush.delay);
		goto out;
	}

	ret = delay_class_ctr(ti, &dc->flush, argv + 6);
	if (ret)
		goto bad;
	max_delay = max(max_delay, dc->flush.delay);

out:
	if (max_delay < 50) {
		/*
		 * In case of small requested delays, use kthread instead of
		 * timers and workqueue to achieve better latency.
		 */
		dc->worker = kthread_create(&flush_worker_fn, dc,
					    "dm-delay-flush-worker");
		if (IS_ERR(dc->worker)) {
			ret = PTR_ERR(dc->worker);
			goto bad;
		}
	} else {
		timer_setup(&dc->delay_timer, handle_delayed_timer, 0);
		INIT_WORK(&dc->flush_expired_bios, flush_expired_bios);
		mutex_init(&dc->timer_lock);
		dc->kdelayd_wq = alloc_workqueue("kdelayd", WQ_MEM_RECLAIM, 0);
		if (!dc->kdelayd_wq) {
			ret = -EINVAL;
			DMERR("Couldn't start kdelayd");
			goto bad;
		}
	}

	ti->num_flush_bios = 1;
	ti->num_discard_bios = 1;
@@ -260,6 +325,9 @@ static int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio)
	list_add_tail(&delayed->list, &dc->delayed_bios);
	mutex_unlock(&delayed_bios_lock);

	if (delay_is_fast(dc))
		wake_up_process(dc->worker);
	else
		queue_timeout(dc, expires);

	return DM_MAPIO_SUBMITTED;
@@ -270,8 +338,13 @@ static void delay_presuspend(struct dm_target *ti)
	struct delay_c *dc = ti->private;

	atomic_set(&dc->may_delay, 0);

	if (delay_is_fast(dc))
		flush_delayed_bios_fast(dc, true);
	else {
		del_timer_sync(&dc->delay_timer);
	flush_bios(flush_delayed_bios(dc, 1));
		flush_bios(flush_delayed_bios(dc, true));
	}
}

static void delay_resume(struct dm_target *ti)
@@ -356,7 +429,7 @@ static int delay_iterate_devices(struct dm_target *ti,

static struct target_type delay_target = {
	.name	     = "delay",
	.version     = {1, 3, 0},
	.version     = {1, 4, 0},
	.features    = DM_TARGET_PASSES_INTEGRITY,
	.module      = THIS_MODULE,
	.ctr	     = delay_ctr,
+10 −20
Original line number Diff line number Diff line
@@ -493,42 +493,32 @@ static int sb_mac(struct dm_integrity_c *ic, bool wr)
{
	SHASH_DESC_ON_STACK(desc, ic->journal_mac);
	int r;
	unsigned int size = crypto_shash_digestsize(ic->journal_mac);
	unsigned int mac_size = crypto_shash_digestsize(ic->journal_mac);
	__u8 *sb = (__u8 *)ic->sb;
	__u8 *mac = sb + (1 << SECTOR_SHIFT) - mac_size;

	if (sizeof(struct superblock) + size > 1 << SECTOR_SHIFT) {
	if (sizeof(struct superblock) + mac_size > 1 << SECTOR_SHIFT) {
		dm_integrity_io_error(ic, "digest is too long", -EINVAL);
		return -EINVAL;
	}

	desc->tfm = ic->journal_mac;

	r = crypto_shash_init(desc);
	if (unlikely(r < 0)) {
		dm_integrity_io_error(ic, "crypto_shash_init", r);
		return r;
	}

	r = crypto_shash_update(desc, (__u8 *)ic->sb, (1 << SECTOR_SHIFT) - size);
	if (unlikely(r < 0)) {
		dm_integrity_io_error(ic, "crypto_shash_update", r);
		return r;
	}

	if (likely(wr)) {
		r = crypto_shash_final(desc, (__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size);
		r = crypto_shash_digest(desc, sb, mac - sb, mac);
		if (unlikely(r < 0)) {
			dm_integrity_io_error(ic, "crypto_shash_final", r);
			dm_integrity_io_error(ic, "crypto_shash_digest", r);
			return r;
		}
	} else {
		__u8 result[HASH_MAX_DIGESTSIZE];
		__u8 actual_mac[HASH_MAX_DIGESTSIZE];

		r = crypto_shash_final(desc, result);
		r = crypto_shash_digest(desc, sb, mac - sb, actual_mac);
		if (unlikely(r < 0)) {
			dm_integrity_io_error(ic, "crypto_shash_final", r);
			dm_integrity_io_error(ic, "crypto_shash_digest", r);
			return r;
		}
		if (memcmp((__u8 *)ic->sb + (1 << SECTOR_SHIFT) - size, result, size)) {
		if (memcmp(mac, actual_mac, mac_size)) {
			dm_integrity_io_error(ic, "superblock mac", -EILSEQ);
			dm_audit_log_target(DM_MSG_PREFIX, "mac-superblock", ic->ti, 0);
			return -EILSEQ;
Loading