Commit 3c727285 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

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

Pull device mapper updates from Mikulas Patocka:

 - better error handling when reloading a table

 - use use generic disable_* functions instead of open coding them

 - lock queue limits when reading them

 - remove unneeded kvfree from alloc_targets

 - fix BLK_FEAT_ATOMIC_WRITES

 - pass through operations on wrapped inline crypto keys

 - dm-verity:
     - use softirq context only when !need_resched()
     - fix a memory leak if some arguments are specified multiple times

 - dm-mpath:
    - interface for explicit probing of active paths
    - replace spin_lock_irqsave with spin_lock_irq

 - dm-delay: don't busy-wait in kthread

 - dm-bufio: remove maximum age based eviction

 - dm-flakey: various fixes

 - vdo indexer: don't read request structure after enqueuing

 - dm-zone: Use bdev_*() helper functions where applicable

 - dm-mirror: fix a tiny race condition

 - dm-stripe: small code cleanup

* tag 'for-6.16/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: (29 commits)
  dm-stripe: small code cleanup
  dm-verity: fix a memory leak if some arguments are specified multiple times
  dm-mirror: fix a tiny race condition
  dm-table: check BLK_FEAT_ATOMIC_WRITES inside limits_lock
  dm mpath: replace spin_lock_irqsave with spin_lock_irq
  dm-mpath: Don't grab work_mutex while probing paths
  dm-zone: Use bdev_*() helper functions where applicable
  dm vdo indexer: don't read request structure after enqueuing
  dm: pass through operations on wrapped inline crypto keys
  blk-crypto: export wrapped key functions
  dm-table: Set BLK_FEAT_ATOMIC_WRITES for target queue limits
  dm mpath: Interface for explicit probing of active paths
  dm: Allow .prepare_ioctl to handle ioctls directly
  dm-flakey: make corrupting read bios work
  dm-flakey: remove useless ERROR_READS check in flakey_end_io
  dm-flakey: error all IOs when num_features is absent
  dm-flakey: Clean up parsing messages
  dm: remove unneeded kvfree from alloc_targets
  dm-bufio: remove maximum age based eviction
  dm-verity: use softirq context only when !need_resched()
  ...
parents 0939bd2f 9f2f6316
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -501,6 +501,7 @@ int blk_crypto_derive_sw_secret(struct block_device *bdev,
	blk_crypto_hw_exit(profile);
	return err;
}
EXPORT_SYMBOL_GPL(blk_crypto_derive_sw_secret);

int blk_crypto_import_key(struct blk_crypto_profile *profile,
			  const u8 *raw_key, size_t raw_key_size,
@@ -520,6 +521,7 @@ int blk_crypto_import_key(struct blk_crypto_profile *profile,
	blk_crypto_hw_exit(profile);
	return ret;
}
EXPORT_SYMBOL_GPL(blk_crypto_import_key);

int blk_crypto_generate_key(struct blk_crypto_profile *profile,
			    u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
@@ -537,6 +539,7 @@ int blk_crypto_generate_key(struct blk_crypto_profile *profile,
	blk_crypto_hw_exit(profile);
	return ret;
}
EXPORT_SYMBOL_GPL(blk_crypto_generate_key);

int blk_crypto_prepare_key(struct blk_crypto_profile *profile,
			   const u8 *lt_key, size_t lt_key_size,
@@ -556,6 +559,7 @@ int blk_crypto_prepare_key(struct blk_crypto_profile *profile,
	blk_crypto_hw_exit(profile);
	return ret;
}
EXPORT_SYMBOL_GPL(blk_crypto_prepare_key);

/**
 * blk_crypto_intersect_capabilities() - restrict supported crypto capabilities
+36 −153
Original line number Diff line number Diff line
@@ -40,16 +40,6 @@
#define DM_BUFIO_WRITEBACK_RATIO	3
#define DM_BUFIO_LOW_WATERMARK_RATIO	16

/*
 * Check buffer ages in this interval (seconds)
 */
#define DM_BUFIO_WORK_TIMER_SECS	30

/*
 * Free buffers when they are older than this (seconds)
 */
#define DM_BUFIO_DEFAULT_AGE_SECS	300

/*
 * The nr of bytes of cached data to keep around.
 */
@@ -1057,10 +1047,8 @@ static unsigned long dm_bufio_cache_size_latch;

static DEFINE_SPINLOCK(global_spinlock);

/*
 * Buffers are freed after this timeout
 */
static unsigned int dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
static unsigned int dm_bufio_max_age; /* No longer does anything */

static unsigned long dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;

static unsigned long dm_bufio_peak_allocated;
@@ -1088,7 +1076,6 @@ static LIST_HEAD(dm_bufio_all_clients);
static DEFINE_MUTEX(dm_bufio_clients_lock);

static struct workqueue_struct *dm_bufio_wq;
static struct delayed_work dm_bufio_cleanup_old_work;
static struct work_struct dm_bufio_replacement_work;


@@ -2680,130 +2667,6 @@ EXPORT_SYMBOL_GPL(dm_bufio_set_sector_offset);

/*--------------------------------------------------------------*/

static unsigned int get_max_age_hz(void)
{
	unsigned int max_age = READ_ONCE(dm_bufio_max_age);

	if (max_age > UINT_MAX / HZ)
		max_age = UINT_MAX / HZ;

	return max_age * HZ;
}

static bool older_than(struct dm_buffer *b, unsigned long age_hz)
{
	return time_after_eq(jiffies, READ_ONCE(b->last_accessed) + age_hz);
}

struct evict_params {
	gfp_t gfp;
	unsigned long age_hz;

	/*
	 * This gets updated with the largest last_accessed (ie. most
	 * recently used) of the evicted buffers.  It will not be reinitialised
	 * by __evict_many(), so you can use it across multiple invocations.
	 */
	unsigned long last_accessed;
};

/*
 * We may not be able to evict this buffer if IO pending or the client
 * is still using it.
 *
 * And if GFP_NOFS is used, we must not do any I/O because we hold
 * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
 * rerouted to different bufio client.
 */
static enum evict_result select_for_evict(struct dm_buffer *b, void *context)
{
	struct evict_params *params = context;

	if (!(params->gfp & __GFP_FS) ||
	    (static_branch_unlikely(&no_sleep_enabled) && b->c->no_sleep)) {
		if (test_bit_acquire(B_READING, &b->state) ||
		    test_bit(B_WRITING, &b->state) ||
		    test_bit(B_DIRTY, &b->state))
			return ER_DONT_EVICT;
	}

	return older_than(b, params->age_hz) ? ER_EVICT : ER_STOP;
}

static unsigned long __evict_many(struct dm_bufio_client *c,
				  struct evict_params *params,
				  int list_mode, unsigned long max_count)
{
	unsigned long count;
	unsigned long last_accessed;
	struct dm_buffer *b;

	for (count = 0; count < max_count; count++) {
		b = cache_evict(&c->cache, list_mode, select_for_evict, params);
		if (!b)
			break;

		last_accessed = READ_ONCE(b->last_accessed);
		if (time_after_eq(params->last_accessed, last_accessed))
			params->last_accessed = last_accessed;

		__make_buffer_clean(b);
		__free_buffer_wake(b);

		cond_resched();
	}

	return count;
}

static void evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
{
	struct evict_params params = {.gfp = 0, .age_hz = age_hz, .last_accessed = 0};
	unsigned long retain = get_retain_buffers(c);
	unsigned long count;
	LIST_HEAD(write_list);

	dm_bufio_lock(c);

	__check_watermark(c, &write_list);
	if (unlikely(!list_empty(&write_list))) {
		dm_bufio_unlock(c);
		__flush_write_list(&write_list);
		dm_bufio_lock(c);
	}

	count = cache_total(&c->cache);
	if (count > retain)
		__evict_many(c, &params, LIST_CLEAN, count - retain);

	dm_bufio_unlock(c);
}

static void cleanup_old_buffers(void)
{
	unsigned long max_age_hz = get_max_age_hz();
	struct dm_bufio_client *c;

	mutex_lock(&dm_bufio_clients_lock);

	__cache_size_refresh();

	list_for_each_entry(c, &dm_bufio_all_clients, client_list)
		evict_old_buffers(c, max_age_hz);

	mutex_unlock(&dm_bufio_clients_lock);
}

static void work_fn(struct work_struct *w)
{
	cleanup_old_buffers();

	queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
			   DM_BUFIO_WORK_TIMER_SECS * HZ);
}

/*--------------------------------------------------------------*/

/*
 * Global cleanup tries to evict the oldest buffers from across _all_
 * the clients.  It does this by repeatedly evicting a few buffers from
@@ -2841,27 +2704,51 @@ static void __insert_client(struct dm_bufio_client *new_client)
	list_add_tail(&new_client->client_list, h);
}

static enum evict_result select_for_evict(struct dm_buffer *b, void *context)
{
	/* In no-sleep mode, we cannot wait on IO. */
	if (static_branch_unlikely(&no_sleep_enabled) && b->c->no_sleep) {
		if (test_bit_acquire(B_READING, &b->state) ||
		    test_bit(B_WRITING, &b->state) ||
		    test_bit(B_DIRTY, &b->state))
			return ER_DONT_EVICT;
	}
	return ER_EVICT;
}

static unsigned long __evict_a_few(unsigned long nr_buffers)
{
	unsigned long count;
	struct dm_bufio_client *c;
	struct evict_params params = {
		.gfp = GFP_KERNEL,
		.age_hz = 0,
		/* set to jiffies in case there are no buffers in this client */
		.last_accessed = jiffies
	};
	unsigned long oldest_buffer = jiffies;
	unsigned long last_accessed;
	unsigned long count;
	struct dm_buffer *b;

	c = __pop_client();
	if (!c)
		return 0;

	dm_bufio_lock(c);
	count = __evict_many(c, &params, LIST_CLEAN, nr_buffers);

	for (count = 0; count < nr_buffers; count++) {
		b = cache_evict(&c->cache, LIST_CLEAN, select_for_evict, NULL);
		if (!b)
			break;

		last_accessed = READ_ONCE(b->last_accessed);
		if (time_after_eq(oldest_buffer, last_accessed))
			oldest_buffer = last_accessed;

		__make_buffer_clean(b);
		__free_buffer_wake(b);

		cond_resched();
	}

	dm_bufio_unlock(c);

	if (count)
		c->oldest_buffer = params.last_accessed;
		c->oldest_buffer = oldest_buffer;
	__insert_client(c);

	return count;
@@ -2944,10 +2831,7 @@ static int __init dm_bufio_init(void)
	if (!dm_bufio_wq)
		return -ENOMEM;

	INIT_DELAYED_WORK(&dm_bufio_cleanup_old_work, work_fn);
	INIT_WORK(&dm_bufio_replacement_work, do_global_cleanup);
	queue_delayed_work(dm_bufio_wq, &dm_bufio_cleanup_old_work,
			   DM_BUFIO_WORK_TIMER_SECS * HZ);

	return 0;
}
@@ -2959,7 +2843,6 @@ static void __exit dm_bufio_exit(void)
{
	int bug = 0;

	cancel_delayed_work_sync(&dm_bufio_cleanup_old_work);
	destroy_workqueue(dm_bufio_wq);

	if (dm_bufio_client_count) {
@@ -2996,7 +2879,7 @@ module_param_named(max_cache_size_bytes, dm_bufio_cache_size, ulong, 0644);
MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache");

module_param_named(max_age_seconds, dm_bufio_max_age, uint, 0644);
MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
MODULE_PARM_DESC(max_age_seconds, "No longer does anything");

module_param_named(retain_bytes, dm_bufio_retain_bytes, ulong, 0644);
MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory");
+1 −3
Original line number Diff line number Diff line
@@ -141,6 +141,7 @@ struct mapped_device {
#ifdef CONFIG_BLK_DEV_ZONED
	unsigned int nr_zones;
	void *zone_revalidate_map;
	struct task_struct *revalidate_map_task;
#endif

#ifdef CONFIG_IMA
@@ -162,9 +163,6 @@ struct mapped_device {
#define DMF_POST_SUSPENDING 8
#define DMF_EMULATE_ZONE_APPEND 9

void disable_discard(struct mapped_device *md);
void disable_write_zeroes(struct mapped_device *md);

static inline sector_t dm_get_size(struct mapped_device *md)
{
	return get_capacity(md->disk);
+14 −3
Original line number Diff line number Diff line
@@ -14,11 +14,14 @@
#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/delay.h>

#include <linux/device-mapper.h>

#define DM_MSG_PREFIX "delay"

#define SLEEP_SHIFT 3

struct delay_class {
	struct dm_dev *dev;
	sector_t start;
@@ -34,6 +37,7 @@ struct delay_c {
	struct work_struct flush_expired_bios;
	struct list_head delayed_bios;
	struct task_struct *worker;
	unsigned int worker_sleep_us;
	bool may_delay;

	struct delay_class read;
@@ -136,6 +140,7 @@ static int flush_worker_fn(void *data)
			schedule();
		} else {
			spin_unlock(&dc->delayed_bios_lock);
			fsleep(dc->worker_sleep_us);
			cond_resched();
		}
	}
@@ -212,7 +217,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
	struct delay_c *dc;
	int ret;
	unsigned int max_delay;
	unsigned int max_delay, min_delay;

	if (argc != 3 && argc != 6 && argc != 9) {
		ti->error = "Requires exactly 3, 6 or 9 arguments";
@@ -235,7 +240,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
	ret = delay_class_ctr(ti, &dc->read, argv);
	if (ret)
		goto bad;
	max_delay = dc->read.delay;
	min_delay = max_delay = dc->read.delay;

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

	if (argc == 6) {
		ret = delay_class_ctr(ti, &dc->flush, argv + 3);
@@ -263,9 +269,14 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
	if (ret)
		goto bad;
	max_delay = max(max_delay, dc->flush.delay);
	min_delay = min_not_zero(min_delay, dc->flush.delay);

out:
	if (max_delay < 50) {
		if (min_delay >> SLEEP_SHIFT)
			dc->worker_sleep_us = 1000;
		else
			dc->worker_sleep_us = (min_delay * 1000) >> SLEEP_SHIFT;
		/*
		 * In case of small requested delays, use kthread instead of
		 * timers and workqueue to achieve better latency.
@@ -438,7 +449,7 @@ static int delay_iterate_devices(struct dm_target *ti,

static struct target_type delay_target = {
	.name	     = "delay",
	.version     = {1, 4, 0},
	.version     = {1, 5, 0},
	.features    = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_ZONED_HM,
	.module      = THIS_MODULE,
	.ctr	     = delay_ctr,
+3 −1
Original line number Diff line number Diff line
@@ -534,7 +534,9 @@ static void dust_status(struct dm_target *ti, status_type_t type,
	}
}

static int dust_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
static int dust_prepare_ioctl(struct dm_target *ti, struct block_device **bdev,
			      unsigned int cmd, unsigned long arg,
			      bool *forward)
{
	struct dust_device *dd = ti->private;
	struct dm_dev *dev = dd->dev;
Loading