Skip to content

Commit e76239a

Browse files
Christoph Hellwigaxboe
authored andcommitted
block: add a report_zones method
Dispatching a report zones command through the request queue is a major pain due to the command reply payload rewriting necessary. Given that blkdev_report_zones() is executing everything synchronously, implement report zones as a block device file operation instead, allowing major simplification of the code in many places. sd, null-blk, dm-linear and dm-flakey being the only block device drivers supporting exposing zoned block devices, these drivers are modified to provide the device side implementation of the report_zones() block device file operation. For device mappers, a new report_zones() target type operation is defined so that the upper block layer calls blkdev_report_zones() can be propagated down to the underlying devices of the dm targets. Implementation for this new operation is added to the dm-linear and dm-flakey targets. Reviewed-by: Hannes Reinecke <[email protected]> Signed-off-by: Christoph Hellwig <[email protected]> [Damien] * Changed method block_device argument to gendisk * Various bug fixes and improvements * Added support for null_blk, dm-linear and dm-flakey. Reviewed-by: Martin K. Petersen <[email protected]> Reviewed-by: Mike Snitzer <[email protected]> Signed-off-by: Damien Le Moal <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 965b652 commit e76239a

File tree

16 files changed

+266
-425
lines changed

16 files changed

+266
-425
lines changed

block/blk-core.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2300,7 +2300,6 @@ generic_make_request_checks(struct bio *bio)
23002300
if (!q->limits.max_write_same_sectors)
23012301
goto not_supported;
23022302
break;
2303-
case REQ_OP_ZONE_REPORT:
23042303
case REQ_OP_ZONE_RESET:
23052304
if (!blk_queue_is_zoned(q))
23062305
goto not_supported;

block/blk-mq-debugfs.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,6 @@ static const char *const op_name[] = {
283283
REQ_OP_NAME(WRITE),
284284
REQ_OP_NAME(FLUSH),
285285
REQ_OP_NAME(DISCARD),
286-
REQ_OP_NAME(ZONE_REPORT),
287286
REQ_OP_NAME(SECURE_ERASE),
288287
REQ_OP_NAME(ZONE_RESET),
289288
REQ_OP_NAME(WRITE_SAME),

block/blk-zoned.c

Lines changed: 51 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,10 @@ unsigned int blkdev_nr_zones(struct block_device *bdev)
9393
EXPORT_SYMBOL_GPL(blkdev_nr_zones);
9494

9595
/*
96-
* Check that a zone report belongs to the partition.
97-
* If yes, fix its start sector and write pointer, copy it in the
98-
* zone information array and return true. Return false otherwise.
96+
* Check that a zone report belongs to this partition, and if yes, fix its start
97+
* sector and write pointer and return true. Return false otherwise.
9998
*/
100-
static bool blkdev_report_zone(struct block_device *bdev,
101-
struct blk_zone *rep,
102-
struct blk_zone *zone)
99+
static bool blkdev_report_zone(struct block_device *bdev, struct blk_zone *rep)
103100
{
104101
sector_t offset = get_start_sect(bdev);
105102

@@ -114,11 +111,36 @@ static bool blkdev_report_zone(struct block_device *bdev,
114111
rep->wp = rep->start + rep->len;
115112
else
116113
rep->wp -= offset;
117-
memcpy(zone, rep, sizeof(struct blk_zone));
118-
119114
return true;
120115
}
121116

117+
static int blk_report_zones(struct gendisk *disk, sector_t sector,
118+
struct blk_zone *zones, unsigned int *nr_zones,
119+
gfp_t gfp_mask)
120+
{
121+
struct request_queue *q = disk->queue;
122+
unsigned int z = 0, n, nrz = *nr_zones;
123+
sector_t capacity = get_capacity(disk);
124+
int ret;
125+
126+
while (z < nrz && sector < capacity) {
127+
n = nrz - z;
128+
ret = disk->fops->report_zones(disk, sector, &zones[z], &n,
129+
gfp_mask);
130+
if (ret)
131+
return ret;
132+
if (!n)
133+
break;
134+
sector += blk_queue_zone_sectors(q) * n;
135+
z += n;
136+
}
137+
138+
WARN_ON(z > *nr_zones);
139+
*nr_zones = z;
140+
141+
return 0;
142+
}
143+
122144
/**
123145
* blkdev_report_zones - Get zones information
124146
* @bdev: Target block device
@@ -133,130 +155,46 @@ static bool blkdev_report_zone(struct block_device *bdev,
133155
* requested by @nr_zones. The number of zones actually reported is
134156
* returned in @nr_zones.
135157
*/
136-
int blkdev_report_zones(struct block_device *bdev,
137-
sector_t sector,
138-
struct blk_zone *zones,
139-
unsigned int *nr_zones,
158+
int blkdev_report_zones(struct block_device *bdev, sector_t sector,
159+
struct blk_zone *zones, unsigned int *nr_zones,
140160
gfp_t gfp_mask)
141161
{
142162
struct request_queue *q = bdev_get_queue(bdev);
143-
struct blk_zone_report_hdr *hdr;
144-
unsigned int nrz = *nr_zones;
145-
struct page *page;
146-
unsigned int nr_rep;
147-
size_t rep_bytes;
148-
unsigned int nr_pages;
149-
struct bio *bio;
150-
struct bio_vec *bv;
151-
unsigned int i, n, nz;
152-
unsigned int ofst;
153-
void *addr;
163+
unsigned int i, nrz;
154164
int ret;
155165

156-
if (!q)
157-
return -ENXIO;
158-
159166
if (!blk_queue_is_zoned(q))
160167
return -EOPNOTSUPP;
161168

162-
if (!nrz)
163-
return 0;
164-
165-
if (sector > bdev->bd_part->nr_sects) {
166-
*nr_zones = 0;
167-
return 0;
168-
}
169-
170169
/*
171-
* The zone report has a header. So make room for it in the
172-
* payload. Also make sure that the report fits in a single BIO
173-
* that will not be split down the stack.
170+
* A block device that advertized itself as zoned must have a
171+
* report_zones method. If it does not have one defined, the device
172+
* driver has a bug. So warn about that.
174173
*/
175-
rep_bytes = sizeof(struct blk_zone_report_hdr) +
176-
sizeof(struct blk_zone) * nrz;
177-
rep_bytes = (rep_bytes + PAGE_SIZE - 1) & PAGE_MASK;
178-
if (rep_bytes > (queue_max_sectors(q) << 9))
179-
rep_bytes = queue_max_sectors(q) << 9;
180-
181-
nr_pages = min_t(unsigned int, BIO_MAX_PAGES,
182-
rep_bytes >> PAGE_SHIFT);
183-
nr_pages = min_t(unsigned int, nr_pages,
184-
queue_max_segments(q));
185-
186-
bio = bio_alloc(gfp_mask, nr_pages);
187-
if (!bio)
188-
return -ENOMEM;
174+
if (WARN_ON_ONCE(!bdev->bd_disk->fops->report_zones))
175+
return -EOPNOTSUPP;
189176

190-
bio_set_dev(bio, bdev);
191-
bio->bi_iter.bi_sector = blk_zone_start(q, sector);
192-
bio_set_op_attrs(bio, REQ_OP_ZONE_REPORT, 0);
193-
194-
for (i = 0; i < nr_pages; i++) {
195-
page = alloc_page(gfp_mask);
196-
if (!page) {
197-
ret = -ENOMEM;
198-
goto out;
199-
}
200-
if (!bio_add_page(bio, page, PAGE_SIZE, 0)) {
201-
__free_page(page);
202-
break;
203-
}
177+
if (!*nr_zones || sector >= bdev->bd_part->nr_sects) {
178+
*nr_zones = 0;
179+
return 0;
204180
}
205181

206-
if (i == 0)
207-
ret = -ENOMEM;
208-
else
209-
ret = submit_bio_wait(bio);
182+
nrz = min(*nr_zones,
183+
__blkdev_nr_zones(q, bdev->bd_part->nr_sects - sector));
184+
ret = blk_report_zones(bdev->bd_disk, get_start_sect(bdev) + sector,
185+
zones, &nrz, gfp_mask);
210186
if (ret)
211-
goto out;
212-
213-
/*
214-
* Process the report result: skip the header and go through the
215-
* reported zones to fixup and fixup the zone information for
216-
* partitions. At the same time, return the zone information into
217-
* the zone array.
218-
*/
219-
n = 0;
220-
nz = 0;
221-
nr_rep = 0;
222-
bio_for_each_segment_all(bv, bio, i) {
187+
return ret;
223188

224-
if (!bv->bv_page)
189+
for (i = 0; i < nrz; i++) {
190+
if (!blkdev_report_zone(bdev, zones))
225191
break;
226-
227-
addr = kmap_atomic(bv->bv_page);
228-
229-
/* Get header in the first page */
230-
ofst = 0;
231-
if (!nr_rep) {
232-
hdr = addr;
233-
nr_rep = hdr->nr_zones;
234-
ofst = sizeof(struct blk_zone_report_hdr);
235-
}
236-
237-
/* Fixup and report zones */
238-
while (ofst < bv->bv_len &&
239-
n < nr_rep && nz < nrz) {
240-
if (blkdev_report_zone(bdev, addr + ofst, &zones[nz]))
241-
nz++;
242-
ofst += sizeof(struct blk_zone);
243-
n++;
244-
}
245-
246-
kunmap_atomic(addr);
247-
248-
if (n >= nr_rep || nz >= nrz)
249-
break;
250-
192+
zones++;
251193
}
252194

253-
*nr_zones = nz;
254-
out:
255-
bio_for_each_segment_all(bv, bio, i)
256-
__free_page(bv->bv_page);
257-
bio_put(bio);
195+
*nr_zones = i;
258196

259-
return ret;
197+
return 0;
260198
}
261199
EXPORT_SYMBOL_GPL(blkdev_report_zones);
262200

drivers/block/null_blk.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ struct nullb {
8787
#ifdef CONFIG_BLK_DEV_ZONED
8888
int null_zone_init(struct nullb_device *dev);
8989
void null_zone_exit(struct nullb_device *dev);
90-
blk_status_t null_zone_report(struct nullb *nullb, struct bio *bio);
90+
int null_zone_report(struct gendisk *disk, sector_t sector,
91+
struct blk_zone *zones, unsigned int *nr_zones,
92+
gfp_t gfp_mask);
9193
void null_zone_write(struct nullb_cmd *cmd, sector_t sector,
9294
unsigned int nr_sectors);
9395
void null_zone_reset(struct nullb_cmd *cmd, sector_t sector);
@@ -97,10 +99,11 @@ static inline int null_zone_init(struct nullb_device *dev)
9799
return -EINVAL;
98100
}
99101
static inline void null_zone_exit(struct nullb_device *dev) {}
100-
static inline blk_status_t null_zone_report(struct nullb *nullb,
101-
struct bio *bio)
102+
static inline int null_zone_report(struct gendisk *disk, sector_t sector,
103+
struct blk_zone *zones,
104+
unsigned int *nr_zones, gfp_t gfp_mask)
102105
{
103-
return BLK_STS_NOTSUPP;
106+
return -EOPNOTSUPP;
104107
}
105108
static inline void null_zone_write(struct nullb_cmd *cmd, sector_t sector,
106109
unsigned int nr_sectors)

drivers/block/null_blk_main.c

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,34 +1129,12 @@ static void null_restart_queue_async(struct nullb *nullb)
11291129
blk_mq_start_stopped_hw_queues(q, true);
11301130
}
11311131

1132-
static bool cmd_report_zone(struct nullb *nullb, struct nullb_cmd *cmd)
1133-
{
1134-
struct nullb_device *dev = cmd->nq->dev;
1135-
1136-
if (dev->queue_mode == NULL_Q_BIO) {
1137-
if (bio_op(cmd->bio) == REQ_OP_ZONE_REPORT) {
1138-
cmd->error = null_zone_report(nullb, cmd->bio);
1139-
return true;
1140-
}
1141-
} else {
1142-
if (req_op(cmd->rq) == REQ_OP_ZONE_REPORT) {
1143-
cmd->error = null_zone_report(nullb, cmd->rq->bio);
1144-
return true;
1145-
}
1146-
}
1147-
1148-
return false;
1149-
}
1150-
11511132
static blk_status_t null_handle_cmd(struct nullb_cmd *cmd)
11521133
{
11531134
struct nullb_device *dev = cmd->nq->dev;
11541135
struct nullb *nullb = dev->nullb;
11551136
int err = 0;
11561137

1157-
if (cmd_report_zone(nullb, cmd))
1158-
goto out;
1159-
11601138
if (test_bit(NULLB_DEV_FL_THROTTLED, &dev->flags)) {
11611139
struct request *rq = cmd->rq;
11621140

@@ -1443,6 +1421,7 @@ static const struct block_device_operations null_fops = {
14431421
.owner = THIS_MODULE,
14441422
.open = null_open,
14451423
.release = null_release,
1424+
.report_zones = null_zone_report,
14461425
};
14471426

14481427
static void null_init_queue(struct nullb *nullb, struct nullb_queue *nq)

drivers/block/null_blk_zoned.c

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,54 +48,27 @@ void null_zone_exit(struct nullb_device *dev)
4848
kvfree(dev->zones);
4949
}
5050

51-
static void null_zone_fill_bio(struct nullb_device *dev, struct bio *bio,
52-
unsigned int zno, unsigned int nr_zones)
51+
int null_zone_report(struct gendisk *disk, sector_t sector,
52+
struct blk_zone *zones, unsigned int *nr_zones,
53+
gfp_t gfp_mask)
5354
{
54-
struct blk_zone_report_hdr *hdr = NULL;
55-
struct bio_vec bvec;
56-
struct bvec_iter iter;
57-
void *addr;
58-
unsigned int zones_to_cpy;
59-
60-
bio_for_each_segment(bvec, bio, iter) {
61-
addr = kmap_atomic(bvec.bv_page);
62-
63-
zones_to_cpy = bvec.bv_len / sizeof(struct blk_zone);
64-
65-
if (!hdr) {
66-
hdr = (struct blk_zone_report_hdr *)addr;
67-
hdr->nr_zones = nr_zones;
68-
zones_to_cpy--;
69-
addr += sizeof(struct blk_zone_report_hdr);
70-
}
71-
72-
zones_to_cpy = min_t(unsigned int, zones_to_cpy, nr_zones);
73-
74-
memcpy(addr, &dev->zones[zno],
75-
zones_to_cpy * sizeof(struct blk_zone));
76-
77-
kunmap_atomic(addr);
55+
struct nullb *nullb = disk->private_data;
56+
struct nullb_device *dev = nullb->dev;
57+
unsigned int zno, nrz = 0;
7858

79-
nr_zones -= zones_to_cpy;
80-
zno += zones_to_cpy;
59+
if (!dev->zoned)
60+
/* Not a zoned null device */
61+
return -EOPNOTSUPP;
8162

82-
if (!nr_zones)
83-
break;
63+
zno = null_zone_no(dev, sector);
64+
if (zno < dev->nr_zones) {
65+
nrz = min_t(unsigned int, *nr_zones, dev->nr_zones - zno);
66+
memcpy(zones, &dev->zones[zno], nrz * sizeof(struct blk_zone));
8467
}
85-
}
8668

87-
blk_status_t null_zone_report(struct nullb *nullb, struct bio *bio)
88-
{
89-
struct nullb_device *dev = nullb->dev;
90-
unsigned int zno = null_zone_no(dev, bio->bi_iter.bi_sector);
91-
unsigned int nr_zones = dev->nr_zones - zno;
92-
unsigned int max_zones;
69+
*nr_zones = nrz;
9370

94-
max_zones = (bio->bi_iter.bi_size / sizeof(struct blk_zone)) - 1;
95-
nr_zones = min_t(unsigned int, nr_zones, max_zones);
96-
null_zone_fill_bio(nullb->dev, bio, zno, nr_zones);
97-
98-
return BLK_STS_OK;
71+
return 0;
9972
}
10073

10174
void null_zone_write(struct nullb_cmd *cmd, sector_t sector,

0 commit comments

Comments
 (0)