dm: rearrange core declarations for extended use from dm-zone.c
[linux-block.git] / drivers / md / dm-zone.c
CommitLineData
7fc18728
DLM
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2021 Western Digital Corporation or its affiliates.
4 */
5
6#include <linux/blkdev.h>
7
8#include "dm-core.h"
9
10/*
11 * User facing dm device block device report zone operation. This calls the
12 * report_zones operation for each target of a device table. This operation is
13 * generally implemented by targets using dm_report_zones().
14 */
15int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
16 unsigned int nr_zones, report_zones_cb cb, void *data)
17{
18 struct mapped_device *md = disk->private_data;
19 struct dm_table *map;
20 int srcu_idx, ret;
21 struct dm_report_zones_args args = {
22 .next_sector = sector,
23 .orig_data = data,
24 .orig_cb = cb,
25 };
26
27 if (dm_suspended_md(md))
28 return -EAGAIN;
29
30 map = dm_get_live_table(md, &srcu_idx);
31 if (!map) {
32 ret = -EIO;
33 goto out;
34 }
35
36 do {
37 struct dm_target *tgt;
38
39 tgt = dm_table_find_target(map, args.next_sector);
40 if (WARN_ON_ONCE(!tgt->type->report_zones)) {
41 ret = -EIO;
42 goto out;
43 }
44
45 args.tgt = tgt;
46 ret = tgt->type->report_zones(tgt, &args,
47 nr_zones - args.zone_idx);
48 if (ret < 0)
49 goto out;
50 } while (args.zone_idx < nr_zones &&
51 args.next_sector < get_capacity(disk));
52
53 ret = args.zone_idx;
54out:
55 dm_put_live_table(md, srcu_idx);
56 return ret;
57}
58
912e8875
DLM
59static int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx,
60 void *data)
7fc18728
DLM
61{
62 struct dm_report_zones_args *args = data;
63 sector_t sector_diff = args->tgt->begin - args->start;
64
65 /*
66 * Ignore zones beyond the target range.
67 */
68 if (zone->start >= args->start + args->tgt->len)
69 return 0;
70
71 /*
72 * Remap the start sector and write pointer position of the zone
73 * to match its position in the target range.
74 */
75 zone->start += sector_diff;
76 if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
77 if (zone->cond == BLK_ZONE_COND_FULL)
78 zone->wp = zone->start + zone->len;
79 else if (zone->cond == BLK_ZONE_COND_EMPTY)
80 zone->wp = zone->start;
81 else
82 zone->wp += sector_diff;
83 }
84
85 args->next_sector = zone->start + zone->len;
86 return args->orig_cb(zone, args->zone_idx++, args->orig_data);
87}
912e8875
DLM
88
89/*
90 * Helper for drivers of zoned targets to implement struct target_type
91 * report_zones operation.
92 */
93int dm_report_zones(struct block_device *bdev, sector_t start, sector_t sector,
94 struct dm_report_zones_args *args, unsigned int nr_zones)
95{
96 /*
97 * Set the target mapping start sector first so that
98 * dm_report_zones_cb() can correctly remap zone information.
99 */
100 args->start = start;
101
102 return blkdev_report_zones(bdev, sector, nr_zones,
103 dm_report_zones_cb, args);
104}
105EXPORT_SYMBOL_GPL(dm_report_zones);
7fc18728 106
bf14e2b2
DLM
107bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)
108{
109 struct request_queue *q = md->queue;
110
111 if (!blk_queue_is_zoned(q))
112 return false;
113
114 switch (bio_op(bio)) {
115 case REQ_OP_WRITE_ZEROES:
116 case REQ_OP_WRITE_SAME:
117 case REQ_OP_WRITE:
118 return !op_is_flush(bio->bi_opf) && bio_sectors(bio);
119 default:
120 return false;
121 }
122}
123
7fc18728
DLM
124void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q)
125{
126 if (!blk_queue_is_zoned(q))
127 return;
128
129 /*
130 * For a zoned target, the number of zones should be updated for the
131 * correct value to be exposed in sysfs queue/nr_zones. For a BIO based
132 * target, this is all that is needed.
133 */
134 WARN_ON_ONCE(queue_is_mq(q));
135 q->nr_zones = blkdev_nr_zones(t->md->disk);
136}