Blame view

block/blk-zoned.c 16.7 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"
02694e863   Chaitanya Kulkarni   block: add a zone...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  #define ZONE_COND_NAME(name) [BLK_ZONE_COND_##name] = #name
  static const char *const zone_cond_name[] = {
  	ZONE_COND_NAME(NOT_WP),
  	ZONE_COND_NAME(EMPTY),
  	ZONE_COND_NAME(IMP_OPEN),
  	ZONE_COND_NAME(EXP_OPEN),
  	ZONE_COND_NAME(CLOSED),
  	ZONE_COND_NAME(READONLY),
  	ZONE_COND_NAME(FULL),
  	ZONE_COND_NAME(OFFLINE),
  };
  #undef ZONE_COND_NAME
  
  /**
   * blk_zone_cond_str - Return string XXX in BLK_ZONE_COND_XXX.
   * @zone_cond: BLK_ZONE_COND_XXX.
   *
   * Description: Centralize block layer function to convert BLK_ZONE_COND_XXX
   * into string format. Useful in the debugging and tracing zone conditions. For
   * invalid BLK_ZONE_COND_XXX it returns string "UNKNOWN".
   */
  const char *blk_zone_cond_str(enum blk_zone_cond zone_cond)
  {
  	static const char *zone_cond_str = "UNKNOWN";
  
  	if (zone_cond < ARRAY_SIZE(zone_cond_name) && zone_cond_name[zone_cond])
  		zone_cond_str = zone_cond_name[zone_cond];
  
  	return zone_cond_str;
  }
  EXPORT_SYMBOL_GPL(blk_zone_cond_str);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
53
  /*
6cc77e9cb   Christoph Hellwig   block: introduce ...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
   * 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);
1392d3701   Johannes Thumshirn   block: introduce ...
74
75
76
77
78
79
80
81
82
83
84
85
86
  bool blk_req_zone_write_trylock(struct request *rq)
  {
  	unsigned int zno = blk_rq_zone_no(rq);
  
  	if (test_and_set_bit(zno, rq->q->seq_zones_wlock))
  		return false;
  
  	WARN_ON_ONCE(rq->rq_flags & RQF_ZONE_WRITE_LOCKED);
  	rq->rq_flags |= RQF_ZONE_WRITE_LOCKED;
  
  	return true;
  }
  EXPORT_SYMBOL_GPL(blk_req_zone_write_trylock);
6cc77e9cb   Christoph Hellwig   block: introduce ...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  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 ...
106
107
  /**
   * blkdev_nr_zones - Get number of zones
9b38bb4b1   Christoph Hellwig   block: simplify b...
108
   * @disk:	Target gendisk
a91e13802   Damien Le Moal   block: Introduce ...
109
   *
9b38bb4b1   Christoph Hellwig   block: simplify b...
110
111
   * Return the total number of zones of a zoned block device.  For a block
   * device without zone capabilities, the number of zones is always 0.
a91e13802   Damien Le Moal   block: Introduce ...
112
   */
9b38bb4b1   Christoph Hellwig   block: simplify b...
113
  unsigned int blkdev_nr_zones(struct gendisk *disk)
a91e13802   Damien Le Moal   block: Introduce ...
114
  {
9b38bb4b1   Christoph Hellwig   block: simplify b...
115
  	sector_t zone_sectors = blk_queue_zone_sectors(disk->queue);
a91e13802   Damien Le Moal   block: Introduce ...
116

9b38bb4b1   Christoph Hellwig   block: simplify b...
117
  	if (!blk_queue_is_zoned(disk->queue))
a91e13802   Damien Le Moal   block: Introduce ...
118
  		return 0;
9b38bb4b1   Christoph Hellwig   block: simplify b...
119
  	return (get_capacity(disk) + zone_sectors - 1) >> ilog2(zone_sectors);
a91e13802   Damien Le Moal   block: Introduce ...
120
121
  }
  EXPORT_SYMBOL_GPL(blkdev_nr_zones);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
122
123
124
125
  /**
   * blkdev_report_zones - Get zones information
   * @bdev:	Target block device
   * @sector:	Sector from which to report zones
d41003513   Christoph Hellwig   block: rework zon...
126
127
128
   * @nr_zones:	Maximum number of zones to report
   * @cb:		Callback function called for each reported zone
   * @data:	Private data for the callback
6a0cb1bc1   Hannes Reinecke   block: Implement ...
129
130
   *
   * Description:
d41003513   Christoph Hellwig   block: rework zon...
131
132
133
134
135
136
137
138
139
   *    Get zone information starting from the zone containing @sector for at most
   *    @nr_zones, and call @cb for each zone reported by the device.
   *    To report all zones in a device starting from @sector, the BLK_ALL_ZONES
   *    constant can be passed to @nr_zones.
   *    Returns the number of zones reported by the device, or a negative errno
   *    value in case of failure.
   *
   *    Note: The caller must use memalloc_noXX_save/restore() calls to control
   *    memory allocations done within this function.
6a0cb1bc1   Hannes Reinecke   block: Implement ...
140
   */
e76239a37   Christoph Hellwig   block: add a repo...
141
  int blkdev_report_zones(struct block_device *bdev, sector_t sector,
d41003513   Christoph Hellwig   block: rework zon...
142
  			unsigned int nr_zones, report_zones_cb cb, void *data)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
143
  {
ceeb373aa   Damien Le Moal   block: Simplify r...
144
  	struct gendisk *disk = bdev->bd_disk;
5eac3eb30   Damien Le Moal   block: Remove par...
145
  	sector_t capacity = get_capacity(disk);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
146

d41003513   Christoph Hellwig   block: rework zon...
147
148
  	if (!blk_queue_is_zoned(bdev_get_queue(bdev)) ||
  	    WARN_ON_ONCE(!disk->fops->report_zones))
e76239a37   Christoph Hellwig   block: add a repo...
149
  		return -EOPNOTSUPP;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
150

d41003513   Christoph Hellwig   block: rework zon...
151
  	if (!nr_zones || sector >= capacity)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
152
  		return 0;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
153

d41003513   Christoph Hellwig   block: rework zon...
154
  	return disk->fops->report_zones(disk, sector, nr_zones, cb, data);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
155
156
  }
  EXPORT_SYMBOL_GPL(blkdev_report_zones);
1ee533eca   Damien Le Moal   block: improve ha...
157
158
  static inline unsigned long *blk_alloc_zone_bitmap(int node,
  						   unsigned int nr_zones)
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
159
  {
1ee533eca   Damien Le Moal   block: improve ha...
160
161
162
  	return kcalloc_node(BITS_TO_LONGS(nr_zones), sizeof(unsigned long),
  			    GFP_NOIO, node);
  }
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
163

1ee533eca   Damien Le Moal   block: improve ha...
164
165
166
  static int blk_zone_need_reset_cb(struct blk_zone *zone, unsigned int idx,
  				  void *data)
  {
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
167
  	/*
1ee533eca   Damien Le Moal   block: improve ha...
168
169
  	 * For an all-zones reset, ignore conventional, empty, read-only
  	 * and offline zones.
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
170
  	 */
1ee533eca   Damien Le Moal   block: improve ha...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
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
231
232
233
234
235
236
237
238
239
  	switch (zone->cond) {
  	case BLK_ZONE_COND_NOT_WP:
  	case BLK_ZONE_COND_EMPTY:
  	case BLK_ZONE_COND_READONLY:
  	case BLK_ZONE_COND_OFFLINE:
  		return 0;
  	default:
  		set_bit(idx, (unsigned long *)data);
  		return 0;
  	}
  }
  
  static int blkdev_zone_reset_all_emulated(struct block_device *bdev,
  					  gfp_t gfp_mask)
  {
  	struct request_queue *q = bdev_get_queue(bdev);
  	sector_t capacity = get_capacity(bdev->bd_disk);
  	sector_t zone_sectors = blk_queue_zone_sectors(q);
  	unsigned long *need_reset;
  	struct bio *bio = NULL;
  	sector_t sector = 0;
  	int ret;
  
  	need_reset = blk_alloc_zone_bitmap(q->node, q->nr_zones);
  	if (!need_reset)
  		return -ENOMEM;
  
  	ret = bdev->bd_disk->fops->report_zones(bdev->bd_disk, 0,
  				q->nr_zones, blk_zone_need_reset_cb,
  				need_reset);
  	if (ret < 0)
  		goto out_free_need_reset;
  
  	ret = 0;
  	while (sector < capacity) {
  		if (!test_bit(blk_queue_zone_no(q, sector), need_reset)) {
  			sector += zone_sectors;
  			continue;
  		}
  
  		bio = blk_next_bio(bio, 0, gfp_mask);
  		bio_set_dev(bio, bdev);
  		bio->bi_opf = REQ_OP_ZONE_RESET | REQ_SYNC;
  		bio->bi_iter.bi_sector = sector;
  		sector += zone_sectors;
  
  		/* This may take a while, so be nice to others */
  		cond_resched();
  	}
  
  	if (bio) {
  		ret = submit_bio_wait(bio);
  		bio_put(bio);
  	}
  
  out_free_need_reset:
  	kfree(need_reset);
  	return ret;
  }
  
  static int blkdev_zone_reset_all(struct block_device *bdev, gfp_t gfp_mask)
  {
  	struct bio bio;
  
  	bio_init(&bio, NULL, 0);
  	bio_set_dev(&bio, bdev);
  	bio.bi_opf = REQ_OP_ZONE_RESET_ALL | REQ_SYNC;
  
  	return submit_bio_wait(&bio);
6e33dbf28   Chaitanya Kulkarni   blk-zoned: implem...
240
  }
6a0cb1bc1   Hannes Reinecke   block: Implement ...
241
  /**
6c1b1da58   Ajay Joshi   block: add zone o...
242
   * blkdev_zone_mgmt - Execute a zone management operation on a range of zones
6a0cb1bc1   Hannes Reinecke   block: Implement ...
243
   * @bdev:	Target block device
6c1b1da58   Ajay Joshi   block: add zone o...
244
245
246
247
   * @op:		Operation to be performed on the zones
   * @sector:	Start sector of the first zone to operate on
   * @nr_sectors:	Number of sectors, should be at least the length of one zone and
   *		must be zone size aligned.
6a0cb1bc1   Hannes Reinecke   block: Implement ...
248
249
250
   * @gfp_mask:	Memory allocation flags (for bio_alloc)
   *
   * Description:
6c1b1da58   Ajay Joshi   block: add zone o...
251
   *    Perform the specified operation on the range of zones specified by
6a0cb1bc1   Hannes Reinecke   block: Implement ...
252
253
   *    @sector..@sector+@nr_sectors. Specifying the entire disk sector range
   *    is valid, but the specified range should not contain conventional zones.
6c1b1da58   Ajay Joshi   block: add zone o...
254
255
   *    The operation to execute on each zone can be a zone reset, open, close
   *    or finish request.
6a0cb1bc1   Hannes Reinecke   block: Implement ...
256
   */
6c1b1da58   Ajay Joshi   block: add zone o...
257
258
259
  int blkdev_zone_mgmt(struct block_device *bdev, enum req_opf op,
  		     sector_t sector, sector_t nr_sectors,
  		     gfp_t gfp_mask)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
260
261
  {
  	struct request_queue *q = bdev_get_queue(bdev);
6c1b1da58   Ajay Joshi   block: add zone o...
262
  	sector_t zone_sectors = blk_queue_zone_sectors(q);
5eac3eb30   Damien Le Moal   block: Remove par...
263
  	sector_t capacity = get_capacity(bdev->bd_disk);
6a0cb1bc1   Hannes Reinecke   block: Implement ...
264
  	sector_t end_sector = sector + nr_sectors;
a2d6b3a2d   Damien Le Moal   block: Improve zo...
265
  	struct bio *bio = NULL;
1ee533eca   Damien Le Moal   block: improve ha...
266
  	int ret = 0;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
267

6a0cb1bc1   Hannes Reinecke   block: Implement ...
268
269
  	if (!blk_queue_is_zoned(q))
  		return -EOPNOTSUPP;
a2d6b3a2d   Damien Le Moal   block: Improve zo...
270
271
  	if (bdev_read_only(bdev))
  		return -EPERM;
6c1b1da58   Ajay Joshi   block: add zone o...
272
273
  	if (!op_is_zone_mgmt(op))
  		return -EOPNOTSUPP;
11bde9860   Alexey Dobriyan   block, zoned: fix...
274
  	if (end_sector <= sector || end_sector > capacity)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
275
276
277
278
  		/* Out of range */
  		return -EINVAL;
  
  	/* Check alignment (handle eventual smaller last zone) */
6a0cb1bc1   Hannes Reinecke   block: Implement ...
279
280
  	if (sector & (zone_sectors - 1))
  		return -EINVAL;
5eac3eb30   Damien Le Moal   block: Remove par...
281
  	if ((nr_sectors & (zone_sectors - 1)) && end_sector != capacity)
6a0cb1bc1   Hannes Reinecke   block: Implement ...
282
  		return -EINVAL;
1ee533eca   Damien Le Moal   block: improve ha...
283
284
285
286
287
288
289
290
291
292
293
  	/*
  	 * In the case of a zone reset operation over all zones,
  	 * REQ_OP_ZONE_RESET_ALL can be used with devices supporting this
  	 * command. For other devices, we emulate this command behavior by
  	 * identifying the zones needing a reset.
  	 */
  	if (op == REQ_OP_ZONE_RESET && sector == 0 && nr_sectors == capacity) {
  		if (!blk_queue_zone_resetall(q))
  			return blkdev_zone_reset_all_emulated(bdev, gfp_mask);
  		return blkdev_zone_reset_all(bdev, gfp_mask);
  	}
6a0cb1bc1   Hannes Reinecke   block: Implement ...
294
  	while (sector < end_sector) {
a2d6b3a2d   Damien Le Moal   block: Improve zo...
295
  		bio = blk_next_bio(bio, 0, gfp_mask);
74d46992e   Christoph Hellwig   block: replace bi...
296
  		bio_set_dev(bio, bdev);
8e42d239c   Chaitanya Kulkarni   block: mark zone-...
297
  		bio->bi_opf = op | REQ_SYNC;
c7a1d926d   Damien Le Moal   block: Simplify R...
298
  		bio->bi_iter.bi_sector = sector;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
299
300
301
302
  		sector += zone_sectors;
  
  		/* This may take a while, so be nice to others */
  		cond_resched();
6a0cb1bc1   Hannes Reinecke   block: Implement ...
303
  	}
a2d6b3a2d   Damien Le Moal   block: Improve zo...
304
305
  	ret = submit_bio_wait(bio);
  	bio_put(bio);
a2d6b3a2d   Damien Le Moal   block: Improve zo...
306
  	return ret;
6a0cb1bc1   Hannes Reinecke   block: Implement ...
307
  }
6c1b1da58   Ajay Joshi   block: add zone o...
308
  EXPORT_SYMBOL_GPL(blkdev_zone_mgmt);
3ed05a987   Shaun Tancheff   blk-zoned: implem...
309

d41003513   Christoph Hellwig   block: rework zon...
310
311
312
313
314
315
316
317
318
319
320
321
322
  struct zone_report_args {
  	struct blk_zone __user *zones;
  };
  
  static int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,
  				    void *data)
  {
  	struct zone_report_args *args = data;
  
  	if (copy_to_user(&args->zones[idx], zone, sizeof(struct blk_zone)))
  		return -EFAULT;
  	return 0;
  }
56c4bddb9   Bart Van Assche   block: Suppress k...
323
  /*
3ed05a987   Shaun Tancheff   blk-zoned: implem...
324
325
326
327
328
329
330
   * 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;
d41003513   Christoph Hellwig   block: rework zon...
331
  	struct zone_report_args args;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
332
333
  	struct request_queue *q;
  	struct blk_zone_report rep;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
334
335
336
337
338
339
340
341
342
343
344
  	int ret;
  
  	if (!argp)
  		return -EINVAL;
  
  	q = bdev_get_queue(bdev);
  	if (!q)
  		return -ENXIO;
  
  	if (!blk_queue_is_zoned(q))
  		return -ENOTTY;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
345
346
347
348
349
  	if (copy_from_user(&rep, argp, sizeof(struct blk_zone_report)))
  		return -EFAULT;
  
  	if (!rep.nr_zones)
  		return -EINVAL;
d41003513   Christoph Hellwig   block: rework zon...
350
351
352
353
354
  	args.zones = argp + sizeof(struct blk_zone_report);
  	ret = blkdev_report_zones(bdev, rep.sector, rep.nr_zones,
  				  blkdev_copy_zone_to_user, &args);
  	if (ret < 0)
  		return ret;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
355

d41003513   Christoph Hellwig   block: rework zon...
356
  	rep.nr_zones = ret;
82394db73   Matias Bjørling   block: add capaci...
357
  	rep.flags = BLK_ZONE_REP_CAPACITY;
d41003513   Christoph Hellwig   block: rework zon...
358
359
360
  	if (copy_to_user(argp, &rep, sizeof(struct blk_zone_report)))
  		return -EFAULT;
  	return 0;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
361
  }
e51135059   Shin'ichiro Kawasaki   block: Discard pa...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  static int blkdev_truncate_zone_range(struct block_device *bdev, fmode_t mode,
  				      const struct blk_zone_range *zrange)
  {
  	loff_t start, end;
  
  	if (zrange->sector + zrange->nr_sectors <= zrange->sector ||
  	    zrange->sector + zrange->nr_sectors > get_capacity(bdev->bd_disk))
  		/* Out of range */
  		return -EINVAL;
  
  	start = zrange->sector << SECTOR_SHIFT;
  	end = ((zrange->sector + zrange->nr_sectors) << SECTOR_SHIFT) - 1;
  
  	return truncate_bdev_range(bdev, mode, start, end);
  }
56c4bddb9   Bart Van Assche   block: Suppress k...
377
  /*
e876df1fe   Ajay Joshi   block: add zone o...
378
   * BLKRESETZONE, BLKOPENZONE, BLKCLOSEZONE and BLKFINISHZONE ioctl processing.
3ed05a987   Shaun Tancheff   blk-zoned: implem...
379
380
   * Called from blkdev_ioctl.
   */
e876df1fe   Ajay Joshi   block: add zone o...
381
382
  int blkdev_zone_mgmt_ioctl(struct block_device *bdev, fmode_t mode,
  			   unsigned int cmd, unsigned long arg)
3ed05a987   Shaun Tancheff   blk-zoned: implem...
383
384
385
386
  {
  	void __user *argp = (void __user *)arg;
  	struct request_queue *q;
  	struct blk_zone_range zrange;
e876df1fe   Ajay Joshi   block: add zone o...
387
  	enum req_opf op;
e51135059   Shin'ichiro Kawasaki   block: Discard pa...
388
  	int ret;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
389
390
391
392
393
394
395
396
397
398
  
  	if (!argp)
  		return -EINVAL;
  
  	q = bdev_get_queue(bdev);
  	if (!q)
  		return -ENXIO;
  
  	if (!blk_queue_is_zoned(q))
  		return -ENOTTY;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
399
400
401
402
403
  	if (!(mode & FMODE_WRITE))
  		return -EBADF;
  
  	if (copy_from_user(&zrange, argp, sizeof(struct blk_zone_range)))
  		return -EFAULT;
e876df1fe   Ajay Joshi   block: add zone o...
404
405
406
  	switch (cmd) {
  	case BLKRESETZONE:
  		op = REQ_OP_ZONE_RESET;
e51135059   Shin'ichiro Kawasaki   block: Discard pa...
407
408
  
  		/* Invalidate the page cache, including dirty pages. */
5e84e9d61   Shin'ichiro Kawasaki   block: Hold inval...
409
  		filemap_invalidate_lock(bdev->bd_inode->i_mapping);
e51135059   Shin'ichiro Kawasaki   block: Discard pa...
410
411
  		ret = blkdev_truncate_zone_range(bdev, mode, &zrange);
  		if (ret)
5e84e9d61   Shin'ichiro Kawasaki   block: Hold inval...
412
  			goto fail;
e876df1fe   Ajay Joshi   block: add zone o...
413
414
415
416
417
418
419
420
421
422
423
424
425
  		break;
  	case BLKOPENZONE:
  		op = REQ_OP_ZONE_OPEN;
  		break;
  	case BLKCLOSEZONE:
  		op = REQ_OP_ZONE_CLOSE;
  		break;
  	case BLKFINISHZONE:
  		op = REQ_OP_ZONE_FINISH;
  		break;
  	default:
  		return -ENOTTY;
  	}
e51135059   Shin'ichiro Kawasaki   block: Discard pa...
426
427
  	ret = blkdev_zone_mgmt(bdev, op, zrange.sector, zrange.nr_sectors,
  			       GFP_KERNEL);
5e84e9d61   Shin'ichiro Kawasaki   block: Hold inval...
428
429
430
  fail:
  	if (cmd == BLKRESETZONE)
  		filemap_invalidate_unlock(bdev->bd_inode->i_mapping);
e51135059   Shin'ichiro Kawasaki   block: Discard pa...
431
432
  
  	return ret;
3ed05a987   Shaun Tancheff   blk-zoned: implem...
433
  }
bf5054569   Damien Le Moal   block: Introduce ...
434

bf5054569   Damien Le Moal   block: Introduce ...
435
436
  void blk_queue_free_zone_bitmaps(struct request_queue *q)
  {
f216fdd77   Christoph Hellwig   block: replace se...
437
438
  	kfree(q->conv_zones_bitmap);
  	q->conv_zones_bitmap = NULL;
bf5054569   Damien Le Moal   block: Introduce ...
439
440
441
  	kfree(q->seq_zones_wlock);
  	q->seq_zones_wlock = NULL;
  }
d41003513   Christoph Hellwig   block: rework zon...
442
443
  struct blk_revalidate_zone_args {
  	struct gendisk	*disk;
f216fdd77   Christoph Hellwig   block: replace se...
444
  	unsigned long	*conv_zones_bitmap;
d41003513   Christoph Hellwig   block: rework zon...
445
  	unsigned long	*seq_zones_wlock;
e94f58194   Christoph Hellwig   block: allocate t...
446
  	unsigned int	nr_zones;
6c6b35491   Christoph Hellwig   block: set the zo...
447
  	sector_t	zone_sectors;
d41003513   Christoph Hellwig   block: rework zon...
448
449
  	sector_t	sector;
  };
d9dd73087   Damien Le Moal   block: Enhance bl...
450
451
452
  /*
   * Helper function to check the validity of zones of a zoned block device.
   */
d41003513   Christoph Hellwig   block: rework zon...
453
454
  static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
  				  void *data)
d9dd73087   Damien Le Moal   block: Enhance bl...
455
  {
d41003513   Christoph Hellwig   block: rework zon...
456
457
  	struct blk_revalidate_zone_args *args = data;
  	struct gendisk *disk = args->disk;
d9dd73087   Damien Le Moal   block: Enhance bl...
458
  	struct request_queue *q = disk->queue;
d9dd73087   Damien Le Moal   block: Enhance bl...
459
460
461
462
463
464
  	sector_t capacity = get_capacity(disk);
  
  	/*
  	 * All zones must have the same size, with the exception on an eventual
  	 * smaller last zone.
  	 */
6c6b35491   Christoph Hellwig   block: set the zo...
465
466
467
468
469
470
471
  	if (zone->start == 0) {
  		if (zone->len == 0 || !is_power_of_2(zone->len)) {
  			pr_warn("%s: Invalid zoned device with non power of two zone size (%llu)
  ",
  				disk->disk_name, zone->len);
  			return -ENODEV;
  		}
d9dd73087   Damien Le Moal   block: Enhance bl...
472

6c6b35491   Christoph Hellwig   block: set the zo...
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  		args->zone_sectors = zone->len;
  		args->nr_zones = (capacity + zone->len - 1) >> ilog2(zone->len);
  	} else if (zone->start + args->zone_sectors < capacity) {
  		if (zone->len != args->zone_sectors) {
  			pr_warn("%s: Invalid zoned device with non constant zone size
  ",
  				disk->disk_name);
  			return -ENODEV;
  		}
  	} else {
  		if (zone->len > args->zone_sectors) {
  			pr_warn("%s: Invalid zoned device with larger last zone size
  ",
  				disk->disk_name);
  			return -ENODEV;
  		}
d9dd73087   Damien Le Moal   block: Enhance bl...
489
490
491
  	}
  
  	/* Check for holes in the zone report */
d41003513   Christoph Hellwig   block: rework zon...
492
  	if (zone->start != args->sector) {
d9dd73087   Damien Le Moal   block: Enhance bl...
493
494
  		pr_warn("%s: Zone gap at sectors %llu..%llu
  ",
d41003513   Christoph Hellwig   block: rework zon...
495
496
  			disk->disk_name, args->sector, zone->start);
  		return -ENODEV;
d9dd73087   Damien Le Moal   block: Enhance bl...
497
498
499
500
501
  	}
  
  	/* Check zone type */
  	switch (zone->type) {
  	case BLK_ZONE_TYPE_CONVENTIONAL:
e94f58194   Christoph Hellwig   block: allocate t...
502
503
504
505
506
507
508
509
  		if (!args->conv_zones_bitmap) {
  			args->conv_zones_bitmap =
  				blk_alloc_zone_bitmap(q->node, args->nr_zones);
  			if (!args->conv_zones_bitmap)
  				return -ENOMEM;
  		}
  		set_bit(idx, args->conv_zones_bitmap);
  		break;
d9dd73087   Damien Le Moal   block: Enhance bl...
510
511
  	case BLK_ZONE_TYPE_SEQWRITE_REQ:
  	case BLK_ZONE_TYPE_SEQWRITE_PREF:
e94f58194   Christoph Hellwig   block: allocate t...
512
513
514
515
516
517
  		if (!args->seq_zones_wlock) {
  			args->seq_zones_wlock =
  				blk_alloc_zone_bitmap(q->node, args->nr_zones);
  			if (!args->seq_zones_wlock)
  				return -ENOMEM;
  		}
d9dd73087   Damien Le Moal   block: Enhance bl...
518
519
520
521
522
  		break;
  	default:
  		pr_warn("%s: Invalid zone type 0x%x at sectors %llu
  ",
  			disk->disk_name, (int)zone->type, zone->start);
d41003513   Christoph Hellwig   block: rework zon...
523
  		return -ENODEV;
d9dd73087   Damien Le Moal   block: Enhance bl...
524
  	}
d41003513   Christoph Hellwig   block: rework zon...
525
526
527
  	args->sector += zone->len;
  	return 0;
  }
bf5054569   Damien Le Moal   block: Introduce ...
528
529
530
  /**
   * blk_revalidate_disk_zones - (re)allocate and initialize zone bitmaps
   * @disk:	Target disk
e732671aa   Damien Le Moal   block: Modify rev...
531
   * @update_driver_data:	Callback to update driver data on the frozen disk
bf5054569   Damien Le Moal   block: Introduce ...
532
533
534
   *
   * Helper function for low-level device drivers to (re) allocate and initialize
   * a disk request queue zone bitmaps. This functions should normally be called
ae58954d8   Christoph Hellwig   block: don't hand...
535
536
537
   * within the disk ->revalidate method for blk-mq based drivers.  For BIO based
   * drivers only q->nr_zones needs to be updated so that the sysfs exposed value
   * is correct.
e732671aa   Damien Le Moal   block: Modify rev...
538
539
540
   * If the @update_driver_data callback function is not NULL, the callback is
   * executed with the device request queue frozen after all zones have been
   * checked.
bf5054569   Damien Le Moal   block: Introduce ...
541
   */
e732671aa   Damien Le Moal   block: Modify rev...
542
543
  int blk_revalidate_disk_zones(struct gendisk *disk,
  			      void (*update_driver_data)(struct gendisk *disk))
bf5054569   Damien Le Moal   block: Introduce ...
544
545
  {
  	struct request_queue *q = disk->queue;
e94f58194   Christoph Hellwig   block: allocate t...
546
547
  	struct blk_revalidate_zone_args args = {
  		.disk		= disk,
e94f58194   Christoph Hellwig   block: allocate t...
548
  	};
6c6b35491   Christoph Hellwig   block: set the zo...
549
550
  	unsigned int noio_flag;
  	int ret;
bf5054569   Damien Le Moal   block: Introduce ...
551

c98c3d09f   Christoph Hellwig   block: cleanup th...
552
553
  	if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
  		return -EIO;
ae58954d8   Christoph Hellwig   block: don't hand...
554
555
  	if (WARN_ON_ONCE(!queue_is_mq(q)))
  		return -EIO;
bf5054569   Damien Le Moal   block: Introduce ...
556

1a1206dc4   Johannes Thumshirn   block: don't do r...
557
558
  	if (!get_capacity(disk))
  		return -EIO;
e94f58194   Christoph Hellwig   block: allocate t...
559
  	/*
6c6b35491   Christoph Hellwig   block: set the zo...
560
561
  	 * Ensure that all memory allocations in this context are done as if
  	 * GFP_NOIO was specified.
e94f58194   Christoph Hellwig   block: allocate t...
562
  	 */
6c6b35491   Christoph Hellwig   block: set the zo...
563
564
565
  	noio_flag = memalloc_noio_save();
  	ret = disk->fops->report_zones(disk, 0, UINT_MAX,
  				       blk_revalidate_zone_cb, &args);
2afdeb23e   Damien Le Moal   block: Improve bl...
566
567
568
569
570
  	if (!ret) {
  		pr_warn("%s: No zones reported
  ", disk->disk_name);
  		ret = -ENODEV;
  	}
6c6b35491   Christoph Hellwig   block: set the zo...
571
  	memalloc_noio_restore(noio_flag);
bf5054569   Damien Le Moal   block: Introduce ...
572

bf5054569   Damien Le Moal   block: Introduce ...
573
  	/*
2afdeb23e   Damien Le Moal   block: Improve bl...
574
575
576
577
578
579
580
581
582
583
584
  	 * If zones where reported, make sure that the entire disk capacity
  	 * has been checked.
  	 */
  	if (ret > 0 && args.sector != get_capacity(disk)) {
  		pr_warn("%s: Missing zones from sector %llu
  ",
  			disk->disk_name, args.sector);
  		ret = -ENODEV;
  	}
  
  	/*
6c6b35491   Christoph Hellwig   block: set the zo...
585
586
587
  	 * Install the new bitmaps and update nr_zones only once the queue is
  	 * stopped and all I/Os are completed (i.e. a scheduler is not
  	 * referencing the bitmaps).
bf5054569   Damien Le Moal   block: Introduce ...
588
589
  	 */
  	blk_mq_freeze_queue(q);
2afdeb23e   Damien Le Moal   block: Improve bl...
590
  	if (ret > 0) {
6c6b35491   Christoph Hellwig   block: set the zo...
591
  		blk_queue_chunk_sectors(q, args.zone_sectors);
e94f58194   Christoph Hellwig   block: allocate t...
592
  		q->nr_zones = args.nr_zones;
d41003513   Christoph Hellwig   block: rework zon...
593
  		swap(q->seq_zones_wlock, args.seq_zones_wlock);
f216fdd77   Christoph Hellwig   block: replace se...
594
  		swap(q->conv_zones_bitmap, args.conv_zones_bitmap);
e732671aa   Damien Le Moal   block: Modify rev...
595
596
  		if (update_driver_data)
  			update_driver_data(disk);
d41003513   Christoph Hellwig   block: rework zon...
597
598
  		ret = 0;
  	} else {
bf5054569   Damien Le Moal   block: Introduce ...
599
600
  		pr_warn("%s: failed to revalidate zones
  ", disk->disk_name);
bf5054569   Damien Le Moal   block: Introduce ...
601
  		blk_queue_free_zone_bitmaps(q);
bf5054569   Damien Le Moal   block: Introduce ...
602
  	}
d41003513   Christoph Hellwig   block: rework zon...
603
  	blk_mq_unfreeze_queue(q);
bf5054569   Damien Le Moal   block: Introduce ...
604

d41003513   Christoph Hellwig   block: rework zon...
605
  	kfree(args.seq_zones_wlock);
f216fdd77   Christoph Hellwig   block: replace se...
606
  	kfree(args.conv_zones_bitmap);
bf5054569   Damien Le Moal   block: Introduce ...
607
608
609
  	return ret;
  }
  EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
508aebb80   Damien Le Moal   block: introduce ...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  
  void blk_queue_clear_zone_settings(struct request_queue *q)
  {
  	blk_mq_freeze_queue(q);
  
  	blk_queue_free_zone_bitmaps(q);
  	blk_queue_flag_clear(QUEUE_FLAG_ZONE_RESETALL, q);
  	q->required_elevator_features &= ~ELEVATOR_F_ZBD_SEQ_WRITE;
  	q->nr_zones = 0;
  	q->max_open_zones = 0;
  	q->max_active_zones = 0;
  	q->limits.chunk_sectors = 0;
  	q->limits.zone_write_granularity = 0;
  	q->limits.max_zone_append_sectors = 0;
  
  	blk_mq_unfreeze_queue(q);
  }