Blame view

block/blk-zoned.c 12 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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  }
  EXPORT_SYMBOL_GPL(blkdev_report_zones);
  
  /**
   * 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...
216
217
  	struct bio *bio = NULL;
  	struct blk_plug plug;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
218
  	int ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
219
220
  	if (!blk_queue_is_zoned(q))
  		return -EOPNOTSUPP;
a2d6b3a2d   Damien Le Moal   block: Improve zo...
221
222
223
224
  	if (bdev_read_only(bdev))
  		return -EPERM;
  
  	if (!nr_sectors || end_sector > bdev->bd_part->nr_sects)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
225
226
227
228
  		/* Out of range */
  		return -EINVAL;
  
  	/* Check alignment (handle eventual smaller last zone) */
f99e86485   Damien Le Moal   block: Rename blk...
229
  	zone_sectors = blk_queue_zone_sectors(q);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
230
231
232
233
234
235
  	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...
236
  	blk_start_plug(&plug);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
237
  	while (sector < end_sector) {
a2d6b3a2d   Damien Le Moal   block: Improve zo...
238
  		bio = blk_next_bio(bio, 0, gfp_mask);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
239
  		bio->bi_iter.bi_sector = sector;
74d46992e   Christoph Hellwig   block: replace bi...
240
  		bio_set_dev(bio, bdev);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
241
  		bio_set_op_attrs(bio, REQ_OP_ZONE_RESET, 0);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
242
243
244
245
246
247
  		sector += zone_sectors;
  
  		/* This may take a while, so be nice to others */
  		cond_resched();
  
  	}
a2d6b3a2d   Damien Le Moal   block: Improve zo...
248
249
250
251
252
253
  	ret = submit_bio_wait(bio);
  	bio_put(bio);
  
  	blk_finish_plug(&plug);
  
  	return ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
254
255
  }
  EXPORT_SYMBOL_GPL(blkdev_reset_zones);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
256

56c4bddb9   Bart Van Assche   block: Suppress k...
257
  /*
3ed05a987   Shaun Tancheff   blk-zoned: implem...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
   * 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...
288
  	rep.nr_zones = min(blkdev_nr_zones(bdev), rep.nr_zones);
327ea4adc   Bart Van Assche   blkdev_report_zon...
289

344476e16   Kees Cook   treewide: kvmallo...
290
291
  	zones = kvmalloc_array(rep.nr_zones, sizeof(struct blk_zone),
  			       GFP_KERNEL | __GFP_ZERO);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
292
293
  	if (!zones)
  		return -ENOMEM;
bd976e527   Damien Le Moal   block: Kill gfp_t...
294
  	ret = blkdev_report_zones(bdev, rep.sector, zones, &rep.nr_zones);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  	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...
310
  	kvfree(zones);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
311
312
313
  
  	return ret;
  }
56c4bddb9   Bart Van Assche   block: Suppress k...
314
  /*
3ed05a987   Shaun Tancheff   blk-zoned: implem...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
   * 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 ...
347
348
349
350
351
352
353
354
355
356
357
358
  
  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...
359
  static struct blk_zone *blk_alloc_zones(unsigned int *nr_zones)
bf5054569   Damien Le Moal   block: Introduce ...
360
  {
26202928f   Damien Le Moal   block: Limit zone...
361
362
363
364
365
366
367
368
369
370
371
372
  	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 ...
373
  	}
26202928f   Damien Le Moal   block: Limit zone...
374
375
376
  	*nr_zones = nrz;
  
  	return zones;
bf5054569   Damien Le Moal   block: Introduce ...
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  }
  
  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...
403
  	unsigned int noio_flag;
bf5054569   Damien Le Moal   block: Introduce ...
404
405
406
407
408
409
410
  	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_...
411
  	if (!queue_is_mq(q)) {
bf5054569   Damien Le Moal   block: Introduce ...
412
413
414
  		q->nr_zones = nr_zones;
  		return 0;
  	}
bd976e527   Damien Le Moal   block: Kill gfp_t...
415
416
417
418
419
  	/*
  	 * 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 ...
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  	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...
436
  	zones = blk_alloc_zones(&rep_nr_zones);
bf5054569   Damien Le Moal   block: Introduce ...
437
438
439
440
441
  	if (!zones)
  		goto out;
  
  	while (z < nr_zones) {
  		nrz = min(nr_zones - z, rep_nr_zones);
bd976e527   Damien Le Moal   block: Kill gfp_t...
442
  		ret = blk_report_zones(disk, sector, zones, &nrz);
bf5054569   Damien Le Moal   block: Introduce ...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
  		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...
473
  	memalloc_noio_restore(noio_flag);
26202928f   Damien Le Moal   block: Limit zone...
474
  	kvfree(zones);
bf5054569   Damien Le Moal   block: Introduce ...
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  	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);