xref: /openbmc/linux/drivers/md/dm-zoned-target.c (revision 4aef108a4d60bb52bf4e8e2ed9444afe2cdfe6a9)
1bae9a0aaSDmitry Fomichev // SPDX-License-Identifier: GPL-2.0-only
23b1a94c8SDamien Le Moal /*
33b1a94c8SDamien Le Moal  * Copyright (C) 2017 Western Digital Corporation or its affiliates.
43b1a94c8SDamien Le Moal  *
53b1a94c8SDamien Le Moal  * This file is released under the GPL.
63b1a94c8SDamien Le Moal  */
73b1a94c8SDamien Le Moal 
83b1a94c8SDamien Le Moal #include "dm-zoned.h"
93b1a94c8SDamien Le Moal 
103b1a94c8SDamien Le Moal #include <linux/module.h>
113b1a94c8SDamien Le Moal 
123b1a94c8SDamien Le Moal #define	DM_MSG_PREFIX		"zoned"
133b1a94c8SDamien Le Moal 
143b1a94c8SDamien Le Moal #define DMZ_MIN_BIOS		8192
153b1a94c8SDamien Le Moal 
163b1a94c8SDamien Le Moal /*
173b1a94c8SDamien Le Moal  * Zone BIO context.
183b1a94c8SDamien Le Moal  */
193b1a94c8SDamien Le Moal struct dmz_bioctx {
2052d67758SHannes Reinecke 	struct dmz_dev		*dev;
213b1a94c8SDamien Le Moal 	struct dm_zone		*zone;
223b1a94c8SDamien Le Moal 	struct bio		*bio;
23092b5648SJohn Pittman 	refcount_t		ref;
243b1a94c8SDamien Le Moal };
253b1a94c8SDamien Le Moal 
263b1a94c8SDamien Le Moal /*
273b1a94c8SDamien Le Moal  * Chunk work descriptor.
283b1a94c8SDamien Le Moal  */
293b1a94c8SDamien Le Moal struct dm_chunk_work {
303b1a94c8SDamien Le Moal 	struct work_struct	work;
31092b5648SJohn Pittman 	refcount_t		refcount;
323b1a94c8SDamien Le Moal 	struct dmz_target	*target;
333b1a94c8SDamien Le Moal 	unsigned int		chunk;
343b1a94c8SDamien Le Moal 	struct bio_list		bio_list;
353b1a94c8SDamien Le Moal };
363b1a94c8SDamien Le Moal 
373b1a94c8SDamien Le Moal /*
383b1a94c8SDamien Le Moal  * Target descriptor.
393b1a94c8SDamien Le Moal  */
403b1a94c8SDamien Le Moal struct dmz_target {
414dba1288SHannes Reinecke 	struct dm_dev		**ddev;
42f97809aeSHannes Reinecke 	unsigned int		nr_ddevs;
433b1a94c8SDamien Le Moal 
444dba1288SHannes Reinecke 	unsigned int		flags;
453b1a94c8SDamien Le Moal 
463b1a94c8SDamien Le Moal 	/* Zoned block device information */
473b1a94c8SDamien Le Moal 	struct dmz_dev		*dev;
483b1a94c8SDamien Le Moal 
493b1a94c8SDamien Le Moal 	/* For metadata handling */
503b1a94c8SDamien Le Moal 	struct dmz_metadata     *metadata;
513b1a94c8SDamien Le Moal 
523b1a94c8SDamien Le Moal 	/* For chunk work */
533b1a94c8SDamien Le Moal 	struct radix_tree_root	chunk_rxtree;
543b1a94c8SDamien Le Moal 	struct workqueue_struct *chunk_wq;
5572d711c8SMike Snitzer 	struct mutex		chunk_lock;
563b1a94c8SDamien Le Moal 
573b1a94c8SDamien Le Moal 	/* For cloned BIOs to zones */
586f1c819cSKent Overstreet 	struct bio_set		bio_set;
593b1a94c8SDamien Le Moal 
603b1a94c8SDamien Le Moal 	/* For flush */
613b1a94c8SDamien Le Moal 	spinlock_t		flush_lock;
623b1a94c8SDamien Le Moal 	struct bio_list		flush_list;
633b1a94c8SDamien Le Moal 	struct delayed_work	flush_work;
643b1a94c8SDamien Le Moal 	struct workqueue_struct *flush_wq;
653b1a94c8SDamien Le Moal };
663b1a94c8SDamien Le Moal 
673b1a94c8SDamien Le Moal /*
683b1a94c8SDamien Le Moal  * Flush intervals (seconds).
693b1a94c8SDamien Le Moal  */
703b1a94c8SDamien Le Moal #define DMZ_FLUSH_PERIOD	(10 * HZ)
713b1a94c8SDamien Le Moal 
723b1a94c8SDamien Le Moal /*
733b1a94c8SDamien Le Moal  * Target BIO completion.
743b1a94c8SDamien Le Moal  */
dmz_bio_endio(struct bio * bio,blk_status_t status)753b1a94c8SDamien Le Moal static inline void dmz_bio_endio(struct bio *bio, blk_status_t status)
763b1a94c8SDamien Le Moal {
7752d67758SHannes Reinecke 	struct dmz_bioctx *bioctx =
7852d67758SHannes Reinecke 		dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
793b1a94c8SDamien Le Moal 
80d57f9da8SDamien Le Moal 	if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
81d57f9da8SDamien Le Moal 		bio->bi_status = status;
82bd5c4031SHannes Reinecke 	if (bioctx->dev && bio->bi_status != BLK_STS_OK)
8352d67758SHannes Reinecke 		bioctx->dev->flags |= DMZ_CHECK_BDEV;
84d57f9da8SDamien Le Moal 
85d57f9da8SDamien Le Moal 	if (refcount_dec_and_test(&bioctx->ref)) {
86d57f9da8SDamien Le Moal 		struct dm_zone *zone = bioctx->zone;
87d57f9da8SDamien Le Moal 
88d57f9da8SDamien Le Moal 		if (zone) {
89d57f9da8SDamien Le Moal 			if (bio->bi_status != BLK_STS_OK &&
90d57f9da8SDamien Le Moal 			    bio_op(bio) == REQ_OP_WRITE &&
91d57f9da8SDamien Le Moal 			    dmz_is_seq(zone))
92d57f9da8SDamien Le Moal 				set_bit(DMZ_SEQ_WRITE_ERR, &zone->flags);
93d57f9da8SDamien Le Moal 			dmz_deactivate_zone(zone);
94d57f9da8SDamien Le Moal 		}
953b1a94c8SDamien Le Moal 		bio_endio(bio);
963b1a94c8SDamien Le Moal 	}
97d57f9da8SDamien Le Moal }
983b1a94c8SDamien Le Moal 
993b1a94c8SDamien Le Moal /*
100d57f9da8SDamien Le Moal  * Completion callback for an internally cloned target BIO. This terminates the
1013b1a94c8SDamien Le Moal  * target BIO when there are no more references to its context.
1023b1a94c8SDamien Le Moal  */
dmz_clone_endio(struct bio * clone)103d57f9da8SDamien Le Moal static void dmz_clone_endio(struct bio *clone)
1043b1a94c8SDamien Le Moal {
105d57f9da8SDamien Le Moal 	struct dmz_bioctx *bioctx = clone->bi_private;
106d57f9da8SDamien Le Moal 	blk_status_t status = clone->bi_status;
1073b1a94c8SDamien Le Moal 
108d57f9da8SDamien Le Moal 	bio_put(clone);
1093b1a94c8SDamien Le Moal 	dmz_bio_endio(bioctx->bio, status);
1103b1a94c8SDamien Le Moal }
1113b1a94c8SDamien Le Moal 
1123b1a94c8SDamien Le Moal /*
113d57f9da8SDamien Le Moal  * Issue a clone of a target BIO. The clone may only partially process the
1143b1a94c8SDamien Le Moal  * original target BIO.
1153b1a94c8SDamien Le Moal  */
dmz_submit_bio(struct dmz_target * dmz,struct dm_zone * zone,struct bio * bio,sector_t chunk_block,unsigned int nr_blocks)116d57f9da8SDamien Le Moal static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,
1173b1a94c8SDamien Le Moal 			  struct bio *bio, sector_t chunk_block,
1183b1a94c8SDamien Le Moal 			  unsigned int nr_blocks)
1193b1a94c8SDamien Le Moal {
12052d67758SHannes Reinecke 	struct dmz_bioctx *bioctx =
12152d67758SHannes Reinecke 		dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
1228f22272aSHannes Reinecke 	struct dmz_dev *dev = zone->dev;
1233b1a94c8SDamien Le Moal 	struct bio *clone;
1243b1a94c8SDamien Le Moal 
12552d67758SHannes Reinecke 	if (dev->flags & DMZ_BDEV_DYING)
12652d67758SHannes Reinecke 		return -EIO;
12752d67758SHannes Reinecke 
128abfc426dSChristoph Hellwig 	clone = bio_alloc_clone(dev->bdev, bio, GFP_NOIO, &dmz->bio_set);
1293b1a94c8SDamien Le Moal 	if (!clone)
1303b1a94c8SDamien Le Moal 		return -ENOMEM;
1313b1a94c8SDamien Le Moal 
13252d67758SHannes Reinecke 	bioctx->dev = dev;
133d57f9da8SDamien Le Moal 	clone->bi_iter.bi_sector =
134d57f9da8SDamien Le Moal 		dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
1353b1a94c8SDamien Le Moal 	clone->bi_iter.bi_size = dmz_blk2sect(nr_blocks) << SECTOR_SHIFT;
136d57f9da8SDamien Le Moal 	clone->bi_end_io = dmz_clone_endio;
1373b1a94c8SDamien Le Moal 	clone->bi_private = bioctx;
1383b1a94c8SDamien Le Moal 
1393b1a94c8SDamien Le Moal 	bio_advance(bio, clone->bi_iter.bi_size);
1403b1a94c8SDamien Le Moal 
141092b5648SJohn Pittman 	refcount_inc(&bioctx->ref);
142ed00aabdSChristoph Hellwig 	submit_bio_noacct(clone);
1433b1a94c8SDamien Le Moal 
144d57f9da8SDamien Le Moal 	if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone))
145d57f9da8SDamien Le Moal 		zone->wp_block += nr_blocks;
146d57f9da8SDamien Le Moal 
1473b1a94c8SDamien Le Moal 	return 0;
1483b1a94c8SDamien Le Moal }
1493b1a94c8SDamien Le Moal 
1503b1a94c8SDamien Le Moal /*
1513b1a94c8SDamien Le Moal  * Zero out pages of discarded blocks accessed by a read BIO.
1523b1a94c8SDamien Le Moal  */
dmz_handle_read_zero(struct dmz_target * dmz,struct bio * bio,sector_t chunk_block,unsigned int nr_blocks)1533b1a94c8SDamien Le Moal static void dmz_handle_read_zero(struct dmz_target *dmz, struct bio *bio,
1543b1a94c8SDamien Le Moal 				 sector_t chunk_block, unsigned int nr_blocks)
1553b1a94c8SDamien Le Moal {
1563b1a94c8SDamien Le Moal 	unsigned int size = nr_blocks << DMZ_BLOCK_SHIFT;
1573b1a94c8SDamien Le Moal 
1583b1a94c8SDamien Le Moal 	/* Clear nr_blocks */
1593b1a94c8SDamien Le Moal 	swap(bio->bi_iter.bi_size, size);
1603b1a94c8SDamien Le Moal 	zero_fill_bio(bio);
1613b1a94c8SDamien Le Moal 	swap(bio->bi_iter.bi_size, size);
1623b1a94c8SDamien Le Moal 
1633b1a94c8SDamien Le Moal 	bio_advance(bio, size);
1643b1a94c8SDamien Le Moal }
1653b1a94c8SDamien Le Moal 
1663b1a94c8SDamien Le Moal /*
1673b1a94c8SDamien Le Moal  * Process a read BIO.
1683b1a94c8SDamien Le Moal  */
dmz_handle_read(struct dmz_target * dmz,struct dm_zone * zone,struct bio * bio)1693b1a94c8SDamien Le Moal static int dmz_handle_read(struct dmz_target *dmz, struct dm_zone *zone,
1703b1a94c8SDamien Le Moal 			   struct bio *bio)
1713b1a94c8SDamien Le Moal {
17236820560SHannes Reinecke 	struct dmz_metadata *zmd = dmz->metadata;
17336820560SHannes Reinecke 	sector_t chunk_block = dmz_chunk_block(zmd, dmz_bio_block(bio));
1743b1a94c8SDamien Le Moal 	unsigned int nr_blocks = dmz_bio_blocks(bio);
1753b1a94c8SDamien Le Moal 	sector_t end_block = chunk_block + nr_blocks;
1763b1a94c8SDamien Le Moal 	struct dm_zone *rzone, *bzone;
1773b1a94c8SDamien Le Moal 	int ret;
1783b1a94c8SDamien Le Moal 
1793b1a94c8SDamien Le Moal 	/* Read into unmapped chunks need only zeroing the BIO buffer */
1803b1a94c8SDamien Le Moal 	if (!zone) {
1813b1a94c8SDamien Le Moal 		zero_fill_bio(bio);
1823b1a94c8SDamien Le Moal 		return 0;
1833b1a94c8SDamien Le Moal 	}
1843b1a94c8SDamien Le Moal 
1852234e732SHannes Reinecke 	DMDEBUG("(%s): READ chunk %llu -> %s zone %u, block %llu, %u blocks",
1862234e732SHannes Reinecke 		dmz_metadata_label(zmd),
18736820560SHannes Reinecke 		(unsigned long long)dmz_bio_chunk(zmd, bio),
18834f5affdSHannes Reinecke 		(dmz_is_rnd(zone) ? "RND" :
18934f5affdSHannes Reinecke 		 (dmz_is_cache(zone) ? "CACHE" : "SEQ")),
190b7122873SHannes Reinecke 		zone->id,
1913b1a94c8SDamien Le Moal 		(unsigned long long)chunk_block, nr_blocks);
1923b1a94c8SDamien Le Moal 
1933b1a94c8SDamien Le Moal 	/* Check block validity to determine the read location */
1943b1a94c8SDamien Le Moal 	bzone = zone->bzone;
1953b1a94c8SDamien Le Moal 	while (chunk_block < end_block) {
1963b1a94c8SDamien Le Moal 		nr_blocks = 0;
19734f5affdSHannes Reinecke 		if (dmz_is_rnd(zone) || dmz_is_cache(zone) ||
19834f5affdSHannes Reinecke 		    chunk_block < zone->wp_block) {
1993b1a94c8SDamien Le Moal 			/* Test block validity in the data zone */
20036820560SHannes Reinecke 			ret = dmz_block_valid(zmd, zone, chunk_block);
2013b1a94c8SDamien Le Moal 			if (ret < 0)
2023b1a94c8SDamien Le Moal 				return ret;
2033b1a94c8SDamien Le Moal 			if (ret > 0) {
2043b1a94c8SDamien Le Moal 				/* Read data zone blocks */
2053b1a94c8SDamien Le Moal 				nr_blocks = ret;
2063b1a94c8SDamien Le Moal 				rzone = zone;
2073b1a94c8SDamien Le Moal 			}
2083b1a94c8SDamien Le Moal 		}
2093b1a94c8SDamien Le Moal 
2103b1a94c8SDamien Le Moal 		/*
2113b1a94c8SDamien Le Moal 		 * No valid blocks found in the data zone.
2123b1a94c8SDamien Le Moal 		 * Check the buffer zone, if there is one.
2133b1a94c8SDamien Le Moal 		 */
2143b1a94c8SDamien Le Moal 		if (!nr_blocks && bzone) {
21536820560SHannes Reinecke 			ret = dmz_block_valid(zmd, bzone, chunk_block);
2163b1a94c8SDamien Le Moal 			if (ret < 0)
2173b1a94c8SDamien Le Moal 				return ret;
2183b1a94c8SDamien Le Moal 			if (ret > 0) {
2193b1a94c8SDamien Le Moal 				/* Read buffer zone blocks */
2203b1a94c8SDamien Le Moal 				nr_blocks = ret;
2213b1a94c8SDamien Le Moal 				rzone = bzone;
2223b1a94c8SDamien Le Moal 			}
2233b1a94c8SDamien Le Moal 		}
2243b1a94c8SDamien Le Moal 
2253b1a94c8SDamien Le Moal 		if (nr_blocks) {
2263b1a94c8SDamien Le Moal 			/* Valid blocks found: read them */
22752d67758SHannes Reinecke 			nr_blocks = min_t(unsigned int, nr_blocks,
22852d67758SHannes Reinecke 					  end_block - chunk_block);
22952d67758SHannes Reinecke 			ret = dmz_submit_bio(dmz, rzone, bio,
23052d67758SHannes Reinecke 					     chunk_block, nr_blocks);
2313b1a94c8SDamien Le Moal 			if (ret)
2323b1a94c8SDamien Le Moal 				return ret;
2333b1a94c8SDamien Le Moal 			chunk_block += nr_blocks;
2343b1a94c8SDamien Le Moal 		} else {
2353b1a94c8SDamien Le Moal 			/* No valid block: zeroout the current BIO block */
2363b1a94c8SDamien Le Moal 			dmz_handle_read_zero(dmz, bio, chunk_block, 1);
2373b1a94c8SDamien Le Moal 			chunk_block++;
2383b1a94c8SDamien Le Moal 		}
2393b1a94c8SDamien Le Moal 	}
2403b1a94c8SDamien Le Moal 
2413b1a94c8SDamien Le Moal 	return 0;
2423b1a94c8SDamien Le Moal }
2433b1a94c8SDamien Le Moal 
2443b1a94c8SDamien Le Moal /*
2453b1a94c8SDamien Le Moal  * Write blocks directly in a data zone, at the write pointer.
2463b1a94c8SDamien Le Moal  * If a buffer zone is assigned, invalidate the blocks written
2473b1a94c8SDamien Le Moal  * in place.
2483b1a94c8SDamien Le Moal  */
dmz_handle_direct_write(struct dmz_target * dmz,struct dm_zone * zone,struct bio * bio,sector_t chunk_block,unsigned int nr_blocks)2493b1a94c8SDamien Le Moal static int dmz_handle_direct_write(struct dmz_target *dmz,
2503b1a94c8SDamien Le Moal 				   struct dm_zone *zone, struct bio *bio,
2513b1a94c8SDamien Le Moal 				   sector_t chunk_block,
2523b1a94c8SDamien Le Moal 				   unsigned int nr_blocks)
2533b1a94c8SDamien Le Moal {
2543b1a94c8SDamien Le Moal 	struct dmz_metadata *zmd = dmz->metadata;
2553b1a94c8SDamien Le Moal 	struct dm_zone *bzone = zone->bzone;
2563b1a94c8SDamien Le Moal 	int ret;
2573b1a94c8SDamien Le Moal 
2583b1a94c8SDamien Le Moal 	if (dmz_is_readonly(zone))
2593b1a94c8SDamien Le Moal 		return -EROFS;
2603b1a94c8SDamien Le Moal 
2613b1a94c8SDamien Le Moal 	/* Submit write */
262d57f9da8SDamien Le Moal 	ret = dmz_submit_bio(dmz, zone, bio, chunk_block, nr_blocks);
263d57f9da8SDamien Le Moal 	if (ret)
264d57f9da8SDamien Le Moal 		return ret;
2653b1a94c8SDamien Le Moal 
2663b1a94c8SDamien Le Moal 	/*
2673b1a94c8SDamien Le Moal 	 * Validate the blocks in the data zone and invalidate
2683b1a94c8SDamien Le Moal 	 * in the buffer zone, if there is one.
2693b1a94c8SDamien Le Moal 	 */
2703b1a94c8SDamien Le Moal 	ret = dmz_validate_blocks(zmd, zone, chunk_block, nr_blocks);
2713b1a94c8SDamien Le Moal 	if (ret == 0 && bzone)
2723b1a94c8SDamien Le Moal 		ret = dmz_invalidate_blocks(zmd, bzone, chunk_block, nr_blocks);
2733b1a94c8SDamien Le Moal 
2743b1a94c8SDamien Le Moal 	return ret;
2753b1a94c8SDamien Le Moal }
2763b1a94c8SDamien Le Moal 
2773b1a94c8SDamien Le Moal /*
2783b1a94c8SDamien Le Moal  * Write blocks in the buffer zone of @zone.
2793b1a94c8SDamien Le Moal  * If no buffer zone is assigned yet, get one.
2803b1a94c8SDamien Le Moal  * Called with @zone write locked.
2813b1a94c8SDamien Le Moal  */
dmz_handle_buffered_write(struct dmz_target * dmz,struct dm_zone * zone,struct bio * bio,sector_t chunk_block,unsigned int nr_blocks)2823b1a94c8SDamien Le Moal static int dmz_handle_buffered_write(struct dmz_target *dmz,
2833b1a94c8SDamien Le Moal 				     struct dm_zone *zone, struct bio *bio,
2843b1a94c8SDamien Le Moal 				     sector_t chunk_block,
2853b1a94c8SDamien Le Moal 				     unsigned int nr_blocks)
2863b1a94c8SDamien Le Moal {
2873b1a94c8SDamien Le Moal 	struct dmz_metadata *zmd = dmz->metadata;
2883b1a94c8SDamien Le Moal 	struct dm_zone *bzone;
2893b1a94c8SDamien Le Moal 	int ret;
2903b1a94c8SDamien Le Moal 
2913b1a94c8SDamien Le Moal 	/* Get the buffer zone. One will be allocated if needed */
2923b1a94c8SDamien Le Moal 	bzone = dmz_get_chunk_buffer(zmd, zone);
29375d66ffbSDmitry Fomichev 	if (IS_ERR(bzone))
29475d66ffbSDmitry Fomichev 		return PTR_ERR(bzone);
2953b1a94c8SDamien Le Moal 
2963b1a94c8SDamien Le Moal 	if (dmz_is_readonly(bzone))
2973b1a94c8SDamien Le Moal 		return -EROFS;
2983b1a94c8SDamien Le Moal 
2993b1a94c8SDamien Le Moal 	/* Submit write */
300d57f9da8SDamien Le Moal 	ret = dmz_submit_bio(dmz, bzone, bio, chunk_block, nr_blocks);
301d57f9da8SDamien Le Moal 	if (ret)
302d57f9da8SDamien Le Moal 		return ret;
3033b1a94c8SDamien Le Moal 
3043b1a94c8SDamien Le Moal 	/*
3053b1a94c8SDamien Le Moal 	 * Validate the blocks in the buffer zone
3063b1a94c8SDamien Le Moal 	 * and invalidate in the data zone.
3073b1a94c8SDamien Le Moal 	 */
3083b1a94c8SDamien Le Moal 	ret = dmz_validate_blocks(zmd, bzone, chunk_block, nr_blocks);
3093b1a94c8SDamien Le Moal 	if (ret == 0 && chunk_block < zone->wp_block)
3103b1a94c8SDamien Le Moal 		ret = dmz_invalidate_blocks(zmd, zone, chunk_block, nr_blocks);
3113b1a94c8SDamien Le Moal 
3123b1a94c8SDamien Le Moal 	return ret;
3133b1a94c8SDamien Le Moal }
3143b1a94c8SDamien Le Moal 
3153b1a94c8SDamien Le Moal /*
3163b1a94c8SDamien Le Moal  * Process a write BIO.
3173b1a94c8SDamien Le Moal  */
dmz_handle_write(struct dmz_target * dmz,struct dm_zone * zone,struct bio * bio)3183b1a94c8SDamien Le Moal static int dmz_handle_write(struct dmz_target *dmz, struct dm_zone *zone,
3193b1a94c8SDamien Le Moal 			    struct bio *bio)
3203b1a94c8SDamien Le Moal {
32136820560SHannes Reinecke 	struct dmz_metadata *zmd = dmz->metadata;
32236820560SHannes Reinecke 	sector_t chunk_block = dmz_chunk_block(zmd, dmz_bio_block(bio));
3233b1a94c8SDamien Le Moal 	unsigned int nr_blocks = dmz_bio_blocks(bio);
3243b1a94c8SDamien Le Moal 
3253b1a94c8SDamien Le Moal 	if (!zone)
3263b1a94c8SDamien Le Moal 		return -ENOSPC;
3273b1a94c8SDamien Le Moal 
3282234e732SHannes Reinecke 	DMDEBUG("(%s): WRITE chunk %llu -> %s zone %u, block %llu, %u blocks",
3292234e732SHannes Reinecke 		dmz_metadata_label(zmd),
33036820560SHannes Reinecke 		(unsigned long long)dmz_bio_chunk(zmd, bio),
33134f5affdSHannes Reinecke 		(dmz_is_rnd(zone) ? "RND" :
33234f5affdSHannes Reinecke 		 (dmz_is_cache(zone) ? "CACHE" : "SEQ")),
333b7122873SHannes Reinecke 		zone->id,
3343b1a94c8SDamien Le Moal 		(unsigned long long)chunk_block, nr_blocks);
3353b1a94c8SDamien Le Moal 
33634f5affdSHannes Reinecke 	if (dmz_is_rnd(zone) || dmz_is_cache(zone) ||
33734f5affdSHannes Reinecke 	    chunk_block == zone->wp_block) {
3383b1a94c8SDamien Le Moal 		/*
3393b1a94c8SDamien Le Moal 		 * zone is a random zone or it is a sequential zone
3403b1a94c8SDamien Le Moal 		 * and the BIO is aligned to the zone write pointer:
3413b1a94c8SDamien Le Moal 		 * direct write the zone.
3423b1a94c8SDamien Le Moal 		 */
34352d67758SHannes Reinecke 		return dmz_handle_direct_write(dmz, zone, bio,
34452d67758SHannes Reinecke 					       chunk_block, nr_blocks);
3453b1a94c8SDamien Le Moal 	}
3463b1a94c8SDamien Le Moal 
3473b1a94c8SDamien Le Moal 	/*
3483b1a94c8SDamien Le Moal 	 * This is an unaligned write in a sequential zone:
3493b1a94c8SDamien Le Moal 	 * use buffered write.
3503b1a94c8SDamien Le Moal 	 */
3513b1a94c8SDamien Le Moal 	return dmz_handle_buffered_write(dmz, zone, bio, chunk_block, nr_blocks);
3523b1a94c8SDamien Le Moal }
3533b1a94c8SDamien Le Moal 
3543b1a94c8SDamien Le Moal /*
3553b1a94c8SDamien Le Moal  * Process a discard BIO.
3563b1a94c8SDamien Le Moal  */
dmz_handle_discard(struct dmz_target * dmz,struct dm_zone * zone,struct bio * bio)3573b1a94c8SDamien Le Moal static int dmz_handle_discard(struct dmz_target *dmz, struct dm_zone *zone,
3583b1a94c8SDamien Le Moal 			      struct bio *bio)
3593b1a94c8SDamien Le Moal {
3603b1a94c8SDamien Le Moal 	struct dmz_metadata *zmd = dmz->metadata;
3613b1a94c8SDamien Le Moal 	sector_t block = dmz_bio_block(bio);
3623b1a94c8SDamien Le Moal 	unsigned int nr_blocks = dmz_bio_blocks(bio);
36336820560SHannes Reinecke 	sector_t chunk_block = dmz_chunk_block(zmd, block);
3643b1a94c8SDamien Le Moal 	int ret = 0;
3653b1a94c8SDamien Le Moal 
3663b1a94c8SDamien Le Moal 	/* For unmapped chunks, there is nothing to do */
3673b1a94c8SDamien Le Moal 	if (!zone)
3683b1a94c8SDamien Le Moal 		return 0;
3693b1a94c8SDamien Le Moal 
3703b1a94c8SDamien Le Moal 	if (dmz_is_readonly(zone))
3713b1a94c8SDamien Le Moal 		return -EROFS;
3723b1a94c8SDamien Le Moal 
3732234e732SHannes Reinecke 	DMDEBUG("(%s): DISCARD chunk %llu -> zone %u, block %llu, %u blocks",
3742234e732SHannes Reinecke 		dmz_metadata_label(dmz->metadata),
37536820560SHannes Reinecke 		(unsigned long long)dmz_bio_chunk(zmd, bio),
376b7122873SHannes Reinecke 		zone->id,
3773b1a94c8SDamien Le Moal 		(unsigned long long)chunk_block, nr_blocks);
3783b1a94c8SDamien Le Moal 
3793b1a94c8SDamien Le Moal 	/*
3803b1a94c8SDamien Le Moal 	 * Invalidate blocks in the data zone and its
3813b1a94c8SDamien Le Moal 	 * buffer zone if one is mapped.
3823b1a94c8SDamien Le Moal 	 */
38334f5affdSHannes Reinecke 	if (dmz_is_rnd(zone) || dmz_is_cache(zone) ||
38434f5affdSHannes Reinecke 	    chunk_block < zone->wp_block)
3853b1a94c8SDamien Le Moal 		ret = dmz_invalidate_blocks(zmd, zone, chunk_block, nr_blocks);
3863b1a94c8SDamien Le Moal 	if (ret == 0 && zone->bzone)
3873b1a94c8SDamien Le Moal 		ret = dmz_invalidate_blocks(zmd, zone->bzone,
3883b1a94c8SDamien Le Moal 					    chunk_block, nr_blocks);
3893b1a94c8SDamien Le Moal 	return ret;
3903b1a94c8SDamien Le Moal }
3913b1a94c8SDamien Le Moal 
3923b1a94c8SDamien Le Moal /*
3933b1a94c8SDamien Le Moal  * Process a BIO.
3943b1a94c8SDamien Le Moal  */
dmz_handle_bio(struct dmz_target * dmz,struct dm_chunk_work * cw,struct bio * bio)3953b1a94c8SDamien Le Moal static void dmz_handle_bio(struct dmz_target *dmz, struct dm_chunk_work *cw,
3963b1a94c8SDamien Le Moal 			   struct bio *bio)
3973b1a94c8SDamien Le Moal {
39852d67758SHannes Reinecke 	struct dmz_bioctx *bioctx =
39952d67758SHannes Reinecke 		dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
4003b1a94c8SDamien Le Moal 	struct dmz_metadata *zmd = dmz->metadata;
4013b1a94c8SDamien Le Moal 	struct dm_zone *zone;
402174364f6SDamien Le Moal 	int ret;
4033b1a94c8SDamien Le Moal 
4043b1a94c8SDamien Le Moal 	dmz_lock_metadata(zmd);
4053b1a94c8SDamien Le Moal 
4063b1a94c8SDamien Le Moal 	/*
4073b1a94c8SDamien Le Moal 	 * Get the data zone mapping the chunk. There may be no
4083b1a94c8SDamien Le Moal 	 * mapping for read and discard. If a mapping is obtained,
4093b1a94c8SDamien Le Moal 	 + the zone returned will be set to active state.
4103b1a94c8SDamien Le Moal 	 */
41136820560SHannes Reinecke 	zone = dmz_get_chunk_mapping(zmd, dmz_bio_chunk(zmd, bio),
4123b1a94c8SDamien Le Moal 				     bio_op(bio));
4133b1a94c8SDamien Le Moal 	if (IS_ERR(zone)) {
4143b1a94c8SDamien Le Moal 		ret = PTR_ERR(zone);
4153b1a94c8SDamien Le Moal 		goto out;
4163b1a94c8SDamien Le Moal 	}
4173b1a94c8SDamien Le Moal 
4183b1a94c8SDamien Le Moal 	/* Process the BIO */
4193b1a94c8SDamien Le Moal 	if (zone) {
4203b1a94c8SDamien Le Moal 		dmz_activate_zone(zone);
4213b1a94c8SDamien Le Moal 		bioctx->zone = zone;
422f97809aeSHannes Reinecke 		dmz_reclaim_bio_acc(zone->dev->reclaim);
4233b1a94c8SDamien Le Moal 	}
4243b1a94c8SDamien Le Moal 
4253b1a94c8SDamien Le Moal 	switch (bio_op(bio)) {
4263b1a94c8SDamien Le Moal 	case REQ_OP_READ:
4273b1a94c8SDamien Le Moal 		ret = dmz_handle_read(dmz, zone, bio);
4283b1a94c8SDamien Le Moal 		break;
4293b1a94c8SDamien Le Moal 	case REQ_OP_WRITE:
4303b1a94c8SDamien Le Moal 		ret = dmz_handle_write(dmz, zone, bio);
4313b1a94c8SDamien Le Moal 		break;
4323b1a94c8SDamien Le Moal 	case REQ_OP_DISCARD:
4333b1a94c8SDamien Le Moal 	case REQ_OP_WRITE_ZEROES:
4343b1a94c8SDamien Le Moal 		ret = dmz_handle_discard(dmz, zone, bio);
4353b1a94c8SDamien Le Moal 		break;
4363b1a94c8SDamien Le Moal 	default:
4372234e732SHannes Reinecke 		DMERR("(%s): Unsupported BIO operation 0x%x",
4382234e732SHannes Reinecke 		      dmz_metadata_label(dmz->metadata), bio_op(bio));
4393b1a94c8SDamien Le Moal 		ret = -EIO;
4403b1a94c8SDamien Le Moal 	}
4413b1a94c8SDamien Le Moal 
4423b1a94c8SDamien Le Moal 	/*
4433b1a94c8SDamien Le Moal 	 * Release the chunk mapping. This will check that the mapping
4443b1a94c8SDamien Le Moal 	 * is still valid, that is, that the zone used still has valid blocks.
4453b1a94c8SDamien Le Moal 	 */
4463b1a94c8SDamien Le Moal 	if (zone)
4473b1a94c8SDamien Le Moal 		dmz_put_chunk_mapping(zmd, zone);
4483b1a94c8SDamien Le Moal out:
4493b1a94c8SDamien Le Moal 	dmz_bio_endio(bio, errno_to_blk_status(ret));
4503b1a94c8SDamien Le Moal 
4513b1a94c8SDamien Le Moal 	dmz_unlock_metadata(zmd);
4523b1a94c8SDamien Le Moal }
4533b1a94c8SDamien Le Moal 
4543b1a94c8SDamien Le Moal /*
4553b1a94c8SDamien Le Moal  * Increment a chunk reference counter.
4563b1a94c8SDamien Le Moal  */
dmz_get_chunk_work(struct dm_chunk_work * cw)4573b1a94c8SDamien Le Moal static inline void dmz_get_chunk_work(struct dm_chunk_work *cw)
4583b1a94c8SDamien Le Moal {
459092b5648SJohn Pittman 	refcount_inc(&cw->refcount);
4603b1a94c8SDamien Le Moal }
4613b1a94c8SDamien Le Moal 
4623b1a94c8SDamien Le Moal /*
4633b1a94c8SDamien Le Moal  * Decrement a chunk work reference count and
4643b1a94c8SDamien Le Moal  * free it if it becomes 0.
4653b1a94c8SDamien Le Moal  */
dmz_put_chunk_work(struct dm_chunk_work * cw)4663b1a94c8SDamien Le Moal static void dmz_put_chunk_work(struct dm_chunk_work *cw)
4673b1a94c8SDamien Le Moal {
468092b5648SJohn Pittman 	if (refcount_dec_and_test(&cw->refcount)) {
4693b1a94c8SDamien Le Moal 		WARN_ON(!bio_list_empty(&cw->bio_list));
4703b1a94c8SDamien Le Moal 		radix_tree_delete(&cw->target->chunk_rxtree, cw->chunk);
4713b1a94c8SDamien Le Moal 		kfree(cw);
4723b1a94c8SDamien Le Moal 	}
4733b1a94c8SDamien Le Moal }
4743b1a94c8SDamien Le Moal 
4753b1a94c8SDamien Le Moal /*
4763b1a94c8SDamien Le Moal  * Chunk BIO work function.
4773b1a94c8SDamien Le Moal  */
dmz_chunk_work(struct work_struct * work)4783b1a94c8SDamien Le Moal static void dmz_chunk_work(struct work_struct *work)
4793b1a94c8SDamien Le Moal {
4803b1a94c8SDamien Le Moal 	struct dm_chunk_work *cw = container_of(work, struct dm_chunk_work, work);
4813b1a94c8SDamien Le Moal 	struct dmz_target *dmz = cw->target;
4823b1a94c8SDamien Le Moal 	struct bio *bio;
4833b1a94c8SDamien Le Moal 
4843b1a94c8SDamien Le Moal 	mutex_lock(&dmz->chunk_lock);
4853b1a94c8SDamien Le Moal 
4863b1a94c8SDamien Le Moal 	/* Process the chunk BIOs */
4873b1a94c8SDamien Le Moal 	while ((bio = bio_list_pop(&cw->bio_list))) {
4883b1a94c8SDamien Le Moal 		mutex_unlock(&dmz->chunk_lock);
4893b1a94c8SDamien Le Moal 		dmz_handle_bio(dmz, cw, bio);
4903b1a94c8SDamien Le Moal 		mutex_lock(&dmz->chunk_lock);
4913b1a94c8SDamien Le Moal 		dmz_put_chunk_work(cw);
4923b1a94c8SDamien Le Moal 	}
4933b1a94c8SDamien Le Moal 
4943b1a94c8SDamien Le Moal 	/* Queueing the work incremented the work refcount */
4953b1a94c8SDamien Le Moal 	dmz_put_chunk_work(cw);
4963b1a94c8SDamien Le Moal 
4973b1a94c8SDamien Le Moal 	mutex_unlock(&dmz->chunk_lock);
4983b1a94c8SDamien Le Moal }
4993b1a94c8SDamien Le Moal 
5003b1a94c8SDamien Le Moal /*
5013b1a94c8SDamien Le Moal  * Flush work.
5023b1a94c8SDamien Le Moal  */
dmz_flush_work(struct work_struct * work)5033b1a94c8SDamien Le Moal static void dmz_flush_work(struct work_struct *work)
5043b1a94c8SDamien Le Moal {
5053b1a94c8SDamien Le Moal 	struct dmz_target *dmz = container_of(work, struct dmz_target, flush_work.work);
5063b1a94c8SDamien Le Moal 	struct bio *bio;
5073b1a94c8SDamien Le Moal 	int ret;
5083b1a94c8SDamien Le Moal 
5093b1a94c8SDamien Le Moal 	/* Flush dirty metadata blocks */
5103b1a94c8SDamien Le Moal 	ret = dmz_flush_metadata(dmz->metadata);
51175d66ffbSDmitry Fomichev 	if (ret)
51249de3b7dSHannes Reinecke 		DMDEBUG("(%s): Metadata flush failed, rc=%d",
5132234e732SHannes Reinecke 			dmz_metadata_label(dmz->metadata), ret);
5143b1a94c8SDamien Le Moal 
5153b1a94c8SDamien Le Moal 	/* Process queued flush requests */
5163b1a94c8SDamien Le Moal 	while (1) {
5173b1a94c8SDamien Le Moal 		spin_lock(&dmz->flush_lock);
5183b1a94c8SDamien Le Moal 		bio = bio_list_pop(&dmz->flush_list);
5193b1a94c8SDamien Le Moal 		spin_unlock(&dmz->flush_lock);
5203b1a94c8SDamien Le Moal 
5213b1a94c8SDamien Le Moal 		if (!bio)
5223b1a94c8SDamien Le Moal 			break;
5233b1a94c8SDamien Le Moal 
5243b1a94c8SDamien Le Moal 		dmz_bio_endio(bio, errno_to_blk_status(ret));
5253b1a94c8SDamien Le Moal 	}
5263b1a94c8SDamien Le Moal 
5273b1a94c8SDamien Le Moal 	queue_delayed_work(dmz->flush_wq, &dmz->flush_work, DMZ_FLUSH_PERIOD);
5283b1a94c8SDamien Le Moal }
5293b1a94c8SDamien Le Moal 
5303b1a94c8SDamien Le Moal /*
5313b1a94c8SDamien Le Moal  * Get a chunk work and start it to process a new BIO.
5323b1a94c8SDamien Le Moal  * If the BIO chunk has no work yet, create one.
5333b1a94c8SDamien Le Moal  */
dmz_queue_chunk_work(struct dmz_target * dmz,struct bio * bio)534d7428c50SDmitry Fomichev static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio)
5353b1a94c8SDamien Le Moal {
53636820560SHannes Reinecke 	unsigned int chunk = dmz_bio_chunk(dmz->metadata, bio);
5373b1a94c8SDamien Le Moal 	struct dm_chunk_work *cw;
538d7428c50SDmitry Fomichev 	int ret = 0;
5393b1a94c8SDamien Le Moal 
5403b1a94c8SDamien Le Moal 	mutex_lock(&dmz->chunk_lock);
5413b1a94c8SDamien Le Moal 
5423b1a94c8SDamien Le Moal 	/* Get the BIO chunk work. If one is not active yet, create one */
5433b1a94c8SDamien Le Moal 	cw = radix_tree_lookup(&dmz->chunk_rxtree, chunk);
544ee63634bSShin'ichiro Kawasaki 	if (cw) {
545ee63634bSShin'ichiro Kawasaki 		dmz_get_chunk_work(cw);
546ee63634bSShin'ichiro Kawasaki 	} else {
5473b1a94c8SDamien Le Moal 		/* Create a new chunk work */
5484218a955SDamien Le Moal 		cw = kmalloc(sizeof(struct dm_chunk_work), GFP_NOIO);
549d7428c50SDmitry Fomichev 		if (unlikely(!cw)) {
550d7428c50SDmitry Fomichev 			ret = -ENOMEM;
5513b1a94c8SDamien Le Moal 			goto out;
552d7428c50SDmitry Fomichev 		}
5533b1a94c8SDamien Le Moal 
5543b1a94c8SDamien Le Moal 		INIT_WORK(&cw->work, dmz_chunk_work);
555ee63634bSShin'ichiro Kawasaki 		refcount_set(&cw->refcount, 1);
5563b1a94c8SDamien Le Moal 		cw->target = dmz;
5573b1a94c8SDamien Le Moal 		cw->chunk = chunk;
5583b1a94c8SDamien Le Moal 		bio_list_init(&cw->bio_list);
5593b1a94c8SDamien Le Moal 
5603b1a94c8SDamien Le Moal 		ret = radix_tree_insert(&dmz->chunk_rxtree, chunk, cw);
5613b1a94c8SDamien Le Moal 		if (unlikely(ret)) {
5623b1a94c8SDamien Le Moal 			kfree(cw);
5633b1a94c8SDamien Le Moal 			goto out;
5643b1a94c8SDamien Le Moal 		}
5653b1a94c8SDamien Le Moal 	}
5663b1a94c8SDamien Le Moal 
5673b1a94c8SDamien Le Moal 	bio_list_add(&cw->bio_list, bio);
5683b1a94c8SDamien Le Moal 
5693b1a94c8SDamien Le Moal 	if (queue_work(dmz->chunk_wq, &cw->work))
5703b1a94c8SDamien Le Moal 		dmz_get_chunk_work(cw);
5713b1a94c8SDamien Le Moal out:
5723b1a94c8SDamien Le Moal 	mutex_unlock(&dmz->chunk_lock);
573d7428c50SDmitry Fomichev 	return ret;
5743b1a94c8SDamien Le Moal }
5753b1a94c8SDamien Le Moal 
5763b1a94c8SDamien Le Moal /*
577e7fad909SDmitry Fomichev  * Check if the backing device is being removed. If it's on the way out,
57875d66ffbSDmitry Fomichev  * start failing I/O. Reclaim and metadata components also call this
57975d66ffbSDmitry Fomichev  * function to cleanly abort operation in the event of such failure.
58075d66ffbSDmitry Fomichev  */
dmz_bdev_is_dying(struct dmz_dev * dmz_dev)58175d66ffbSDmitry Fomichev bool dmz_bdev_is_dying(struct dmz_dev *dmz_dev)
58275d66ffbSDmitry Fomichev {
583e7fad909SDmitry Fomichev 	if (dmz_dev->flags & DMZ_BDEV_DYING)
584e7fad909SDmitry Fomichev 		return true;
58575d66ffbSDmitry Fomichev 
586e7fad909SDmitry Fomichev 	if (dmz_dev->flags & DMZ_CHECK_BDEV)
587e7fad909SDmitry Fomichev 		return !dmz_check_bdev(dmz_dev);
588e7fad909SDmitry Fomichev 
58975d66ffbSDmitry Fomichev 	if (blk_queue_dying(bdev_get_queue(dmz_dev->bdev))) {
59075d66ffbSDmitry Fomichev 		dmz_dev_warn(dmz_dev, "Backing device queue dying");
59175d66ffbSDmitry Fomichev 		dmz_dev->flags |= DMZ_BDEV_DYING;
59275d66ffbSDmitry Fomichev 	}
59375d66ffbSDmitry Fomichev 
59475d66ffbSDmitry Fomichev 	return dmz_dev->flags & DMZ_BDEV_DYING;
59575d66ffbSDmitry Fomichev }
59675d66ffbSDmitry Fomichev 
59775d66ffbSDmitry Fomichev /*
598e7fad909SDmitry Fomichev  * Check the backing device availability. This detects such events as
599e7fad909SDmitry Fomichev  * backing device going offline due to errors, media removals, etc.
600e7fad909SDmitry Fomichev  * This check is less efficient than dmz_bdev_is_dying() and should
601e7fad909SDmitry Fomichev  * only be performed as a part of error handling.
602e7fad909SDmitry Fomichev  */
dmz_check_bdev(struct dmz_dev * dmz_dev)603e7fad909SDmitry Fomichev bool dmz_check_bdev(struct dmz_dev *dmz_dev)
604e7fad909SDmitry Fomichev {
605e7fad909SDmitry Fomichev 	struct gendisk *disk;
606e7fad909SDmitry Fomichev 
607e7fad909SDmitry Fomichev 	dmz_dev->flags &= ~DMZ_CHECK_BDEV;
608e7fad909SDmitry Fomichev 
609e7fad909SDmitry Fomichev 	if (dmz_bdev_is_dying(dmz_dev))
610e7fad909SDmitry Fomichev 		return false;
611e7fad909SDmitry Fomichev 
612e7fad909SDmitry Fomichev 	disk = dmz_dev->bdev->bd_disk;
613e7fad909SDmitry Fomichev 	if (disk->fops->check_events &&
614e7fad909SDmitry Fomichev 	    disk->fops->check_events(disk, 0) & DISK_EVENT_MEDIA_CHANGE) {
615e7fad909SDmitry Fomichev 		dmz_dev_warn(dmz_dev, "Backing device offline");
616e7fad909SDmitry Fomichev 		dmz_dev->flags |= DMZ_BDEV_DYING;
617e7fad909SDmitry Fomichev 	}
618e7fad909SDmitry Fomichev 
619e7fad909SDmitry Fomichev 	return !(dmz_dev->flags & DMZ_BDEV_DYING);
620e7fad909SDmitry Fomichev }
621e7fad909SDmitry Fomichev 
622e7fad909SDmitry Fomichev /*
6233b1a94c8SDamien Le Moal  * Process a new BIO.
6243b1a94c8SDamien Le Moal  */
dmz_map(struct dm_target * ti,struct bio * bio)6253b1a94c8SDamien Le Moal static int dmz_map(struct dm_target *ti, struct bio *bio)
6263b1a94c8SDamien Le Moal {
6273b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
62836820560SHannes Reinecke 	struct dmz_metadata *zmd = dmz->metadata;
6293b1a94c8SDamien Le Moal 	struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
6303b1a94c8SDamien Le Moal 	sector_t sector = bio->bi_iter.bi_sector;
6313b1a94c8SDamien Le Moal 	unsigned int nr_sectors = bio_sectors(bio);
6323b1a94c8SDamien Le Moal 	sector_t chunk_sector;
633d7428c50SDmitry Fomichev 	int ret;
6343b1a94c8SDamien Le Moal 
635d0e21ce4SHannes Reinecke 	if (dmz_dev_is_dying(zmd))
63675d66ffbSDmitry Fomichev 		return DM_MAPIO_KILL;
63775d66ffbSDmitry Fomichev 
6382234e732SHannes Reinecke 	DMDEBUG("(%s): BIO op %d sector %llu + %u => chunk %llu, block %llu, %u blocks",
6392234e732SHannes Reinecke 		dmz_metadata_label(zmd),
6403b1a94c8SDamien Le Moal 		bio_op(bio), (unsigned long long)sector, nr_sectors,
64136820560SHannes Reinecke 		(unsigned long long)dmz_bio_chunk(zmd, bio),
64236820560SHannes Reinecke 		(unsigned long long)dmz_chunk_block(zmd, dmz_bio_block(bio)),
6433b1a94c8SDamien Le Moal 		(unsigned int)dmz_bio_blocks(bio));
6443b1a94c8SDamien Le Moal 
645edbe9597SMikulas Patocka 	if (!nr_sectors && bio_op(bio) != REQ_OP_WRITE)
6463b1a94c8SDamien Le Moal 		return DM_MAPIO_REMAPPED;
6473b1a94c8SDamien Le Moal 
6483b1a94c8SDamien Le Moal 	/* The BIO should be block aligned */
6493b1a94c8SDamien Le Moal 	if ((nr_sectors & DMZ_BLOCK_SECTORS_MASK) || (sector & DMZ_BLOCK_SECTORS_MASK))
6503b1a94c8SDamien Le Moal 		return DM_MAPIO_KILL;
6513b1a94c8SDamien Le Moal 
6523b1a94c8SDamien Le Moal 	/* Initialize the BIO context */
65352d67758SHannes Reinecke 	bioctx->dev = NULL;
6543b1a94c8SDamien Le Moal 	bioctx->zone = NULL;
6553b1a94c8SDamien Le Moal 	bioctx->bio = bio;
656092b5648SJohn Pittman 	refcount_set(&bioctx->ref, 1);
6573b1a94c8SDamien Le Moal 
6583b1a94c8SDamien Le Moal 	/* Set the BIO pending in the flush list */
659edbe9597SMikulas Patocka 	if (!nr_sectors && bio_op(bio) == REQ_OP_WRITE) {
6603b1a94c8SDamien Le Moal 		spin_lock(&dmz->flush_lock);
6613b1a94c8SDamien Le Moal 		bio_list_add(&dmz->flush_list, bio);
6623b1a94c8SDamien Le Moal 		spin_unlock(&dmz->flush_lock);
6633b1a94c8SDamien Le Moal 		mod_delayed_work(dmz->flush_wq, &dmz->flush_work, 0);
6643b1a94c8SDamien Le Moal 		return DM_MAPIO_SUBMITTED;
6653b1a94c8SDamien Le Moal 	}
6663b1a94c8SDamien Le Moal 
6673b1a94c8SDamien Le Moal 	/* Split zone BIOs to fit entirely into a zone */
66836820560SHannes Reinecke 	chunk_sector = sector & (dmz_zone_nr_sectors(zmd) - 1);
66936820560SHannes Reinecke 	if (chunk_sector + nr_sectors > dmz_zone_nr_sectors(zmd))
67036820560SHannes Reinecke 		dm_accept_partial_bio(bio, dmz_zone_nr_sectors(zmd) - chunk_sector);
6713b1a94c8SDamien Le Moal 
6723b1a94c8SDamien Le Moal 	/* Now ready to handle this BIO */
673d7428c50SDmitry Fomichev 	ret = dmz_queue_chunk_work(dmz, bio);
674d7428c50SDmitry Fomichev 	if (ret) {
67549de3b7dSHannes Reinecke 		DMDEBUG("(%s): BIO op %d, can't process chunk %llu, err %i",
6762234e732SHannes Reinecke 			dmz_metadata_label(zmd),
67736820560SHannes Reinecke 			bio_op(bio), (u64)dmz_bio_chunk(zmd, bio),
678d7428c50SDmitry Fomichev 			ret);
679d7428c50SDmitry Fomichev 		return DM_MAPIO_REQUEUE;
680d7428c50SDmitry Fomichev 	}
6813b1a94c8SDamien Le Moal 
6823b1a94c8SDamien Le Moal 	return DM_MAPIO_SUBMITTED;
6833b1a94c8SDamien Le Moal }
6843b1a94c8SDamien Le Moal 
6853b1a94c8SDamien Le Moal /*
6863b1a94c8SDamien Le Moal  * Get zoned device information.
6873b1a94c8SDamien Le Moal  */
dmz_get_zoned_device(struct dm_target * ti,char * path,int idx,int nr_devs)688bd5c4031SHannes Reinecke static int dmz_get_zoned_device(struct dm_target *ti, char *path,
689bd5c4031SHannes Reinecke 				int idx, int nr_devs)
6903b1a94c8SDamien Le Moal {
6913b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
692bd5c4031SHannes Reinecke 	struct dm_dev *ddev;
6933b1a94c8SDamien Le Moal 	struct dmz_dev *dev;
6943b1a94c8SDamien Le Moal 	int ret;
695bd5c4031SHannes Reinecke 	struct block_device *bdev;
6963b1a94c8SDamien Le Moal 
6973b1a94c8SDamien Le Moal 	/* Get the target device */
698bd5c4031SHannes Reinecke 	ret = dm_get_device(ti, path, dm_table_get_mode(ti->table), &ddev);
6993b1a94c8SDamien Le Moal 	if (ret) {
7003b1a94c8SDamien Le Moal 		ti->error = "Get target device failed";
7013b1a94c8SDamien Le Moal 		return ret;
7023b1a94c8SDamien Le Moal 	}
7033b1a94c8SDamien Le Moal 
704bd5c4031SHannes Reinecke 	bdev = ddev->bdev;
705bd5c4031SHannes Reinecke 	if (bdev_zoned_model(bdev) == BLK_ZONED_NONE) {
706bd5c4031SHannes Reinecke 		if (nr_devs == 1) {
707bd5c4031SHannes Reinecke 			ti->error = "Invalid regular device";
7083b1a94c8SDamien Le Moal 			goto err;
7093b1a94c8SDamien Le Moal 		}
710bd5c4031SHannes Reinecke 		if (idx != 0) {
711bd5c4031SHannes Reinecke 			ti->error = "First device must be a regular device";
712bd5c4031SHannes Reinecke 			goto err;
713bd5c4031SHannes Reinecke 		}
714bd5c4031SHannes Reinecke 		if (dmz->ddev[0]) {
715bd5c4031SHannes Reinecke 			ti->error = "Too many regular devices";
716bd5c4031SHannes Reinecke 			goto err;
717bd5c4031SHannes Reinecke 		}
718bd5c4031SHannes Reinecke 		dev = &dmz->dev[idx];
719bd5c4031SHannes Reinecke 		dev->flags = DMZ_BDEV_REGULAR;
720bd5c4031SHannes Reinecke 	} else {
721bd5c4031SHannes Reinecke 		if (dmz->ddev[idx]) {
722bd5c4031SHannes Reinecke 			ti->error = "Too many zoned devices";
723bd5c4031SHannes Reinecke 			goto err;
724bd5c4031SHannes Reinecke 		}
725bd5c4031SHannes Reinecke 		if (nr_devs > 1 && idx == 0) {
726bd5c4031SHannes Reinecke 			ti->error = "First device must be a regular device";
727bd5c4031SHannes Reinecke 			goto err;
728bd5c4031SHannes Reinecke 		}
729bd5c4031SHannes Reinecke 		dev = &dmz->dev[idx];
730bd5c4031SHannes Reinecke 	}
731bd5c4031SHannes Reinecke 	dev->bdev = bdev;
73269875d44SHannes Reinecke 	dev->dev_idx = idx;
7333b1a94c8SDamien Le Moal 
7346dcbb52cSChristoph Hellwig 	dev->capacity = bdev_nr_sectors(bdev);
735bd5c4031SHannes Reinecke 	if (ti->begin) {
736bd5c4031SHannes Reinecke 		ti->error = "Partial mapping is not supported";
7373b1a94c8SDamien Le Moal 		goto err;
7383b1a94c8SDamien Le Moal 	}
7393b1a94c8SDamien Le Moal 
740bd5c4031SHannes Reinecke 	dmz->ddev[idx] = ddev;
7413b1a94c8SDamien Le Moal 
7423b1a94c8SDamien Le Moal 	return 0;
7433b1a94c8SDamien Le Moal err:
744bd5c4031SHannes Reinecke 	dm_put_device(ti, ddev);
745bd5c4031SHannes Reinecke 	return -EINVAL;
7463b1a94c8SDamien Le Moal }
7473b1a94c8SDamien Le Moal 
7483b1a94c8SDamien Le Moal /*
7493b1a94c8SDamien Le Moal  * Cleanup zoned device information.
7503b1a94c8SDamien Le Moal  */
dmz_put_zoned_devices(struct dm_target * ti)751*9850ccd5SFedor Pchelkin static void dmz_put_zoned_devices(struct dm_target *ti)
7523b1a94c8SDamien Le Moal {
7533b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
754bd5c4031SHannes Reinecke 	int i;
7553b1a94c8SDamien Le Moal 
756*9850ccd5SFedor Pchelkin 	for (i = 0; i < dmz->nr_ddevs; i++)
757*9850ccd5SFedor Pchelkin 		if (dmz->ddev[i])
758bd5c4031SHannes Reinecke 			dm_put_device(ti, dmz->ddev[i]);
759*9850ccd5SFedor Pchelkin 
760*9850ccd5SFedor Pchelkin 	kfree(dmz->ddev);
761bd5c4031SHannes Reinecke }
762bd5c4031SHannes Reinecke 
dmz_fixup_devices(struct dm_target * ti)763bd5c4031SHannes Reinecke static int dmz_fixup_devices(struct dm_target *ti)
764bd5c4031SHannes Reinecke {
765bd5c4031SHannes Reinecke 	struct dmz_target *dmz = ti->private;
766fabed68cSChristoph Hellwig 	struct dmz_dev *reg_dev = NULL;
7674dba1288SHannes Reinecke 	sector_t zone_nr_sectors = 0;
7684dba1288SHannes Reinecke 	int i;
769bd5c4031SHannes Reinecke 
770bd5c4031SHannes Reinecke 	/*
7714dba1288SHannes Reinecke 	 * When we have more than on devices, the first one must be a
7724dba1288SHannes Reinecke 	 * regular block device and the others zoned block devices.
773bd5c4031SHannes Reinecke 	 */
7744dba1288SHannes Reinecke 	if (dmz->nr_ddevs > 1) {
775bd5c4031SHannes Reinecke 		reg_dev = &dmz->dev[0];
776bd5c4031SHannes Reinecke 		if (!(reg_dev->flags & DMZ_BDEV_REGULAR)) {
777bd5c4031SHannes Reinecke 			ti->error = "Primary disk is not a regular device";
778bd5c4031SHannes Reinecke 			return -EINVAL;
779bd5c4031SHannes Reinecke 		}
7804dba1288SHannes Reinecke 		for (i = 1; i < dmz->nr_ddevs; i++) {
781fabed68cSChristoph Hellwig 			struct dmz_dev *zoned_dev = &dmz->dev[i];
782fabed68cSChristoph Hellwig 			struct block_device *bdev = zoned_dev->bdev;
783fabed68cSChristoph Hellwig 
784bd5c4031SHannes Reinecke 			if (zoned_dev->flags & DMZ_BDEV_REGULAR) {
785bd5c4031SHannes Reinecke 				ti->error = "Secondary disk is not a zoned device";
786bd5c4031SHannes Reinecke 				return -EINVAL;
787bd5c4031SHannes Reinecke 			}
7884dba1288SHannes Reinecke 			if (zone_nr_sectors &&
789fabed68cSChristoph Hellwig 			    zone_nr_sectors != bdev_zone_sectors(bdev)) {
7904dba1288SHannes Reinecke 				ti->error = "Zone nr sectors mismatch";
7914dba1288SHannes Reinecke 				return -EINVAL;
7924dba1288SHannes Reinecke 			}
793fabed68cSChristoph Hellwig 			zone_nr_sectors = bdev_zone_sectors(bdev);
7944dba1288SHannes Reinecke 			zoned_dev->zone_nr_sectors = zone_nr_sectors;
795fabed68cSChristoph Hellwig 			zoned_dev->nr_zones = bdev_nr_zones(bdev);
7964dba1288SHannes Reinecke 		}
797bd5c4031SHannes Reinecke 	} else {
798fabed68cSChristoph Hellwig 		struct dmz_dev *zoned_dev = &dmz->dev[0];
799fabed68cSChristoph Hellwig 		struct block_device *bdev = zoned_dev->bdev;
800fabed68cSChristoph Hellwig 
801bd5c4031SHannes Reinecke 		if (zoned_dev->flags & DMZ_BDEV_REGULAR) {
802bd5c4031SHannes Reinecke 			ti->error = "Disk is not a zoned device";
803bd5c4031SHannes Reinecke 			return -EINVAL;
804bd5c4031SHannes Reinecke 		}
805fabed68cSChristoph Hellwig 		zoned_dev->zone_nr_sectors = bdev_zone_sectors(bdev);
806fabed68cSChristoph Hellwig 		zoned_dev->nr_zones = bdev_nr_zones(bdev);
8074dba1288SHannes Reinecke 	}
808bd5c4031SHannes Reinecke 
809bd5c4031SHannes Reinecke 	if (reg_dev) {
8104dba1288SHannes Reinecke 		sector_t zone_offset;
8114dba1288SHannes Reinecke 
8124dba1288SHannes Reinecke 		reg_dev->zone_nr_sectors = zone_nr_sectors;
81342c689f6SNathan Chancellor 		reg_dev->nr_zones =
81442c689f6SNathan Chancellor 			DIV_ROUND_UP_SECTOR_T(reg_dev->capacity,
815bd5c4031SHannes Reinecke 					      reg_dev->zone_nr_sectors);
8164dba1288SHannes Reinecke 		reg_dev->zone_offset = 0;
8174dba1288SHannes Reinecke 		zone_offset = reg_dev->nr_zones;
8184dba1288SHannes Reinecke 		for (i = 1; i < dmz->nr_ddevs; i++) {
8194dba1288SHannes Reinecke 			dmz->dev[i].zone_offset = zone_offset;
8204dba1288SHannes Reinecke 			zone_offset += dmz->dev[i].nr_zones;
8214dba1288SHannes Reinecke 		}
822bd5c4031SHannes Reinecke 	}
823bd5c4031SHannes Reinecke 	return 0;
8243b1a94c8SDamien Le Moal }
8253b1a94c8SDamien Le Moal 
8263b1a94c8SDamien Le Moal /*
8273b1a94c8SDamien Le Moal  * Setup target.
8283b1a94c8SDamien Le Moal  */
dmz_ctr(struct dm_target * ti,unsigned int argc,char ** argv)8293b1a94c8SDamien Le Moal static int dmz_ctr(struct dm_target *ti, unsigned int argc, char **argv)
8303b1a94c8SDamien Le Moal {
8313b1a94c8SDamien Le Moal 	struct dmz_target *dmz;
832f97809aeSHannes Reinecke 	int ret, i;
8333b1a94c8SDamien Le Moal 
8343b1a94c8SDamien Le Moal 	/* Check arguments */
8354dba1288SHannes Reinecke 	if (argc < 1) {
8363b1a94c8SDamien Le Moal 		ti->error = "Invalid argument count";
8373b1a94c8SDamien Le Moal 		return -EINVAL;
8383b1a94c8SDamien Le Moal 	}
8393b1a94c8SDamien Le Moal 
8403b1a94c8SDamien Le Moal 	/* Allocate and initialize the target descriptor */
8413b1a94c8SDamien Le Moal 	dmz = kzalloc(sizeof(struct dmz_target), GFP_KERNEL);
8423b1a94c8SDamien Le Moal 	if (!dmz) {
8433b1a94c8SDamien Le Moal 		ti->error = "Unable to allocate the zoned target descriptor";
8443b1a94c8SDamien Le Moal 		return -ENOMEM;
8453b1a94c8SDamien Le Moal 	}
8464dba1288SHannes Reinecke 	dmz->dev = kcalloc(argc, sizeof(struct dmz_dev), GFP_KERNEL);
847bd5c4031SHannes Reinecke 	if (!dmz->dev) {
848bd5c4031SHannes Reinecke 		ti->error = "Unable to allocate the zoned device descriptors";
849bd5c4031SHannes Reinecke 		kfree(dmz);
850bd5c4031SHannes Reinecke 		return -ENOMEM;
851bd5c4031SHannes Reinecke 	}
8524dba1288SHannes Reinecke 	dmz->ddev = kcalloc(argc, sizeof(struct dm_dev *), GFP_KERNEL);
8534dba1288SHannes Reinecke 	if (!dmz->ddev) {
8544dba1288SHannes Reinecke 		ti->error = "Unable to allocate the dm device descriptors";
8554dba1288SHannes Reinecke 		ret = -ENOMEM;
8564dba1288SHannes Reinecke 		goto err;
8574dba1288SHannes Reinecke 	}
858f97809aeSHannes Reinecke 	dmz->nr_ddevs = argc;
8594dba1288SHannes Reinecke 
8603b1a94c8SDamien Le Moal 	ti->private = dmz;
8613b1a94c8SDamien Le Moal 
8623b1a94c8SDamien Le Moal 	/* Get the target zoned block device */
8634dba1288SHannes Reinecke 	for (i = 0; i < argc; i++) {
8644dba1288SHannes Reinecke 		ret = dmz_get_zoned_device(ti, argv[i], i, argc);
865bd5c4031SHannes Reinecke 		if (ret)
8664dba1288SHannes Reinecke 			goto err_dev;
867bd5c4031SHannes Reinecke 	}
868bd5c4031SHannes Reinecke 	ret = dmz_fixup_devices(ti);
8694dba1288SHannes Reinecke 	if (ret)
8704dba1288SHannes Reinecke 		goto err_dev;
8713b1a94c8SDamien Le Moal 
8723b1a94c8SDamien Le Moal 	/* Initialize metadata */
873bd5c4031SHannes Reinecke 	ret = dmz_ctr_metadata(dmz->dev, argc, &dmz->metadata,
8742234e732SHannes Reinecke 			       dm_table_device_name(ti->table));
8753b1a94c8SDamien Le Moal 	if (ret) {
8763b1a94c8SDamien Le Moal 		ti->error = "Metadata initialization failed";
8773b1a94c8SDamien Le Moal 		goto err_dev;
8783b1a94c8SDamien Le Moal 	}
8793b1a94c8SDamien Le Moal 
8803b1a94c8SDamien Le Moal 	/* Set target (no write same support) */
8817b237748SHou Tao 	ti->max_io_len = dmz_zone_nr_sectors(dmz->metadata);
8823b1a94c8SDamien Le Moal 	ti->num_flush_bios = 1;
8833b1a94c8SDamien Le Moal 	ti->num_discard_bios = 1;
8843b1a94c8SDamien Le Moal 	ti->num_write_zeroes_bios = 1;
8853b1a94c8SDamien Le Moal 	ti->per_io_data_size = sizeof(struct dmz_bioctx);
8863b1a94c8SDamien Le Moal 	ti->flush_supported = true;
8873b1a94c8SDamien Le Moal 	ti->discards_supported = true;
8883b1a94c8SDamien Le Moal 
8893b1a94c8SDamien Le Moal 	/* The exposed capacity is the number of chunks that can be mapped */
89036820560SHannes Reinecke 	ti->len = (sector_t)dmz_nr_chunks(dmz->metadata) <<
89136820560SHannes Reinecke 		dmz_zone_nr_sectors_shift(dmz->metadata);
8923b1a94c8SDamien Le Moal 
8933b1a94c8SDamien Le Moal 	/* Zone BIO */
8946f1c819cSKent Overstreet 	ret = bioset_init(&dmz->bio_set, DMZ_MIN_BIOS, 0, 0);
8956f1c819cSKent Overstreet 	if (ret) {
8963b1a94c8SDamien Le Moal 		ti->error = "Create BIO set failed";
8973b1a94c8SDamien Le Moal 		goto err_meta;
8983b1a94c8SDamien Le Moal 	}
8993b1a94c8SDamien Le Moal 
9003b1a94c8SDamien Le Moal 	/* Chunk BIO work */
9013b1a94c8SDamien Le Moal 	mutex_init(&dmz->chunk_lock);
9022d0b2d64SBart Van Assche 	INIT_RADIX_TREE(&dmz->chunk_rxtree, GFP_NOIO);
9032234e732SHannes Reinecke 	dmz->chunk_wq = alloc_workqueue("dmz_cwq_%s",
9042234e732SHannes Reinecke 					WQ_MEM_RECLAIM | WQ_UNBOUND, 0,
9052234e732SHannes Reinecke 					dmz_metadata_label(dmz->metadata));
9063b1a94c8SDamien Le Moal 	if (!dmz->chunk_wq) {
9073b1a94c8SDamien Le Moal 		ti->error = "Create chunk workqueue failed";
9083b1a94c8SDamien Le Moal 		ret = -ENOMEM;
9093b1a94c8SDamien Le Moal 		goto err_bio;
9103b1a94c8SDamien Le Moal 	}
9113b1a94c8SDamien Le Moal 
9123b1a94c8SDamien Le Moal 	/* Flush work */
9133b1a94c8SDamien Le Moal 	spin_lock_init(&dmz->flush_lock);
9143b1a94c8SDamien Le Moal 	bio_list_init(&dmz->flush_list);
9153b1a94c8SDamien Le Moal 	INIT_DELAYED_WORK(&dmz->flush_work, dmz_flush_work);
9163b1a94c8SDamien Le Moal 	dmz->flush_wq = alloc_ordered_workqueue("dmz_fwq_%s", WQ_MEM_RECLAIM,
9172234e732SHannes Reinecke 						dmz_metadata_label(dmz->metadata));
9183b1a94c8SDamien Le Moal 	if (!dmz->flush_wq) {
9193b1a94c8SDamien Le Moal 		ti->error = "Create flush workqueue failed";
9203b1a94c8SDamien Le Moal 		ret = -ENOMEM;
9213b1a94c8SDamien Le Moal 		goto err_cwq;
9223b1a94c8SDamien Le Moal 	}
9233b1a94c8SDamien Le Moal 	mod_delayed_work(dmz->flush_wq, &dmz->flush_work, DMZ_FLUSH_PERIOD);
9243b1a94c8SDamien Le Moal 
9253b1a94c8SDamien Le Moal 	/* Initialize reclaim */
926f97809aeSHannes Reinecke 	for (i = 0; i < dmz->nr_ddevs; i++) {
927f97809aeSHannes Reinecke 		ret = dmz_ctr_reclaim(dmz->metadata, &dmz->dev[i].reclaim, i);
9283b1a94c8SDamien Le Moal 		if (ret) {
9293b1a94c8SDamien Le Moal 			ti->error = "Zone reclaim initialization failed";
9303b1a94c8SDamien Le Moal 			goto err_fwq;
9313b1a94c8SDamien Le Moal 		}
932f97809aeSHannes Reinecke 	}
9333b1a94c8SDamien Le Moal 
9342234e732SHannes Reinecke 	DMINFO("(%s): Target device: %llu 512-byte logical sectors (%llu blocks)",
9352234e732SHannes Reinecke 	       dmz_metadata_label(dmz->metadata),
9363b1a94c8SDamien Le Moal 	       (unsigned long long)ti->len,
9373b1a94c8SDamien Le Moal 	       (unsigned long long)dmz_sect2blk(ti->len));
9383b1a94c8SDamien Le Moal 
9393b1a94c8SDamien Le Moal 	return 0;
9403b1a94c8SDamien Le Moal err_fwq:
9413b1a94c8SDamien Le Moal 	destroy_workqueue(dmz->flush_wq);
9423b1a94c8SDamien Le Moal err_cwq:
9433b1a94c8SDamien Le Moal 	destroy_workqueue(dmz->chunk_wq);
9443b1a94c8SDamien Le Moal err_bio:
945d5ffebddSMike Snitzer 	mutex_destroy(&dmz->chunk_lock);
9466f1c819cSKent Overstreet 	bioset_exit(&dmz->bio_set);
9473b1a94c8SDamien Le Moal err_meta:
9483b1a94c8SDamien Le Moal 	dmz_dtr_metadata(dmz->metadata);
9493b1a94c8SDamien Le Moal err_dev:
950*9850ccd5SFedor Pchelkin 	dmz_put_zoned_devices(ti);
9513b1a94c8SDamien Le Moal err:
952bd5c4031SHannes Reinecke 	kfree(dmz->dev);
9533b1a94c8SDamien Le Moal 	kfree(dmz);
9543b1a94c8SDamien Le Moal 
9553b1a94c8SDamien Le Moal 	return ret;
9563b1a94c8SDamien Le Moal }
9573b1a94c8SDamien Le Moal 
9583b1a94c8SDamien Le Moal /*
9593b1a94c8SDamien Le Moal  * Cleanup target.
9603b1a94c8SDamien Le Moal  */
dmz_dtr(struct dm_target * ti)9613b1a94c8SDamien Le Moal static void dmz_dtr(struct dm_target *ti)
9623b1a94c8SDamien Le Moal {
9633b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
964f97809aeSHannes Reinecke 	int i;
9653b1a94c8SDamien Le Moal 
9663b1a94c8SDamien Le Moal 	destroy_workqueue(dmz->chunk_wq);
9673b1a94c8SDamien Le Moal 
968f97809aeSHannes Reinecke 	for (i = 0; i < dmz->nr_ddevs; i++)
969f97809aeSHannes Reinecke 		dmz_dtr_reclaim(dmz->dev[i].reclaim);
9703b1a94c8SDamien Le Moal 
9713b1a94c8SDamien Le Moal 	cancel_delayed_work_sync(&dmz->flush_work);
9723b1a94c8SDamien Le Moal 	destroy_workqueue(dmz->flush_wq);
9733b1a94c8SDamien Le Moal 
9743b1a94c8SDamien Le Moal 	(void) dmz_flush_metadata(dmz->metadata);
9753b1a94c8SDamien Le Moal 
9763b1a94c8SDamien Le Moal 	dmz_dtr_metadata(dmz->metadata);
9773b1a94c8SDamien Le Moal 
9786f1c819cSKent Overstreet 	bioset_exit(&dmz->bio_set);
9793b1a94c8SDamien Le Moal 
980*9850ccd5SFedor Pchelkin 	dmz_put_zoned_devices(ti);
9813b1a94c8SDamien Le Moal 
982d5ffebddSMike Snitzer 	mutex_destroy(&dmz->chunk_lock);
983d5ffebddSMike Snitzer 
984bd5c4031SHannes Reinecke 	kfree(dmz->dev);
9853b1a94c8SDamien Le Moal 	kfree(dmz);
9863b1a94c8SDamien Le Moal }
9873b1a94c8SDamien Le Moal 
9883b1a94c8SDamien Le Moal /*
9893b1a94c8SDamien Le Moal  * Setup target request queue limits.
9903b1a94c8SDamien Le Moal  */
dmz_io_hints(struct dm_target * ti,struct queue_limits * limits)9913b1a94c8SDamien Le Moal static void dmz_io_hints(struct dm_target *ti, struct queue_limits *limits)
9923b1a94c8SDamien Le Moal {
9933b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
99436820560SHannes Reinecke 	unsigned int chunk_sectors = dmz_zone_nr_sectors(dmz->metadata);
9953b1a94c8SDamien Le Moal 
9963b1a94c8SDamien Le Moal 	limits->logical_block_size = DMZ_BLOCK_SIZE;
9973b1a94c8SDamien Le Moal 	limits->physical_block_size = DMZ_BLOCK_SIZE;
9983b1a94c8SDamien Le Moal 
9993b1a94c8SDamien Le Moal 	blk_limits_io_min(limits, DMZ_BLOCK_SIZE);
10003b1a94c8SDamien Le Moal 	blk_limits_io_opt(limits, DMZ_BLOCK_SIZE);
10013b1a94c8SDamien Le Moal 
100244d58370SChristoph Hellwig 	limits->discard_alignment = 0;
10033b1a94c8SDamien Le Moal 	limits->discard_granularity = DMZ_BLOCK_SIZE;
10043b1a94c8SDamien Le Moal 	limits->max_discard_sectors = chunk_sectors;
10053b1a94c8SDamien Le Moal 	limits->max_hw_discard_sectors = chunk_sectors;
10063b1a94c8SDamien Le Moal 	limits->max_write_zeroes_sectors = chunk_sectors;
10073b1a94c8SDamien Le Moal 
10083b1a94c8SDamien Le Moal 	/* FS hint to try to align to the device zone size */
10093b1a94c8SDamien Le Moal 	limits->chunk_sectors = chunk_sectors;
10103b1a94c8SDamien Le Moal 	limits->max_sectors = chunk_sectors;
10113b1a94c8SDamien Le Moal 
10123b1a94c8SDamien Le Moal 	/* We are exposing a drive-managed zoned block device */
10133b1a94c8SDamien Le Moal 	limits->zoned = BLK_ZONED_NONE;
10143b1a94c8SDamien Le Moal }
10153b1a94c8SDamien Le Moal 
10163b1a94c8SDamien Le Moal /*
10173b1a94c8SDamien Le Moal  * Pass on ioctl to the backend device.
10183b1a94c8SDamien Le Moal  */
dmz_prepare_ioctl(struct dm_target * ti,struct block_device ** bdev)10195bd5e8d8SMike Snitzer static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
10203b1a94c8SDamien Le Moal {
10213b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
102252d67758SHannes Reinecke 	struct dmz_dev *dev = &dmz->dev[0];
10233b1a94c8SDamien Le Moal 
102452d67758SHannes Reinecke 	if (!dmz_check_bdev(dev))
1025e7fad909SDmitry Fomichev 		return -EIO;
102675d66ffbSDmitry Fomichev 
102752d67758SHannes Reinecke 	*bdev = dev->bdev;
10283b1a94c8SDamien Le Moal 
10293b1a94c8SDamien Le Moal 	return 0;
10303b1a94c8SDamien Le Moal }
10313b1a94c8SDamien Le Moal 
10323b1a94c8SDamien Le Moal /*
10333b1a94c8SDamien Le Moal  * Stop works on suspend.
10343b1a94c8SDamien Le Moal  */
dmz_suspend(struct dm_target * ti)10353b1a94c8SDamien Le Moal static void dmz_suspend(struct dm_target *ti)
10363b1a94c8SDamien Le Moal {
10373b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
1038f97809aeSHannes Reinecke 	int i;
10393b1a94c8SDamien Le Moal 
10403b1a94c8SDamien Le Moal 	flush_workqueue(dmz->chunk_wq);
1041f97809aeSHannes Reinecke 	for (i = 0; i < dmz->nr_ddevs; i++)
1042f97809aeSHannes Reinecke 		dmz_suspend_reclaim(dmz->dev[i].reclaim);
10433b1a94c8SDamien Le Moal 	cancel_delayed_work_sync(&dmz->flush_work);
10443b1a94c8SDamien Le Moal }
10453b1a94c8SDamien Le Moal 
10463b1a94c8SDamien Le Moal /*
10473b1a94c8SDamien Le Moal  * Restart works on resume or if suspend failed.
10483b1a94c8SDamien Le Moal  */
dmz_resume(struct dm_target * ti)10493b1a94c8SDamien Le Moal static void dmz_resume(struct dm_target *ti)
10503b1a94c8SDamien Le Moal {
10513b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
1052f97809aeSHannes Reinecke 	int i;
10533b1a94c8SDamien Le Moal 
10543b1a94c8SDamien Le Moal 	queue_delayed_work(dmz->flush_wq, &dmz->flush_work, DMZ_FLUSH_PERIOD);
1055f97809aeSHannes Reinecke 	for (i = 0; i < dmz->nr_ddevs; i++)
1056f97809aeSHannes Reinecke 		dmz_resume_reclaim(dmz->dev[i].reclaim);
10573b1a94c8SDamien Le Moal }
10583b1a94c8SDamien Le Moal 
dmz_iterate_devices(struct dm_target * ti,iterate_devices_callout_fn fn,void * data)10593b1a94c8SDamien Le Moal static int dmz_iterate_devices(struct dm_target *ti,
10603b1a94c8SDamien Le Moal 			       iterate_devices_callout_fn fn, void *data)
10613b1a94c8SDamien Le Moal {
10623b1a94c8SDamien Le Moal 	struct dmz_target *dmz = ti->private;
1063bd5c4031SHannes Reinecke 	unsigned int zone_nr_sectors = dmz_zone_nr_sectors(dmz->metadata);
1064bd5c4031SHannes Reinecke 	sector_t capacity;
10654dba1288SHannes Reinecke 	int i, r;
10663b1a94c8SDamien Le Moal 
10674dba1288SHannes Reinecke 	for (i = 0; i < dmz->nr_ddevs; i++) {
10684dba1288SHannes Reinecke 		capacity = dmz->dev[i].capacity & ~(zone_nr_sectors - 1);
10694dba1288SHannes Reinecke 		r = fn(ti, dmz->ddev[i], 0, capacity, data);
10704dba1288SHannes Reinecke 		if (r)
10714dba1288SHannes Reinecke 			break;
1072bd5c4031SHannes Reinecke 	}
1073bd5c4031SHannes Reinecke 	return r;
10743b1a94c8SDamien Le Moal }
10753b1a94c8SDamien Le Moal 
dmz_status(struct dm_target * ti,status_type_t type,unsigned int status_flags,char * result,unsigned int maxlen)1076bc3d5717SHannes Reinecke static void dmz_status(struct dm_target *ti, status_type_t type,
1077bc3d5717SHannes Reinecke 		       unsigned int status_flags, char *result,
1078bc3d5717SHannes Reinecke 		       unsigned int maxlen)
1079bc3d5717SHannes Reinecke {
1080bc3d5717SHannes Reinecke 	struct dmz_target *dmz = ti->private;
1081bc3d5717SHannes Reinecke 	ssize_t sz = 0;
1082bc3d5717SHannes Reinecke 	char buf[BDEVNAME_SIZE];
1083bd5c4031SHannes Reinecke 	struct dmz_dev *dev;
1084bd82fdabSHannes Reinecke 	int i;
1085bc3d5717SHannes Reinecke 
1086bc3d5717SHannes Reinecke 	switch (type) {
1087bc3d5717SHannes Reinecke 	case STATUSTYPE_INFO:
1088bd82fdabSHannes Reinecke 		DMEMIT("%u zones %u/%u cache",
1089bc3d5717SHannes Reinecke 		       dmz_nr_zones(dmz->metadata),
109034f5affdSHannes Reinecke 		       dmz_nr_unmap_cache_zones(dmz->metadata),
1091bd82fdabSHannes Reinecke 		       dmz_nr_cache_zones(dmz->metadata));
10924dba1288SHannes Reinecke 		for (i = 0; i < dmz->nr_ddevs; i++) {
1093bd82fdabSHannes Reinecke 			/*
1094bd82fdabSHannes Reinecke 			 * For a multi-device setup the first device
1095bd82fdabSHannes Reinecke 			 * contains only cache zones.
1096bd82fdabSHannes Reinecke 			 */
1097bd82fdabSHannes Reinecke 			if ((i == 0) &&
1098bd82fdabSHannes Reinecke 			    (dmz_nr_cache_zones(dmz->metadata) > 0))
1099bd82fdabSHannes Reinecke 				continue;
1100bd82fdabSHannes Reinecke 			DMEMIT(" %u/%u random %u/%u sequential",
1101bd82fdabSHannes Reinecke 			       dmz_nr_unmap_rnd_zones(dmz->metadata, i),
1102bd82fdabSHannes Reinecke 			       dmz_nr_rnd_zones(dmz->metadata, i),
1103bd82fdabSHannes Reinecke 			       dmz_nr_unmap_seq_zones(dmz->metadata, i),
1104bd82fdabSHannes Reinecke 			       dmz_nr_seq_zones(dmz->metadata, i));
1105bd82fdabSHannes Reinecke 		}
1106bc3d5717SHannes Reinecke 		break;
1107bc3d5717SHannes Reinecke 	case STATUSTYPE_TABLE:
1108bd5c4031SHannes Reinecke 		dev = &dmz->dev[0];
1109bd5c4031SHannes Reinecke 		format_dev_t(buf, dev->bdev->bd_dev);
1110bc3d5717SHannes Reinecke 		DMEMIT("%s", buf);
11114dba1288SHannes Reinecke 		for (i = 1; i < dmz->nr_ddevs; i++) {
11124dba1288SHannes Reinecke 			dev = &dmz->dev[i];
1113bd5c4031SHannes Reinecke 			format_dev_t(buf, dev->bdev->bd_dev);
1114bd5c4031SHannes Reinecke 			DMEMIT(" %s", buf);
1115bd5c4031SHannes Reinecke 		}
1116bc3d5717SHannes Reinecke 		break;
11178ec45662STushar Sugandhi 	case STATUSTYPE_IMA:
11188ec45662STushar Sugandhi 		*result = '\0';
11198ec45662STushar Sugandhi 		break;
1120bc3d5717SHannes Reinecke 	}
1121bc3d5717SHannes Reinecke }
1122bc3d5717SHannes Reinecke 
dmz_message(struct dm_target * ti,unsigned int argc,char ** argv,char * result,unsigned int maxlen)112390b39d58SHannes Reinecke static int dmz_message(struct dm_target *ti, unsigned int argc, char **argv,
112490b39d58SHannes Reinecke 		       char *result, unsigned int maxlen)
112590b39d58SHannes Reinecke {
112690b39d58SHannes Reinecke 	struct dmz_target *dmz = ti->private;
112790b39d58SHannes Reinecke 	int r = -EINVAL;
112890b39d58SHannes Reinecke 
112990b39d58SHannes Reinecke 	if (!strcasecmp(argv[0], "reclaim")) {
1130f97809aeSHannes Reinecke 		int i;
1131f97809aeSHannes Reinecke 
1132f97809aeSHannes Reinecke 		for (i = 0; i < dmz->nr_ddevs; i++)
1133f97809aeSHannes Reinecke 			dmz_schedule_reclaim(dmz->dev[i].reclaim);
113490b39d58SHannes Reinecke 		r = 0;
113590b39d58SHannes Reinecke 	} else
113690b39d58SHannes Reinecke 		DMERR("unrecognized message %s", argv[0]);
113790b39d58SHannes Reinecke 	return r;
113890b39d58SHannes Reinecke }
113990b39d58SHannes Reinecke 
11403664ff82SYangtao Li static struct target_type zoned_target = {
11413b1a94c8SDamien Le Moal 	.name		 = "zoned",
1142bd5c4031SHannes Reinecke 	.version	 = {2, 0, 0},
11432d669cebSShin'ichiro Kawasaki 	.features	 = DM_TARGET_SINGLETON | DM_TARGET_MIXED_ZONED_MODEL,
11443b1a94c8SDamien Le Moal 	.module		 = THIS_MODULE,
11453b1a94c8SDamien Le Moal 	.ctr		 = dmz_ctr,
11463b1a94c8SDamien Le Moal 	.dtr		 = dmz_dtr,
11473b1a94c8SDamien Le Moal 	.map		 = dmz_map,
11483b1a94c8SDamien Le Moal 	.io_hints	 = dmz_io_hints,
11493b1a94c8SDamien Le Moal 	.prepare_ioctl	 = dmz_prepare_ioctl,
11503b1a94c8SDamien Le Moal 	.postsuspend	 = dmz_suspend,
11513b1a94c8SDamien Le Moal 	.resume		 = dmz_resume,
11523b1a94c8SDamien Le Moal 	.iterate_devices = dmz_iterate_devices,
1153bc3d5717SHannes Reinecke 	.status		 = dmz_status,
115490b39d58SHannes Reinecke 	.message	 = dmz_message,
11553b1a94c8SDamien Le Moal };
11563664ff82SYangtao Li module_dm(zoned);
11573b1a94c8SDamien Le Moal 
11583b1a94c8SDamien Le Moal MODULE_DESCRIPTION(DM_NAME " target for zoned block devices");
11593b1a94c8SDamien Le Moal MODULE_AUTHOR("Damien Le Moal <damien.lemoal@wdc.com>");
11603b1a94c8SDamien Le Moal MODULE_LICENSE("GPL");
1161