Commit 0e306952 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'for-6.4/dm-fixes' of...

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

Pull device mapper fixes from Mike Snitzer:

 - Fix DM thinp discard performance regression introduced during this
   merge window where DM core was splitting large discards every 128K
   (max_sectors_kb) rather than every 64M (discard_max_bytes).

 - Extend DM core LOCKFS fix, made during 6.4 merge, to also fix race
   between do_mount and dm's do_suspend (in addition to the earlier
   fix's do_mount race with dm's do_resume).

 - Fix DM thin metadata operations to first check if the thin-pool is in
   "fail_io" mode; otherwise UAF can occur.

 - Fix DM thinp's call to __blkdev_issue_discard to use GFP_NOIO rather
   than GFP_NOWAIT (__blkdev_issue_discard cannot handle NULL return
   from bio_alloc).

* tag 'for-6.4/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm: use op specific max_sectors when splitting abnormal io
  dm thin: fix issue_discard to pass GFP_NOIO to __blkdev_issue_discard
  dm thin metadata: check fail_io before using data_sm
  dm: don't lock fs when the map is NULL during suspend or resume
parents 93fd8eb0 be04c14a
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -1168,13 +1168,10 @@ static int do_resume(struct dm_ioctl *param)
	/* Do we need to load a new map ? */
	if (new_map) {
		sector_t old_size, new_size;
		int srcu_idx;

		/* Suspend if it isn't already suspended */
		old_map = dm_get_live_table(md, &srcu_idx);
		if ((param->flags & DM_SKIP_LOCKFS_FLAG) || !old_map)
		if (param->flags & DM_SKIP_LOCKFS_FLAG)
			suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
		dm_put_live_table(md, srcu_idx);
		if (param->flags & DM_NOFLUSH_FLAG)
			suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
		if (!dm_suspended_md(md))
+12 −8
Original line number Diff line number Diff line
@@ -1756,13 +1756,15 @@ int dm_thin_remove_range(struct dm_thin_device *td,

int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *result)
{
	int r;
	int r = -EINVAL;
	uint32_t ref_count;

	down_read(&pmd->root_lock);
	if (!pmd->fail_io) {
		r = dm_sm_get_count(pmd->data_sm, b, &ref_count);
		if (!r)
			*result = (ref_count > 1);
	}
	up_read(&pmd->root_lock);

	return r;
@@ -1770,9 +1772,10 @@ int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *re

int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
{
	int r = 0;
	int r = -EINVAL;

	pmd_write_lock(pmd);
	if (!pmd->fail_io)
		r = dm_sm_inc_blocks(pmd->data_sm, b, e);
	pmd_write_unlock(pmd);

@@ -1781,9 +1784,10 @@ int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_

int dm_pool_dec_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
{
	int r = 0;
	int r = -EINVAL;

	pmd_write_lock(pmd);
	if (!pmd->fail_io)
		r = dm_sm_dec_blocks(pmd->data_sm, b, e);
	pmd_write_unlock(pmd);

+1 −2
Original line number Diff line number Diff line
@@ -401,8 +401,7 @@ static int issue_discard(struct discard_op *op, dm_block_t data_b, dm_block_t da
	sector_t s = block_to_sectors(tc->pool, data_b);
	sector_t len = block_to_sectors(tc->pool, data_e - data_b);

	return __blkdev_issue_discard(tc->pool_dev->bdev, s, len, GFP_NOWAIT,
				      &op->bio);
	return __blkdev_issue_discard(tc->pool_dev->bdev, s, len, GFP_NOIO, &op->bio);
}

static void end_discard(struct discard_op *op, int r)
+20 −9
Original line number Diff line number Diff line
@@ -1172,7 +1172,8 @@ static inline sector_t max_io_len_target_boundary(struct dm_target *ti,
}

static sector_t __max_io_len(struct dm_target *ti, sector_t sector,
			     unsigned int max_granularity)
			     unsigned int max_granularity,
			     unsigned int max_sectors)
{
	sector_t target_offset = dm_target_offset(ti, sector);
	sector_t len = max_io_len_target_boundary(ti, target_offset);
@@ -1186,13 +1187,13 @@ static sector_t __max_io_len(struct dm_target *ti, sector_t sector,
	if (!max_granularity)
		return len;
	return min_t(sector_t, len,
		min(queue_max_sectors(ti->table->md->queue),
		min(max_sectors ? : queue_max_sectors(ti->table->md->queue),
		    blk_chunk_sectors_left(target_offset, max_granularity)));
}

static inline sector_t max_io_len(struct dm_target *ti, sector_t sector)
{
	return __max_io_len(ti, sector, ti->max_io_len);
	return __max_io_len(ti, sector, ti->max_io_len, 0);
}

int dm_set_target_max_io_len(struct dm_target *ti, sector_t len)
@@ -1581,12 +1582,13 @@ static void __send_empty_flush(struct clone_info *ci)

static void __send_changing_extent_only(struct clone_info *ci, struct dm_target *ti,
					unsigned int num_bios,
					unsigned int max_granularity)
					unsigned int max_granularity,
					unsigned int max_sectors)
{
	unsigned int len, bios;

	len = min_t(sector_t, ci->sector_count,
		    __max_io_len(ti, ci->sector, max_granularity));
		    __max_io_len(ti, ci->sector, max_granularity, max_sectors));

	atomic_add(num_bios, &ci->io->io_count);
	bios = __send_duplicate_bios(ci, ti, num_bios, &len);
@@ -1623,23 +1625,27 @@ static blk_status_t __process_abnormal_io(struct clone_info *ci,
{
	unsigned int num_bios = 0;
	unsigned int max_granularity = 0;
	unsigned int max_sectors = 0;
	struct queue_limits *limits = dm_get_queue_limits(ti->table->md);

	switch (bio_op(ci->bio)) {
	case REQ_OP_DISCARD:
		num_bios = ti->num_discard_bios;
		max_sectors = limits->max_discard_sectors;
		if (ti->max_discard_granularity)
			max_granularity = limits->max_discard_sectors;
			max_granularity = max_sectors;
		break;
	case REQ_OP_SECURE_ERASE:
		num_bios = ti->num_secure_erase_bios;
		max_sectors = limits->max_secure_erase_sectors;
		if (ti->max_secure_erase_granularity)
			max_granularity = limits->max_secure_erase_sectors;
			max_granularity = max_sectors;
		break;
	case REQ_OP_WRITE_ZEROES:
		num_bios = ti->num_write_zeroes_bios;
		max_sectors = limits->max_write_zeroes_sectors;
		if (ti->max_write_zeroes_granularity)
			max_granularity = limits->max_write_zeroes_sectors;
			max_granularity = max_sectors;
		break;
	default:
		break;
@@ -1654,7 +1660,8 @@ static blk_status_t __process_abnormal_io(struct clone_info *ci,
	if (unlikely(!num_bios))
		return BLK_STS_NOTSUPP;

	__send_changing_extent_only(ci, ti, num_bios, max_granularity);
	__send_changing_extent_only(ci, ti, num_bios,
				    max_granularity, max_sectors);
	return BLK_STS_OK;
}

@@ -2808,6 +2815,10 @@ int dm_suspend(struct mapped_device *md, unsigned int suspend_flags)
	}

	map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock));
	if (!map) {
		/* avoid deadlock with fs/namespace.c:do_mount() */
		suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
	}

	r = __dm_suspend(md, map, suspend_flags, TASK_INTERRUPTIBLE, DMF_SUSPENDED);
	if (r)