Commit 7fc18728 authored by Damien Le Moal's avatar Damien Le Moal Committed by Mike Snitzer
Browse files

dm: move zone related code to dm-zone.c



Move core and table code used for zoned targets and conditionally
defined with #ifdef CONFIG_BLK_DEV_ZONED to the new file dm-zone.c.
This file is conditionally compiled depending on CONFIG_BLK_DEV_ZONED.
The small helper dm_set_zones_restrictions() is introduced to
initialize a mapped device request queue zone attributes in
dm_table_set_restrictions().

Signed-off-by: default avatarDamien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Reviewed-by: default avatarHimanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent dd73c320
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -92,6 +92,10 @@ ifeq ($(CONFIG_DM_UEVENT),y)
dm-mod-objs			+= dm-uevent.o
endif

ifeq ($(CONFIG_BLK_DEV_ZONED),y)
dm-mod-objs			+= dm-zone.o
endif

ifeq ($(CONFIG_DM_VERITY_FEC),y)
dm-verity-objs			+= dm-verity-fec.o
endif
+3 −11
Original line number Diff line number Diff line
@@ -2064,17 +2064,9 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
	    dm_table_any_dev_attr(t, device_is_not_random, NULL))
		blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);

	/*
	 * For a zoned target, the number of zones should be updated for the
	 * correct value to be exposed in sysfs queue/nr_zones. For a BIO based
	 * target, this is all that is needed.
	 */
#ifdef CONFIG_BLK_DEV_ZONED
	if (blk_queue_is_zoned(q)) {
		WARN_ON_ONCE(queue_is_mq(q));
		q->nr_zones = blkdev_nr_zones(t->md->disk);
	}
#endif
	/* For a zoned target, setup the zones related queue attributes */
	if (blk_queue_is_zoned(q))
		dm_set_zones_restrictions(t, q);

	dm_update_keyslot_manager(q, t);
	blk_queue_update_readahead(q);

drivers/md/dm-zone.c

0 → 100644
+101 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2021 Western Digital Corporation or its affiliates.
 */

#include <linux/blkdev.h>

#include "dm-core.h"

/*
 * User facing dm device block device report zone operation. This calls the
 * report_zones operation for each target of a device table. This operation is
 * generally implemented by targets using dm_report_zones().
 */
int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
			unsigned int nr_zones, report_zones_cb cb, void *data)
{
	struct mapped_device *md = disk->private_data;
	struct dm_table *map;
	int srcu_idx, ret;
	struct dm_report_zones_args args = {
		.next_sector = sector,
		.orig_data = data,
		.orig_cb = cb,
	};

	if (dm_suspended_md(md))
		return -EAGAIN;

	map = dm_get_live_table(md, &srcu_idx);
	if (!map) {
		ret = -EIO;
		goto out;
	}

	do {
		struct dm_target *tgt;

		tgt = dm_table_find_target(map, args.next_sector);
		if (WARN_ON_ONCE(!tgt->type->report_zones)) {
			ret = -EIO;
			goto out;
		}

		args.tgt = tgt;
		ret = tgt->type->report_zones(tgt, &args,
					      nr_zones - args.zone_idx);
		if (ret < 0)
			goto out;
	} while (args.zone_idx < nr_zones &&
		 args.next_sector < get_capacity(disk));

	ret = args.zone_idx;
out:
	dm_put_live_table(md, srcu_idx);
	return ret;
}

int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx, void *data)
{
	struct dm_report_zones_args *args = data;
	sector_t sector_diff = args->tgt->begin - args->start;

	/*
	 * Ignore zones beyond the target range.
	 */
	if (zone->start >= args->start + args->tgt->len)
		return 0;

	/*
	 * Remap the start sector and write pointer position of the zone
	 * to match its position in the target range.
	 */
	zone->start += sector_diff;
	if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
		if (zone->cond == BLK_ZONE_COND_FULL)
			zone->wp = zone->start + zone->len;
		else if (zone->cond == BLK_ZONE_COND_EMPTY)
			zone->wp = zone->start;
		else
			zone->wp += sector_diff;
	}

	args->next_sector = zone->start + zone->len;
	return args->orig_cb(zone, args->zone_idx++, args->orig_data);
}
EXPORT_SYMBOL_GPL(dm_report_zones_cb);

void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q)
{
	if (!blk_queue_is_zoned(q))
		return;

	/*
	 * For a zoned target, the number of zones should be updated for the
	 * correct value to be exposed in sysfs queue/nr_zones. For a BIO based
	 * target, this is all that is needed.
	 */
	WARN_ON_ONCE(queue_is_mq(q));
	q->nr_zones = blkdev_nr_zones(t->md->disk);
}
+0 −78
Original line number Diff line number Diff line
@@ -444,84 +444,6 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
	return dm_get_geometry(md, geo);
}

#ifdef CONFIG_BLK_DEV_ZONED
int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx, void *data)
{
	struct dm_report_zones_args *args = data;
	sector_t sector_diff = args->tgt->begin - args->start;

	/*
	 * Ignore zones beyond the target range.
	 */
	if (zone->start >= args->start + args->tgt->len)
		return 0;

	/*
	 * Remap the start sector and write pointer position of the zone
	 * to match its position in the target range.
	 */
	zone->start += sector_diff;
	if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
		if (zone->cond == BLK_ZONE_COND_FULL)
			zone->wp = zone->start + zone->len;
		else if (zone->cond == BLK_ZONE_COND_EMPTY)
			zone->wp = zone->start;
		else
			zone->wp += sector_diff;
	}

	args->next_sector = zone->start + zone->len;
	return args->orig_cb(zone, args->zone_idx++, args->orig_data);
}
EXPORT_SYMBOL_GPL(dm_report_zones_cb);

static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
		unsigned int nr_zones, report_zones_cb cb, void *data)
{
	struct mapped_device *md = disk->private_data;
	struct dm_table *map;
	int srcu_idx, ret;
	struct dm_report_zones_args args = {
		.next_sector = sector,
		.orig_data = data,
		.orig_cb = cb,
	};

	if (dm_suspended_md(md))
		return -EAGAIN;

	map = dm_get_live_table(md, &srcu_idx);
	if (!map) {
		ret = -EIO;
		goto out;
	}

	do {
		struct dm_target *tgt;

		tgt = dm_table_find_target(map, args.next_sector);
		if (WARN_ON_ONCE(!tgt->type->report_zones)) {
			ret = -EIO;
			goto out;
		}

		args.tgt = tgt;
		ret = tgt->type->report_zones(tgt, &args,
					      nr_zones - args.zone_idx);
		if (ret < 0)
			goto out;
	} while (args.zone_idx < nr_zones &&
		 args.next_sector < get_capacity(disk));

	ret = args.zone_idx;
out:
	dm_put_live_table(md, srcu_idx);
	return ret;
}
#else
#define dm_blk_report_zones		NULL
#endif /* CONFIG_BLK_DEV_ZONED */

static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
			    struct block_device **bdev)
{
+11 −0
Original line number Diff line number Diff line
@@ -100,6 +100,17 @@ int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t);
 */
#define dm_target_hybrid(t) (dm_target_bio_based(t) && dm_target_request_based(t))

/*
 * Zoned targets related functions.
 */
void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q);
#ifdef CONFIG_BLK_DEV_ZONED
int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
			unsigned int nr_zones, report_zones_cb cb, void *data);
#else
#define dm_blk_report_zones	NULL
#endif

/*-----------------------------------------------------------------
 * A registry of target types.
 *---------------------------------------------------------------*/