Blame view

block/blk-zoned.c 13.1 KB
3dcf60bcb   Christoph Hellwig   block: add SPDX t...
1
  // SPDX-License-Identifier: GPL-2.0
6a0cb1bc1   Hannes Reinecke   block: Implement ...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * Zoned block device handling
   *
   * Copyright (c) 2015, Hannes Reinecke
   * Copyright (c) 2015, SUSE Linux GmbH
   *
   * Copyright (c) 2016, Damien Le Moal
   * Copyright (c) 2016, Western Digital
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/rbtree.h>
  #include <linux/blkdev.h>
bf5054569   Damien Le Moal   block: Introduce ...
16
  #include <linux/blk-mq.h>
26202928f   Damien Le Moal   block: Limit zone...
17
18
  #include <linux/mm.h>
  #include <linux/vmalloc.h>
bd976e527   Damien Le Moal   block: Kill gfp_t...
19
  #include <linux/sched/mm.h>
6a0cb1bc1   Hannes Reinecke   block: Implement ...
20

a2d6b3a2d   Damien Le Moal   block: Improve zo...
21
  #include "blk.h"
6a0cb1bc1   Hannes Reinecke   block: Implement ...
22
23
24
  static inline sector_t blk_zone_start(struct request_queue *q,
  				      sector_t sector)
  {
f99e86485   Damien Le Moal   block: Rename blk...
25
  	sector_t zone_mask = blk_queue_zone_sectors(q) - 1;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
26
27
28
29
30
  
  	return sector & ~zone_mask;
  }
  
  /*
6cc77e9cb   Christoph Hellwig   block: introduce ...
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
   * Return true if a request is a write requests that needs zone write locking.
   */
  bool blk_req_needs_zone_write_lock(struct request *rq)
  {
  	if (!rq->q->seq_zones_wlock)
  		return false;
  
  	if (blk_rq_is_passthrough(rq))
  		return false;
  
  	switch (req_op(rq)) {
  	case REQ_OP_WRITE_ZEROES:
  	case REQ_OP_WRITE_SAME:
  	case REQ_OP_WRITE:
  		return blk_rq_zone_is_seq(rq);
  	default:
  		return false;
  	}
  }
  EXPORT_SYMBOL_GPL(blk_req_needs_zone_write_lock);
  
  void __blk_req_zone_write_lock(struct request *rq)
  {
  	if (WARN_ON_ONCE(test_and_set_bit(blk_rq_zone_no(rq),
  					  rq->q->seq_zones_wlock)))
  		return;
  
  	WARN_ON_ONCE(rq->rq_flags & RQF_ZONE_WRITE_LOCKED);
  	rq->rq_flags |= RQF_ZONE_WRITE_LOCKED;
  }
  EXPORT_SYMBOL_GPL(__blk_req_zone_write_lock);
  
  void __blk_req_zone_write_unlock(struct request *rq)
  {
  	rq->rq_flags &= ~RQF_ZONE_WRITE_LOCKED;
  	if (rq->q->seq_zones_wlock)
  		WARN_ON_ONCE(!test_and_clear_bit(blk_rq_zone_no(rq),
  						 rq->q->seq_zones_wlock));
  }
  EXPORT_SYMBOL_GPL(__blk_req_zone_write_unlock);
a91e13802   Damien Le Moal   block: Introduce ...
71
72
73
  static inline unsigned int __blkdev_nr_zones(struct request_queue *q,
  					     sector_t nr_sectors)
  {
113ab72ed   Damien Le Moal   block: Fix potent...
74
  	sector_t zone_sectors = blk_queue_zone_sectors(q);
a91e13802   Damien Le Moal   block: Introduce ...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  
  	return (nr_sectors + zone_sectors - 1) >> ilog2(zone_sectors);
  }
  
  /**
   * blkdev_nr_zones - Get number of zones
   * @bdev:	Target block device
   *
   * Description:
   *    Return the total number of zones of a zoned block device.
   *    For a regular block device, the number of zones is always 0.
   */
  unsigned int blkdev_nr_zones(struct block_device *bdev)
  {
  	struct request_queue *q = bdev_get_queue(bdev);
  
  	if (!blk_queue_is_zoned(q))
  		return 0;
  
  	return __blkdev_nr_zones(q, bdev->bd_part->nr_sects);
  }
  EXPORT_SYMBOL_GPL(blkdev_nr_zones);
6cc77e9cb   Christoph Hellwig   block: introduce ...
97
  /*
e76239a37   Christoph Hellwig   block: add a repo...
98
99
   * Check that a zone report belongs to this partition, and if yes, fix its start
   * sector and write pointer and return true. Return false otherwise.
6a0cb1bc1   Hannes Reinecke   block: Implement ...
100
   */
e76239a37   Christoph Hellwig   block: add a repo...
101
  static bool blkdev_report_zone(struct block_device *bdev, struct blk_zone *rep)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  {
  	sector_t offset = get_start_sect(bdev);
  
  	if (rep->start < offset)
  		return false;
  
  	rep->start -= offset;
  	if (rep->start + rep->len > bdev->bd_part->nr_sects)
  		return false;
  
  	if (rep->type == BLK_ZONE_TYPE_CONVENTIONAL)
  		rep->wp = rep->start + rep->len;
  	else
  		rep->wp -= offset;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
116
117
  	return true;
  }
e76239a37   Christoph Hellwig   block: add a repo...
118
  static int blk_report_zones(struct gendisk *disk, sector_t sector,
bd976e527   Damien Le Moal   block: Kill gfp_t...
119
  			    struct blk_zone *zones, unsigned int *nr_zones)
e76239a37   Christoph Hellwig   block: add a repo...
120
121
122
123
124
125
126
127
  {
  	struct request_queue *q = disk->queue;
  	unsigned int z = 0, n, nrz = *nr_zones;
  	sector_t capacity = get_capacity(disk);
  	int ret;
  
  	while (z < nrz && sector < capacity) {
  		n = nrz - z;
bd976e527   Damien Le Moal   block: Kill gfp_t...
128
  		ret = disk->fops->report_zones(disk, sector, &zones[z], &n);
e76239a37   Christoph Hellwig   block: add a repo...
129
130
131
132
133
134
135
136
137
138
139
140
141
  		if (ret)
  			return ret;
  		if (!n)
  			break;
  		sector += blk_queue_zone_sectors(q) * n;
  		z += n;
  	}
  
  	WARN_ON(z > *nr_zones);
  	*nr_zones = z;
  
  	return 0;
  }
6a0cb1bc1   Hannes Reinecke   block: Implement ...
142
143
144
145
146
147
  /**
   * blkdev_report_zones - Get zones information
   * @bdev:	Target block device
   * @sector:	Sector from which to report zones
   * @zones:	Array of zone structures where to return the zones information
   * @nr_zones:	Number of zone structures in the zone array
6a0cb1bc1   Hannes Reinecke   block: Implement ...
148
149
150
151
152
153
   *
   * Description:
   *    Get zone information starting from the zone containing @sector.
   *    The number of zone information reported may be less than the number
   *    requested by @nr_zones. The number of zones actually reported is
   *    returned in @nr_zones.
bd976e527   Damien Le Moal   block: Kill gfp_t...
154
155
156
   *    The caller must use memalloc_noXX_save/restore() calls to control
   *    memory allocations done within this function (zone array and command
   *    buffer allocation by the device driver).
6a0cb1bc1   Hannes Reinecke   block: Implement ...
157
   */
e76239a37   Christoph Hellwig   block: add a repo...
158
  int blkdev_report_zones(struct block_device *bdev, sector_t sector,
bd976e527   Damien Le Moal   block: Kill gfp_t...
159
  			struct blk_zone *zones, unsigned int *nr_zones)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
160
161
  {
  	struct request_queue *q = bdev_get_queue(bdev);
e76239a37   Christoph Hellwig   block: add a repo...
162
  	unsigned int i, nrz;
3c4da7581   Arnd Bergmann   block: zoned: fix...
163
  	int ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
164

6a0cb1bc1   Hannes Reinecke   block: Implement ...
165
166
  	if (!blk_queue_is_zoned(q))
  		return -EOPNOTSUPP;
e76239a37   Christoph Hellwig   block: add a repo...
167
168
169
170
171
172
173
  	/*
  	 * A block device that advertized itself as zoned must have a
  	 * report_zones method. If it does not have one defined, the device
  	 * driver has a bug. So warn about that.
  	 */
  	if (WARN_ON_ONCE(!bdev->bd_disk->fops->report_zones))
  		return -EOPNOTSUPP;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
174

e76239a37   Christoph Hellwig   block: add a repo...
175
  	if (!*nr_zones || sector >= bdev->bd_part->nr_sects) {
6a0cb1bc1   Hannes Reinecke   block: Implement ...
176
177
178
  		*nr_zones = 0;
  		return 0;
  	}
e76239a37   Christoph Hellwig   block: add a repo...
179
180
181
  	nrz = min(*nr_zones,
  		  __blkdev_nr_zones(q, bdev->bd_part->nr_sects - sector));
  	ret = blk_report_zones(bdev->bd_disk, get_start_sect(bdev) + sector,
bd976e527   Damien Le Moal   block: Kill gfp_t...
182
  			       zones, &nrz);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
183
  	if (ret)
e76239a37   Christoph Hellwig   block: add a repo...
184
  		return ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
185

e76239a37   Christoph Hellwig   block: add a repo...
186
187
  	for (i = 0; i < nrz; i++) {
  		if (!blkdev_report_zone(bdev, zones))
6a0cb1bc1   Hannes Reinecke   block: Implement ...
188
  			break;
e76239a37   Christoph Hellwig   block: add a repo...
189
  		zones++;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
190
  	}
e76239a37   Christoph Hellwig   block: add a repo...
191
  	*nr_zones = i;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
192

e76239a37   Christoph Hellwig   block: add a repo...
193
  	return 0;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
194
195
  }
  EXPORT_SYMBOL_GPL(blkdev_report_zones);
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  /*
   * Special case of zone reset operation to reset all zones in one command,
   * useful for applications like mkfs.
   */
  static int __blkdev_reset_all_zones(struct block_device *bdev, gfp_t gfp_mask)
  {
  	struct bio *bio = bio_alloc(gfp_mask, 0);
  	int ret;
  
  	/* across the zones operations, don't need any sectors */
  	bio_set_dev(bio, bdev);
  	bio_set_op_attrs(bio, REQ_OP_ZONE_RESET_ALL, 0);
  
  	ret = submit_bio_wait(bio);
  	bio_put(bio);
  
  	return ret;
  }
  
  static inline bool blkdev_allow_reset_all_zones(struct block_device *bdev,
  						sector_t nr_sectors)
  {
  	if (!blk_queue_zone_resetall(bdev_get_queue(bdev)))
  		return false;
  
  	if (nr_sectors != part_nr_sects_read(bdev->bd_part))
  		return false;
  	/*
  	 * REQ_OP_ZONE_RESET_ALL can be executed only if the block device is
  	 * the entire disk, that is, if the blocks device start offset is 0 and
  	 * its capacity is the same as the entire disk.
  	 */
  	return get_start_sect(bdev) == 0 &&
  	       part_nr_sects_read(bdev->bd_part) == get_capacity(bdev->bd_disk);
  }
6a0cb1bc1   Hannes Reinecke   block: Implement ...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  /**
   * blkdev_reset_zones - Reset zones write pointer
   * @bdev:	Target block device
   * @sector:	Start sector of the first zone to reset
   * @nr_sectors:	Number of sectors, at least the length of one zone
   * @gfp_mask:	Memory allocation flags (for bio_alloc)
   *
   * Description:
   *    Reset the write pointer of the zones contained in the range
   *    @sector..@sector+@nr_sectors. Specifying the entire disk sector range
   *    is valid, but the specified range should not contain conventional zones.
   */
  int blkdev_reset_zones(struct block_device *bdev,
  		       sector_t sector, sector_t nr_sectors,
  		       gfp_t gfp_mask)
  {
  	struct request_queue *q = bdev_get_queue(bdev);
  	sector_t zone_sectors;
  	sector_t end_sector = sector + nr_sectors;
a2d6b3a2d   Damien Le Moal   block: Improve zo...
250
251
  	struct bio *bio = NULL;
  	struct blk_plug plug;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
252
  	int ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
253
254
  	if (!blk_queue_is_zoned(q))
  		return -EOPNOTSUPP;
a2d6b3a2d   Damien Le Moal   block: Improve zo...
255
256
257
258
  	if (bdev_read_only(bdev))
  		return -EPERM;
  
  	if (!nr_sectors || end_sector > bdev->bd_part->nr_sects)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
259
260
  		/* Out of range */
  		return -EINVAL;
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
261
262
  	if (blkdev_allow_reset_all_zones(bdev, nr_sectors))
  		return  __blkdev_reset_all_zones(bdev, gfp_mask);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
263
  	/* Check alignment (handle eventual smaller last zone) */
f99e86485   Damien Le Moal   block: Rename blk...
264
  	zone_sectors = blk_queue_zone_sectors(q);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
265
266
267
268
269
270
  	if (sector & (zone_sectors - 1))
  		return -EINVAL;
  
  	if ((nr_sectors & (zone_sectors - 1)) &&
  	    end_sector != bdev->bd_part->nr_sects)
  		return -EINVAL;
a2d6b3a2d   Damien Le Moal   block: Improve zo...
271
  	blk_start_plug(&plug);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
272
  	while (sector < end_sector) {
a2d6b3a2d   Damien Le Moal   block: Improve zo...
273
  		bio = blk_next_bio(bio, 0, gfp_mask);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
274
  		bio->bi_iter.bi_sector = sector;
74d46992e   Christoph Hellwig   block: replace bi...
275
  		bio_set_dev(bio, bdev);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
276
  		bio_set_op_attrs(bio, REQ_OP_ZONE_RESET, 0);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
277
278
279
280
281
282
  		sector += zone_sectors;
  
  		/* This may take a while, so be nice to others */
  		cond_resched();
  
  	}
a2d6b3a2d   Damien Le Moal   block: Improve zo...
283
284
285
286
287
288
  	ret = submit_bio_wait(bio);
  	bio_put(bio);
  
  	blk_finish_plug(&plug);
  
  	return ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
289
290
  }
  EXPORT_SYMBOL_GPL(blkdev_reset_zones);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
291

56c4bddb9   Bart Van Assche   block: Suppress k...
292
  /*
3ed05a987   Shaun Tancheff   blk-zoned: implem...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
   * BLKREPORTZONE ioctl processing.
   * Called from blkdev_ioctl.
   */
  int blkdev_report_zones_ioctl(struct block_device *bdev, fmode_t mode,
  			      unsigned int cmd, unsigned long arg)
  {
  	void __user *argp = (void __user *)arg;
  	struct request_queue *q;
  	struct blk_zone_report rep;
  	struct blk_zone *zones;
  	int ret;
  
  	if (!argp)
  		return -EINVAL;
  
  	q = bdev_get_queue(bdev);
  	if (!q)
  		return -ENXIO;
  
  	if (!blk_queue_is_zoned(q))
  		return -ENOTTY;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  
  	if (copy_from_user(&rep, argp, sizeof(struct blk_zone_report)))
  		return -EFAULT;
  
  	if (!rep.nr_zones)
  		return -EINVAL;
2e85fbaff   Damien Le Moal   block: Limit allo...
323
  	rep.nr_zones = min(blkdev_nr_zones(bdev), rep.nr_zones);
327ea4adc   Bart Van Assche   blkdev_report_zon...
324

344476e16   Kees Cook   treewide: kvmallo...
325
326
  	zones = kvmalloc_array(rep.nr_zones, sizeof(struct blk_zone),
  			       GFP_KERNEL | __GFP_ZERO);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
327
328
  	if (!zones)
  		return -ENOMEM;
bd976e527   Damien Le Moal   block: Kill gfp_t...
329
  	ret = blkdev_report_zones(bdev, rep.sector, zones, &rep.nr_zones);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  	if (ret)
  		goto out;
  
  	if (copy_to_user(argp, &rep, sizeof(struct blk_zone_report))) {
  		ret = -EFAULT;
  		goto out;
  	}
  
  	if (rep.nr_zones) {
  		if (copy_to_user(argp + sizeof(struct blk_zone_report), zones,
  				 sizeof(struct blk_zone) * rep.nr_zones))
  			ret = -EFAULT;
  	}
  
   out:
327ea4adc   Bart Van Assche   blkdev_report_zon...
345
  	kvfree(zones);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
346
347
348
  
  	return ret;
  }
56c4bddb9   Bart Van Assche   block: Suppress k...
349
  /*
3ed05a987   Shaun Tancheff   blk-zoned: implem...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
   * BLKRESETZONE ioctl processing.
   * Called from blkdev_ioctl.
   */
  int blkdev_reset_zones_ioctl(struct block_device *bdev, fmode_t mode,
  			     unsigned int cmd, unsigned long arg)
  {
  	void __user *argp = (void __user *)arg;
  	struct request_queue *q;
  	struct blk_zone_range zrange;
  
  	if (!argp)
  		return -EINVAL;
  
  	q = bdev_get_queue(bdev);
  	if (!q)
  		return -ENXIO;
  
  	if (!blk_queue_is_zoned(q))
  		return -ENOTTY;
  
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  
  	if (!(mode & FMODE_WRITE))
  		return -EBADF;
  
  	if (copy_from_user(&zrange, argp, sizeof(struct blk_zone_range)))
  		return -EFAULT;
  
  	return blkdev_reset_zones(bdev, zrange.sector, zrange.nr_sectors,
  				  GFP_KERNEL);
  }
bf5054569   Damien Le Moal   block: Introduce ...
382
383
384
385
386
387
388
389
390
391
392
393
  
  static inline unsigned long *blk_alloc_zone_bitmap(int node,
  						   unsigned int nr_zones)
  {
  	return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long),
  			    GFP_NOIO, node);
  }
  
  /*
   * Allocate an array of struct blk_zone to get nr_zones zone information.
   * The allocated array may be smaller than nr_zones.
   */
26202928f   Damien Le Moal   block: Limit zone...
394
  static struct blk_zone *blk_alloc_zones(unsigned int *nr_zones)
bf5054569   Damien Le Moal   block: Introduce ...
395
  {
26202928f   Damien Le Moal   block: Limit zone...
396
397
398
399
400
401
402
403
404
405
406
407
  	struct blk_zone *zones;
  	size_t nrz = min(*nr_zones, BLK_ZONED_REPORT_MAX_ZONES);
  
  	/*
  	 * GFP_KERNEL here is meaningless as the caller task context has
  	 * the PF_MEMALLOC_NOIO flag set in blk_revalidate_disk_zones()
  	 * with memalloc_noio_save().
  	 */
  	zones = kvcalloc(nrz, sizeof(struct blk_zone), GFP_KERNEL);
  	if (!zones) {
  		*nr_zones = 0;
  		return NULL;
bf5054569   Damien Le Moal   block: Introduce ...
408
  	}
26202928f   Damien Le Moal   block: Limit zone...
409
410
411
  	*nr_zones = nrz;
  
  	return zones;
bf5054569   Damien Le Moal   block: Introduce ...
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
  }
  
  void blk_queue_free_zone_bitmaps(struct request_queue *q)
  {
  	kfree(q->seq_zones_bitmap);
  	q->seq_zones_bitmap = NULL;
  	kfree(q->seq_zones_wlock);
  	q->seq_zones_wlock = NULL;
  }
  
  /**
   * blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
   * @disk:	Target disk
   *
   * Helper function for low-level device drivers to (re) allocate and initialize
   * a disk request queue zone bitmaps. This functions should normally be called
   * within the disk ->revalidate method. For BIO based queues, no zone bitmap
   * is allocated.
   */
  int blk_revalidate_disk_zones(struct gendisk *disk)
  {
  	struct request_queue *q = disk->queue;
  	unsigned int nr_zones = __blkdev_nr_zones(q, get_capacity(disk));
  	unsigned long *seq_zones_wlock = NULL, *seq_zones_bitmap = NULL;
  	unsigned int i, rep_nr_zones = 0, z = 0, nrz;
  	struct blk_zone *zones = NULL;
bd976e527   Damien Le Moal   block: Kill gfp_t...
438
  	unsigned int noio_flag;
bf5054569   Damien Le Moal   block: Introduce ...
439
440
441
442
443
444
445
  	sector_t sector = 0;
  	int ret = 0;
  
  	/*
  	 * BIO based queues do not use a scheduler so only q->nr_zones
  	 * needs to be updated so that the sysfs exposed value is correct.
  	 */
344e9ffcb   Jens Axboe   block: add queue_...
446
  	if (!queue_is_mq(q)) {
bf5054569   Damien Le Moal   block: Introduce ...
447
448
449
  		q->nr_zones = nr_zones;
  		return 0;
  	}
bd976e527   Damien Le Moal   block: Kill gfp_t...
450
451
452
453
454
  	/*
  	 * Ensure that all memory allocations in this context are done as
  	 * if GFP_NOIO was specified.
  	 */
  	noio_flag = memalloc_noio_save();
bf5054569   Damien Le Moal   block: Introduce ...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  	if (!blk_queue_is_zoned(q) || !nr_zones) {
  		nr_zones = 0;
  		goto update;
  	}
  
  	/* Allocate bitmaps */
  	ret = -ENOMEM;
  	seq_zones_wlock = blk_alloc_zone_bitmap(q->node, nr_zones);
  	if (!seq_zones_wlock)
  		goto out;
  	seq_zones_bitmap = blk_alloc_zone_bitmap(q->node, nr_zones);
  	if (!seq_zones_bitmap)
  		goto out;
  
  	/* Get zone information and initialize seq_zones_bitmap */
  	rep_nr_zones = nr_zones;
26202928f   Damien Le Moal   block: Limit zone...
471
  	zones = blk_alloc_zones(&rep_nr_zones);
bf5054569   Damien Le Moal   block: Introduce ...
472
473
474
475
476
  	if (!zones)
  		goto out;
  
  	while (z < nr_zones) {
  		nrz = min(nr_zones - z, rep_nr_zones);
bd976e527   Damien Le Moal   block: Kill gfp_t...
477
  		ret = blk_report_zones(disk, sector, zones, &nrz);
bf5054569   Damien Le Moal   block: Introduce ...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
  		if (ret)
  			goto out;
  		if (!nrz)
  			break;
  		for (i = 0; i < nrz; i++) {
  			if (zones[i].type != BLK_ZONE_TYPE_CONVENTIONAL)
  				set_bit(z, seq_zones_bitmap);
  			z++;
  		}
  		sector += nrz * blk_queue_zone_sectors(q);
  	}
  
  	if (WARN_ON(z != nr_zones)) {
  		ret = -EIO;
  		goto out;
  	}
  
  update:
  	/*
  	 * Install the new bitmaps, making sure the queue is stopped and
  	 * all I/Os are completed (i.e. a scheduler is not referencing the
  	 * bitmaps).
  	 */
  	blk_mq_freeze_queue(q);
  	q->nr_zones = nr_zones;
  	swap(q->seq_zones_wlock, seq_zones_wlock);
  	swap(q->seq_zones_bitmap, seq_zones_bitmap);
  	blk_mq_unfreeze_queue(q);
  
  out:
bd976e527   Damien Le Moal   block: Kill gfp_t...
508
  	memalloc_noio_restore(noio_flag);
26202928f   Damien Le Moal   block: Limit zone...
509
  	kvfree(zones);
bf5054569   Damien Le Moal   block: Introduce ...
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  	kfree(seq_zones_wlock);
  	kfree(seq_zones_bitmap);
  
  	if (ret) {
  		pr_warn("%s: failed to revalidate zones
  ", disk->disk_name);
  		blk_mq_freeze_queue(q);
  		blk_queue_free_zone_bitmaps(q);
  		blk_mq_unfreeze_queue(q);
  	}
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);