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