xref: /openbmc/linux/drivers/md/dm-zone.c (revision bf14e2b2)
17fc18728SDamien Le Moal // SPDX-License-Identifier: GPL-2.0
27fc18728SDamien Le Moal /*
37fc18728SDamien Le Moal  * Copyright (C) 2021 Western Digital Corporation or its affiliates.
47fc18728SDamien Le Moal  */
57fc18728SDamien Le Moal 
67fc18728SDamien Le Moal #include <linux/blkdev.h>
77fc18728SDamien Le Moal 
87fc18728SDamien Le Moal #include "dm-core.h"
97fc18728SDamien Le Moal 
107fc18728SDamien Le Moal /*
117fc18728SDamien Le Moal  * User facing dm device block device report zone operation. This calls the
127fc18728SDamien Le Moal  * report_zones operation for each target of a device table. This operation is
137fc18728SDamien Le Moal  * generally implemented by targets using dm_report_zones().
147fc18728SDamien Le Moal  */
157fc18728SDamien Le Moal int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
167fc18728SDamien Le Moal 			unsigned int nr_zones, report_zones_cb cb, void *data)
177fc18728SDamien Le Moal {
187fc18728SDamien Le Moal 	struct mapped_device *md = disk->private_data;
197fc18728SDamien Le Moal 	struct dm_table *map;
207fc18728SDamien Le Moal 	int srcu_idx, ret;
217fc18728SDamien Le Moal 	struct dm_report_zones_args args = {
227fc18728SDamien Le Moal 		.next_sector = sector,
237fc18728SDamien Le Moal 		.orig_data = data,
247fc18728SDamien Le Moal 		.orig_cb = cb,
257fc18728SDamien Le Moal 	};
267fc18728SDamien Le Moal 
277fc18728SDamien Le Moal 	if (dm_suspended_md(md))
287fc18728SDamien Le Moal 		return -EAGAIN;
297fc18728SDamien Le Moal 
307fc18728SDamien Le Moal 	map = dm_get_live_table(md, &srcu_idx);
317fc18728SDamien Le Moal 	if (!map) {
327fc18728SDamien Le Moal 		ret = -EIO;
337fc18728SDamien Le Moal 		goto out;
347fc18728SDamien Le Moal 	}
357fc18728SDamien Le Moal 
367fc18728SDamien Le Moal 	do {
377fc18728SDamien Le Moal 		struct dm_target *tgt;
387fc18728SDamien Le Moal 
397fc18728SDamien Le Moal 		tgt = dm_table_find_target(map, args.next_sector);
407fc18728SDamien Le Moal 		if (WARN_ON_ONCE(!tgt->type->report_zones)) {
417fc18728SDamien Le Moal 			ret = -EIO;
427fc18728SDamien Le Moal 			goto out;
437fc18728SDamien Le Moal 		}
447fc18728SDamien Le Moal 
457fc18728SDamien Le Moal 		args.tgt = tgt;
467fc18728SDamien Le Moal 		ret = tgt->type->report_zones(tgt, &args,
477fc18728SDamien Le Moal 					      nr_zones - args.zone_idx);
487fc18728SDamien Le Moal 		if (ret < 0)
497fc18728SDamien Le Moal 			goto out;
507fc18728SDamien Le Moal 	} while (args.zone_idx < nr_zones &&
517fc18728SDamien Le Moal 		 args.next_sector < get_capacity(disk));
527fc18728SDamien Le Moal 
537fc18728SDamien Le Moal 	ret = args.zone_idx;
547fc18728SDamien Le Moal out:
557fc18728SDamien Le Moal 	dm_put_live_table(md, srcu_idx);
567fc18728SDamien Le Moal 	return ret;
577fc18728SDamien Le Moal }
587fc18728SDamien Le Moal 
59912e8875SDamien Le Moal static int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx,
60912e8875SDamien Le Moal 			      void *data)
617fc18728SDamien Le Moal {
627fc18728SDamien Le Moal 	struct dm_report_zones_args *args = data;
637fc18728SDamien Le Moal 	sector_t sector_diff = args->tgt->begin - args->start;
647fc18728SDamien Le Moal 
657fc18728SDamien Le Moal 	/*
667fc18728SDamien Le Moal 	 * Ignore zones beyond the target range.
677fc18728SDamien Le Moal 	 */
687fc18728SDamien Le Moal 	if (zone->start >= args->start + args->tgt->len)
697fc18728SDamien Le Moal 		return 0;
707fc18728SDamien Le Moal 
717fc18728SDamien Le Moal 	/*
727fc18728SDamien Le Moal 	 * Remap the start sector and write pointer position of the zone
737fc18728SDamien Le Moal 	 * to match its position in the target range.
747fc18728SDamien Le Moal 	 */
757fc18728SDamien Le Moal 	zone->start += sector_diff;
767fc18728SDamien Le Moal 	if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
777fc18728SDamien Le Moal 		if (zone->cond == BLK_ZONE_COND_FULL)
787fc18728SDamien Le Moal 			zone->wp = zone->start + zone->len;
797fc18728SDamien Le Moal 		else if (zone->cond == BLK_ZONE_COND_EMPTY)
807fc18728SDamien Le Moal 			zone->wp = zone->start;
817fc18728SDamien Le Moal 		else
827fc18728SDamien Le Moal 			zone->wp += sector_diff;
837fc18728SDamien Le Moal 	}
847fc18728SDamien Le Moal 
857fc18728SDamien Le Moal 	args->next_sector = zone->start + zone->len;
867fc18728SDamien Le Moal 	return args->orig_cb(zone, args->zone_idx++, args->orig_data);
877fc18728SDamien Le Moal }
88912e8875SDamien Le Moal 
89912e8875SDamien Le Moal /*
90912e8875SDamien Le Moal  * Helper for drivers of zoned targets to implement struct target_type
91912e8875SDamien Le Moal  * report_zones operation.
92912e8875SDamien Le Moal  */
93912e8875SDamien Le Moal int dm_report_zones(struct block_device *bdev, sector_t start, sector_t sector,
94912e8875SDamien Le Moal 		    struct dm_report_zones_args *args, unsigned int nr_zones)
95912e8875SDamien Le Moal {
96912e8875SDamien Le Moal 	/*
97912e8875SDamien Le Moal 	 * Set the target mapping start sector first so that
98912e8875SDamien Le Moal 	 * dm_report_zones_cb() can correctly remap zone information.
99912e8875SDamien Le Moal 	 */
100912e8875SDamien Le Moal 	args->start = start;
101912e8875SDamien Le Moal 
102912e8875SDamien Le Moal 	return blkdev_report_zones(bdev, sector, nr_zones,
103912e8875SDamien Le Moal 				   dm_report_zones_cb, args);
104912e8875SDamien Le Moal }
105912e8875SDamien Le Moal EXPORT_SYMBOL_GPL(dm_report_zones);
1067fc18728SDamien Le Moal 
107*bf14e2b2SDamien Le Moal bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)
108*bf14e2b2SDamien Le Moal {
109*bf14e2b2SDamien Le Moal 	struct request_queue *q = md->queue;
110*bf14e2b2SDamien Le Moal 
111*bf14e2b2SDamien Le Moal 	if (!blk_queue_is_zoned(q))
112*bf14e2b2SDamien Le Moal 		return false;
113*bf14e2b2SDamien Le Moal 
114*bf14e2b2SDamien Le Moal 	switch (bio_op(bio)) {
115*bf14e2b2SDamien Le Moal 	case REQ_OP_WRITE_ZEROES:
116*bf14e2b2SDamien Le Moal 	case REQ_OP_WRITE_SAME:
117*bf14e2b2SDamien Le Moal 	case REQ_OP_WRITE:
118*bf14e2b2SDamien Le Moal 		return !op_is_flush(bio->bi_opf) && bio_sectors(bio);
119*bf14e2b2SDamien Le Moal 	default:
120*bf14e2b2SDamien Le Moal 		return false;
121*bf14e2b2SDamien Le Moal 	}
122*bf14e2b2SDamien Le Moal }
123*bf14e2b2SDamien Le Moal 
1247fc18728SDamien Le Moal void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q)
1257fc18728SDamien Le Moal {
1267fc18728SDamien Le Moal 	if (!blk_queue_is_zoned(q))
1277fc18728SDamien Le Moal 		return;
1287fc18728SDamien Le Moal 
1297fc18728SDamien Le Moal 	/*
1307fc18728SDamien Le Moal 	 * For a zoned target, the number of zones should be updated for the
1317fc18728SDamien Le Moal 	 * correct value to be exposed in sysfs queue/nr_zones. For a BIO based
1327fc18728SDamien Le Moal 	 * target, this is all that is needed.
1337fc18728SDamien Le Moal 	 */
1347fc18728SDamien Le Moal 	WARN_ON_ONCE(queue_is_mq(q));
1357fc18728SDamien Le Moal 	q->nr_zones = blkdev_nr_zones(t->md->disk);
1367fc18728SDamien Le Moal }
137