dm: move zone related code to 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
59int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx, void *data)
60{
61 struct dm_report_zones_args *args = data;
62 sector_t sector_diff = args->tgt->begin - args->start;
63
64 /*
65 * Ignore zones beyond the target range.
66 */
67 if (zone->start >= args->start + args->tgt->len)
68 return 0;
69
70 /*
71 * Remap the start sector and write pointer position of the zone
72 * to match its position in the target range.
73 */
74 zone->start += sector_diff;
75 if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
76 if (zone->cond == BLK_ZONE_COND_FULL)
77 zone->wp = zone->start + zone->len;
78 else if (zone->cond == BLK_ZONE_COND_EMPTY)
79 zone->wp = zone->start;
80 else
81 zone->wp += sector_diff;
82 }
83
84 args->next_sector = zone->start + zone->len;
85 return args->orig_cb(zone, args->zone_idx++, args->orig_data);
86}
87EXPORT_SYMBOL_GPL(dm_report_zones_cb);
88
89void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q)
90{
91 if (!blk_queue_is_zoned(q))
92 return;
93
94 /*
95 * For a zoned target, the number of zones should be updated for the
96 * correct value to be exposed in sysfs queue/nr_zones. For a BIO based
97 * target, this is all that is needed.
98 */
99 WARN_ON_ONCE(queue_is_mq(q));
100 q->nr_zones = blkdev_nr_zones(t->md->disk);
101}