blob: ea43f6892ced1b42b6c80c4849c39fb8cf599b03 [file] [log] [blame]
Dmitry Fomichevbae9a0a2019-08-02 15:02:50 -07001// SPDX-License-Identifier: GPL-2.0-only
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09002/*
3 * Copyright (C) 2017 Western Digital Corporation or its affiliates.
4 *
5 * This file is released under the GPL.
6 */
7
8#include "dm-zoned.h"
9
10#include <linux/module.h>
11
12#define DM_MSG_PREFIX "zoned"
13
14#define DMZ_MIN_BIOS 8192
15
Hannes Reineckebd5c4032020-05-11 10:24:30 +020016#define DMZ_MAX_DEVS 2
17
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090018/*
19 * Zone BIO context.
20 */
21struct dmz_bioctx {
Hannes Reinecke52d67752020-05-11 10:24:25 +020022 struct dmz_dev *dev;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090023 struct dm_zone *zone;
24 struct bio *bio;
John Pittman092b56482018-08-23 13:35:57 -040025 refcount_t ref;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090026};
27
28/*
29 * Chunk work descriptor.
30 */
31struct dm_chunk_work {
32 struct work_struct work;
John Pittman092b56482018-08-23 13:35:57 -040033 refcount_t refcount;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090034 struct dmz_target *target;
35 unsigned int chunk;
36 struct bio_list bio_list;
37};
38
39/*
40 * Target descriptor.
41 */
42struct dmz_target {
Hannes Reineckebd5c4032020-05-11 10:24:30 +020043 struct dm_dev *ddev[DMZ_MAX_DEVS];
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090044
45 unsigned long flags;
46
47 /* Zoned block device information */
48 struct dmz_dev *dev;
49
50 /* For metadata handling */
51 struct dmz_metadata *metadata;
52
53 /* For reclaim */
54 struct dmz_reclaim *reclaim;
55
56 /* For chunk work */
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090057 struct radix_tree_root chunk_rxtree;
58 struct workqueue_struct *chunk_wq;
Mike Snitzer72d711c2018-05-22 18:26:20 -040059 struct mutex chunk_lock;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090060
61 /* For cloned BIOs to zones */
Kent Overstreet6f1c8192018-05-20 18:25:53 -040062 struct bio_set bio_set;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090063
64 /* For flush */
65 spinlock_t flush_lock;
66 struct bio_list flush_list;
67 struct delayed_work flush_work;
68 struct workqueue_struct *flush_wq;
69};
70
71/*
72 * Flush intervals (seconds).
73 */
74#define DMZ_FLUSH_PERIOD (10 * HZ)
75
76/*
77 * Target BIO completion.
78 */
79static inline void dmz_bio_endio(struct bio *bio, blk_status_t status)
80{
Hannes Reinecke52d67752020-05-11 10:24:25 +020081 struct dmz_bioctx *bioctx =
82 dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +090083
Damien Le Moald57f9da2018-11-30 15:31:48 +090084 if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
85 bio->bi_status = status;
Hannes Reineckebd5c4032020-05-11 10:24:30 +020086 if (bioctx->dev && bio->bi_status != BLK_STS_OK)
Hannes Reinecke52d67752020-05-11 10:24:25 +020087 bioctx->dev->flags |= DMZ_CHECK_BDEV;
Damien Le Moald57f9da2018-11-30 15:31:48 +090088
89 if (refcount_dec_and_test(&bioctx->ref)) {
90 struct dm_zone *zone = bioctx->zone;
91
92 if (zone) {
93 if (bio->bi_status != BLK_STS_OK &&
94 bio_op(bio) == REQ_OP_WRITE &&
95 dmz_is_seq(zone))
96 set_bit(DMZ_SEQ_WRITE_ERR, &zone->flags);
97 dmz_deactivate_zone(zone);
98 }
99 bio_endio(bio);
100 }
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900101}
102
103/*
Damien Le Moald57f9da2018-11-30 15:31:48 +0900104 * Completion callback for an internally cloned target BIO. This terminates the
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900105 * target BIO when there are no more references to its context.
106 */
Damien Le Moald57f9da2018-11-30 15:31:48 +0900107static void dmz_clone_endio(struct bio *clone)
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900108{
Damien Le Moald57f9da2018-11-30 15:31:48 +0900109 struct dmz_bioctx *bioctx = clone->bi_private;
110 blk_status_t status = clone->bi_status;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900111
Damien Le Moald57f9da2018-11-30 15:31:48 +0900112 bio_put(clone);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900113 dmz_bio_endio(bioctx->bio, status);
114}
115
116/*
Damien Le Moald57f9da2018-11-30 15:31:48 +0900117 * Issue a clone of a target BIO. The clone may only partially process the
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900118 * original target BIO.
119 */
Damien Le Moald57f9da2018-11-30 15:31:48 +0900120static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,
121 struct bio *bio, sector_t chunk_block,
122 unsigned int nr_blocks)
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900123{
Hannes Reinecke52d67752020-05-11 10:24:25 +0200124 struct dmz_bioctx *bioctx =
125 dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
126 struct dmz_dev *dev = dmz_zone_to_dev(dmz->metadata, zone);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900127 struct bio *clone;
128
Hannes Reinecke52d67752020-05-11 10:24:25 +0200129 if (dev->flags & DMZ_BDEV_DYING)
130 return -EIO;
131
Kent Overstreet6f1c8192018-05-20 18:25:53 -0400132 clone = bio_clone_fast(bio, GFP_NOIO, &dmz->bio_set);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900133 if (!clone)
134 return -ENOMEM;
135
Hannes Reinecke52d67752020-05-11 10:24:25 +0200136 bio_set_dev(clone, dev->bdev);
137 bioctx->dev = dev;
Damien Le Moald57f9da2018-11-30 15:31:48 +0900138 clone->bi_iter.bi_sector =
139 dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900140 clone->bi_iter.bi_size = dmz_blk2sect(nr_blocks) << SECTOR_SHIFT;
Damien Le Moald57f9da2018-11-30 15:31:48 +0900141 clone->bi_end_io = dmz_clone_endio;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900142 clone->bi_private = bioctx;
143
144 bio_advance(bio, clone->bi_iter.bi_size);
145
John Pittman092b56482018-08-23 13:35:57 -0400146 refcount_inc(&bioctx->ref);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900147 generic_make_request(clone);
148
Damien Le Moald57f9da2018-11-30 15:31:48 +0900149 if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone))
150 zone->wp_block += nr_blocks;
151
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900152 return 0;
153}
154
155/*
156 * Zero out pages of discarded blocks accessed by a read BIO.
157 */
158static void dmz_handle_read_zero(struct dmz_target *dmz, struct bio *bio,
159 sector_t chunk_block, unsigned int nr_blocks)
160{
161 unsigned int size = nr_blocks << DMZ_BLOCK_SHIFT;
162
163 /* Clear nr_blocks */
164 swap(bio->bi_iter.bi_size, size);
165 zero_fill_bio(bio);
166 swap(bio->bi_iter.bi_size, size);
167
168 bio_advance(bio, size);
169}
170
171/*
172 * Process a read BIO.
173 */
174static int dmz_handle_read(struct dmz_target *dmz, struct dm_zone *zone,
175 struct bio *bio)
176{
Hannes Reinecke36820562020-05-11 10:24:21 +0200177 struct dmz_metadata *zmd = dmz->metadata;
178 sector_t chunk_block = dmz_chunk_block(zmd, dmz_bio_block(bio));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900179 unsigned int nr_blocks = dmz_bio_blocks(bio);
180 sector_t end_block = chunk_block + nr_blocks;
181 struct dm_zone *rzone, *bzone;
182 int ret;
183
184 /* Read into unmapped chunks need only zeroing the BIO buffer */
185 if (!zone) {
186 zero_fill_bio(bio);
187 return 0;
188 }
189
Hannes Reinecke2234e732020-05-11 10:24:22 +0200190 DMDEBUG("(%s): READ chunk %llu -> %s zone %u, block %llu, %u blocks",
191 dmz_metadata_label(zmd),
192 (unsigned long long)dmz_bio_chunk(zmd, bio),
193 (dmz_is_rnd(zone) ? "RND" : "SEQ"),
194 zone->id,
195 (unsigned long long)chunk_block, nr_blocks);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900196
197 /* Check block validity to determine the read location */
198 bzone = zone->bzone;
199 while (chunk_block < end_block) {
200 nr_blocks = 0;
201 if (dmz_is_rnd(zone) || chunk_block < zone->wp_block) {
202 /* Test block validity in the data zone */
Hannes Reinecke36820562020-05-11 10:24:21 +0200203 ret = dmz_block_valid(zmd, zone, chunk_block);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900204 if (ret < 0)
205 return ret;
206 if (ret > 0) {
207 /* Read data zone blocks */
208 nr_blocks = ret;
209 rzone = zone;
210 }
211 }
212
213 /*
214 * No valid blocks found in the data zone.
215 * Check the buffer zone, if there is one.
216 */
217 if (!nr_blocks && bzone) {
Hannes Reinecke36820562020-05-11 10:24:21 +0200218 ret = dmz_block_valid(zmd, bzone, chunk_block);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900219 if (ret < 0)
220 return ret;
221 if (ret > 0) {
222 /* Read buffer zone blocks */
223 nr_blocks = ret;
224 rzone = bzone;
225 }
226 }
227
228 if (nr_blocks) {
229 /* Valid blocks found: read them */
Hannes Reinecke52d67752020-05-11 10:24:25 +0200230 nr_blocks = min_t(unsigned int, nr_blocks,
231 end_block - chunk_block);
232 ret = dmz_submit_bio(dmz, rzone, bio,
233 chunk_block, nr_blocks);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900234 if (ret)
235 return ret;
236 chunk_block += nr_blocks;
237 } else {
238 /* No valid block: zeroout the current BIO block */
239 dmz_handle_read_zero(dmz, bio, chunk_block, 1);
240 chunk_block++;
241 }
242 }
243
244 return 0;
245}
246
247/*
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900248 * Write blocks directly in a data zone, at the write pointer.
249 * If a buffer zone is assigned, invalidate the blocks written
250 * in place.
251 */
252static int dmz_handle_direct_write(struct dmz_target *dmz,
253 struct dm_zone *zone, struct bio *bio,
254 sector_t chunk_block,
255 unsigned int nr_blocks)
256{
257 struct dmz_metadata *zmd = dmz->metadata;
258 struct dm_zone *bzone = zone->bzone;
259 int ret;
260
261 if (dmz_is_readonly(zone))
262 return -EROFS;
263
264 /* Submit write */
Damien Le Moald57f9da2018-11-30 15:31:48 +0900265 ret = dmz_submit_bio(dmz, zone, bio, chunk_block, nr_blocks);
266 if (ret)
267 return ret;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900268
269 /*
270 * Validate the blocks in the data zone and invalidate
271 * in the buffer zone, if there is one.
272 */
273 ret = dmz_validate_blocks(zmd, zone, chunk_block, nr_blocks);
274 if (ret == 0 && bzone)
275 ret = dmz_invalidate_blocks(zmd, bzone, chunk_block, nr_blocks);
276
277 return ret;
278}
279
280/*
281 * Write blocks in the buffer zone of @zone.
282 * If no buffer zone is assigned yet, get one.
283 * Called with @zone write locked.
284 */
285static int dmz_handle_buffered_write(struct dmz_target *dmz,
286 struct dm_zone *zone, struct bio *bio,
287 sector_t chunk_block,
288 unsigned int nr_blocks)
289{
290 struct dmz_metadata *zmd = dmz->metadata;
291 struct dm_zone *bzone;
292 int ret;
293
294 /* Get the buffer zone. One will be allocated if needed */
295 bzone = dmz_get_chunk_buffer(zmd, zone);
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -0700296 if (IS_ERR(bzone))
297 return PTR_ERR(bzone);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900298
299 if (dmz_is_readonly(bzone))
300 return -EROFS;
301
302 /* Submit write */
Damien Le Moald57f9da2018-11-30 15:31:48 +0900303 ret = dmz_submit_bio(dmz, bzone, bio, chunk_block, nr_blocks);
304 if (ret)
305 return ret;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900306
307 /*
308 * Validate the blocks in the buffer zone
309 * and invalidate in the data zone.
310 */
311 ret = dmz_validate_blocks(zmd, bzone, chunk_block, nr_blocks);
312 if (ret == 0 && chunk_block < zone->wp_block)
313 ret = dmz_invalidate_blocks(zmd, zone, chunk_block, nr_blocks);
314
315 return ret;
316}
317
318/*
319 * Process a write BIO.
320 */
321static int dmz_handle_write(struct dmz_target *dmz, struct dm_zone *zone,
322 struct bio *bio)
323{
Hannes Reinecke36820562020-05-11 10:24:21 +0200324 struct dmz_metadata *zmd = dmz->metadata;
325 sector_t chunk_block = dmz_chunk_block(zmd, dmz_bio_block(bio));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900326 unsigned int nr_blocks = dmz_bio_blocks(bio);
327
328 if (!zone)
329 return -ENOSPC;
330
Hannes Reinecke2234e732020-05-11 10:24:22 +0200331 DMDEBUG("(%s): WRITE chunk %llu -> %s zone %u, block %llu, %u blocks",
332 dmz_metadata_label(zmd),
333 (unsigned long long)dmz_bio_chunk(zmd, bio),
334 (dmz_is_rnd(zone) ? "RND" : "SEQ"),
335 zone->id,
336 (unsigned long long)chunk_block, nr_blocks);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900337
338 if (dmz_is_rnd(zone) || chunk_block == zone->wp_block) {
339 /*
340 * zone is a random zone or it is a sequential zone
341 * and the BIO is aligned to the zone write pointer:
342 * direct write the zone.
343 */
Hannes Reinecke52d67752020-05-11 10:24:25 +0200344 return dmz_handle_direct_write(dmz, zone, bio,
345 chunk_block, nr_blocks);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900346 }
347
348 /*
349 * This is an unaligned write in a sequential zone:
350 * use buffered write.
351 */
352 return dmz_handle_buffered_write(dmz, zone, bio, chunk_block, nr_blocks);
353}
354
355/*
356 * Process a discard BIO.
357 */
358static int dmz_handle_discard(struct dmz_target *dmz, struct dm_zone *zone,
359 struct bio *bio)
360{
361 struct dmz_metadata *zmd = dmz->metadata;
362 sector_t block = dmz_bio_block(bio);
363 unsigned int nr_blocks = dmz_bio_blocks(bio);
Hannes Reinecke36820562020-05-11 10:24:21 +0200364 sector_t chunk_block = dmz_chunk_block(zmd, block);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900365 int ret = 0;
366
367 /* For unmapped chunks, there is nothing to do */
368 if (!zone)
369 return 0;
370
371 if (dmz_is_readonly(zone))
372 return -EROFS;
373
Hannes Reinecke2234e732020-05-11 10:24:22 +0200374 DMDEBUG("(%s): DISCARD chunk %llu -> zone %u, block %llu, %u blocks",
375 dmz_metadata_label(dmz->metadata),
376 (unsigned long long)dmz_bio_chunk(zmd, bio),
377 zone->id,
378 (unsigned long long)chunk_block, nr_blocks);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900379
380 /*
381 * Invalidate blocks in the data zone and its
382 * buffer zone if one is mapped.
383 */
384 if (dmz_is_rnd(zone) || chunk_block < zone->wp_block)
385 ret = dmz_invalidate_blocks(zmd, zone, chunk_block, nr_blocks);
386 if (ret == 0 && zone->bzone)
387 ret = dmz_invalidate_blocks(zmd, zone->bzone,
388 chunk_block, nr_blocks);
389 return ret;
390}
391
392/*
393 * Process a BIO.
394 */
395static void dmz_handle_bio(struct dmz_target *dmz, struct dm_chunk_work *cw,
396 struct bio *bio)
397{
Hannes Reinecke52d67752020-05-11 10:24:25 +0200398 struct dmz_bioctx *bioctx =
399 dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900400 struct dmz_metadata *zmd = dmz->metadata;
401 struct dm_zone *zone;
402 int ret;
403
404 /*
405 * Write may trigger a zone allocation. So make sure the
406 * allocation can succeed.
407 */
408 if (bio_op(bio) == REQ_OP_WRITE)
409 dmz_schedule_reclaim(dmz->reclaim);
410
411 dmz_lock_metadata(zmd);
412
413 /*
414 * Get the data zone mapping the chunk. There may be no
415 * mapping for read and discard. If a mapping is obtained,
416 + the zone returned will be set to active state.
417 */
Hannes Reinecke36820562020-05-11 10:24:21 +0200418 zone = dmz_get_chunk_mapping(zmd, dmz_bio_chunk(zmd, bio),
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900419 bio_op(bio));
420 if (IS_ERR(zone)) {
421 ret = PTR_ERR(zone);
422 goto out;
423 }
424
425 /* Process the BIO */
426 if (zone) {
427 dmz_activate_zone(zone);
428 bioctx->zone = zone;
429 }
430
431 switch (bio_op(bio)) {
432 case REQ_OP_READ:
433 ret = dmz_handle_read(dmz, zone, bio);
434 break;
435 case REQ_OP_WRITE:
436 ret = dmz_handle_write(dmz, zone, bio);
437 break;
438 case REQ_OP_DISCARD:
439 case REQ_OP_WRITE_ZEROES:
440 ret = dmz_handle_discard(dmz, zone, bio);
441 break;
442 default:
Hannes Reinecke2234e732020-05-11 10:24:22 +0200443 DMERR("(%s): Unsupported BIO operation 0x%x",
444 dmz_metadata_label(dmz->metadata), bio_op(bio));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900445 ret = -EIO;
446 }
447
448 /*
449 * Release the chunk mapping. This will check that the mapping
450 * is still valid, that is, that the zone used still has valid blocks.
451 */
452 if (zone)
453 dmz_put_chunk_mapping(zmd, zone);
454out:
455 dmz_bio_endio(bio, errno_to_blk_status(ret));
456
457 dmz_unlock_metadata(zmd);
458}
459
460/*
461 * Increment a chunk reference counter.
462 */
463static inline void dmz_get_chunk_work(struct dm_chunk_work *cw)
464{
John Pittman092b56482018-08-23 13:35:57 -0400465 refcount_inc(&cw->refcount);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900466}
467
468/*
469 * Decrement a chunk work reference count and
470 * free it if it becomes 0.
471 */
472static void dmz_put_chunk_work(struct dm_chunk_work *cw)
473{
John Pittman092b56482018-08-23 13:35:57 -0400474 if (refcount_dec_and_test(&cw->refcount)) {
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900475 WARN_ON(!bio_list_empty(&cw->bio_list));
476 radix_tree_delete(&cw->target->chunk_rxtree, cw->chunk);
477 kfree(cw);
478 }
479}
480
481/*
482 * Chunk BIO work function.
483 */
484static void dmz_chunk_work(struct work_struct *work)
485{
486 struct dm_chunk_work *cw = container_of(work, struct dm_chunk_work, work);
487 struct dmz_target *dmz = cw->target;
488 struct bio *bio;
489
490 mutex_lock(&dmz->chunk_lock);
491
492 /* Process the chunk BIOs */
493 while ((bio = bio_list_pop(&cw->bio_list))) {
494 mutex_unlock(&dmz->chunk_lock);
495 dmz_handle_bio(dmz, cw, bio);
496 mutex_lock(&dmz->chunk_lock);
497 dmz_put_chunk_work(cw);
498 }
499
500 /* Queueing the work incremented the work refcount */
501 dmz_put_chunk_work(cw);
502
503 mutex_unlock(&dmz->chunk_lock);
504}
505
506/*
507 * Flush work.
508 */
509static void dmz_flush_work(struct work_struct *work)
510{
511 struct dmz_target *dmz = container_of(work, struct dmz_target, flush_work.work);
512 struct bio *bio;
513 int ret;
514
515 /* Flush dirty metadata blocks */
516 ret = dmz_flush_metadata(dmz->metadata);
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -0700517 if (ret)
Hannes Reinecke2234e732020-05-11 10:24:22 +0200518 DMDEBUG("(%s): Metadata flush failed, rc=%d\n",
519 dmz_metadata_label(dmz->metadata), ret);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900520
521 /* Process queued flush requests */
522 while (1) {
523 spin_lock(&dmz->flush_lock);
524 bio = bio_list_pop(&dmz->flush_list);
525 spin_unlock(&dmz->flush_lock);
526
527 if (!bio)
528 break;
529
530 dmz_bio_endio(bio, errno_to_blk_status(ret));
531 }
532
533 queue_delayed_work(dmz->flush_wq, &dmz->flush_work, DMZ_FLUSH_PERIOD);
534}
535
536/*
537 * Get a chunk work and start it to process a new BIO.
538 * If the BIO chunk has no work yet, create one.
539 */
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700540static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio)
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900541{
Hannes Reinecke36820562020-05-11 10:24:21 +0200542 unsigned int chunk = dmz_bio_chunk(dmz->metadata, bio);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900543 struct dm_chunk_work *cw;
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700544 int ret = 0;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900545
546 mutex_lock(&dmz->chunk_lock);
547
548 /* Get the BIO chunk work. If one is not active yet, create one */
549 cw = radix_tree_lookup(&dmz->chunk_rxtree, chunk);
Shin'ichiro Kawasakiee63634b2020-02-27 09:18:52 +0900550 if (cw) {
551 dmz_get_chunk_work(cw);
552 } else {
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900553 /* Create a new chunk work */
Damien Le Moal4218a952017-07-24 16:44:37 +0900554 cw = kmalloc(sizeof(struct dm_chunk_work), GFP_NOIO);
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700555 if (unlikely(!cw)) {
556 ret = -ENOMEM;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900557 goto out;
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700558 }
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900559
560 INIT_WORK(&cw->work, dmz_chunk_work);
Shin'ichiro Kawasakiee63634b2020-02-27 09:18:52 +0900561 refcount_set(&cw->refcount, 1);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900562 cw->target = dmz;
563 cw->chunk = chunk;
564 bio_list_init(&cw->bio_list);
565
566 ret = radix_tree_insert(&dmz->chunk_rxtree, chunk, cw);
567 if (unlikely(ret)) {
568 kfree(cw);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900569 goto out;
570 }
571 }
572
573 bio_list_add(&cw->bio_list, bio);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900574
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700575 dmz_reclaim_bio_acc(dmz->reclaim);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900576 if (queue_work(dmz->chunk_wq, &cw->work))
577 dmz_get_chunk_work(cw);
578out:
579 mutex_unlock(&dmz->chunk_lock);
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700580 return ret;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900581}
582
583/*
Dmitry Fomicheve7fad902019-11-06 14:34:35 -0800584 * Check if the backing device is being removed. If it's on the way out,
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -0700585 * start failing I/O. Reclaim and metadata components also call this
586 * function to cleanly abort operation in the event of such failure.
587 */
588bool dmz_bdev_is_dying(struct dmz_dev *dmz_dev)
589{
Dmitry Fomicheve7fad902019-11-06 14:34:35 -0800590 if (dmz_dev->flags & DMZ_BDEV_DYING)
591 return true;
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -0700592
Dmitry Fomicheve7fad902019-11-06 14:34:35 -0800593 if (dmz_dev->flags & DMZ_CHECK_BDEV)
594 return !dmz_check_bdev(dmz_dev);
595
596 if (blk_queue_dying(bdev_get_queue(dmz_dev->bdev))) {
597 dmz_dev_warn(dmz_dev, "Backing device queue dying");
598 dmz_dev->flags |= DMZ_BDEV_DYING;
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -0700599 }
600
601 return dmz_dev->flags & DMZ_BDEV_DYING;
602}
603
604/*
Dmitry Fomicheve7fad902019-11-06 14:34:35 -0800605 * Check the backing device availability. This detects such events as
606 * backing device going offline due to errors, media removals, etc.
607 * This check is less efficient than dmz_bdev_is_dying() and should
608 * only be performed as a part of error handling.
609 */
610bool dmz_check_bdev(struct dmz_dev *dmz_dev)
611{
612 struct gendisk *disk;
613
614 dmz_dev->flags &= ~DMZ_CHECK_BDEV;
615
616 if (dmz_bdev_is_dying(dmz_dev))
617 return false;
618
619 disk = dmz_dev->bdev->bd_disk;
620 if (disk->fops->check_events &&
621 disk->fops->check_events(disk, 0) & DISK_EVENT_MEDIA_CHANGE) {
622 dmz_dev_warn(dmz_dev, "Backing device offline");
623 dmz_dev->flags |= DMZ_BDEV_DYING;
624 }
625
626 return !(dmz_dev->flags & DMZ_BDEV_DYING);
627}
628
629/*
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900630 * Process a new BIO.
631 */
632static int dmz_map(struct dm_target *ti, struct bio *bio)
633{
634 struct dmz_target *dmz = ti->private;
Hannes Reinecke36820562020-05-11 10:24:21 +0200635 struct dmz_metadata *zmd = dmz->metadata;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900636 struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
637 sector_t sector = bio->bi_iter.bi_sector;
638 unsigned int nr_sectors = bio_sectors(bio);
639 sector_t chunk_sector;
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700640 int ret;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900641
Hannes Reinecked0e21ce2020-05-11 10:24:23 +0200642 if (dmz_dev_is_dying(zmd))
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -0700643 return DM_MAPIO_KILL;
644
Hannes Reinecke2234e732020-05-11 10:24:22 +0200645 DMDEBUG("(%s): BIO op %d sector %llu + %u => chunk %llu, block %llu, %u blocks",
646 dmz_metadata_label(zmd),
647 bio_op(bio), (unsigned long long)sector, nr_sectors,
648 (unsigned long long)dmz_bio_chunk(zmd, bio),
649 (unsigned long long)dmz_chunk_block(zmd, dmz_bio_block(bio)),
650 (unsigned int)dmz_bio_blocks(bio));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900651
Mikulas Patockaedbe9592017-07-21 11:56:46 -0400652 if (!nr_sectors && bio_op(bio) != REQ_OP_WRITE)
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900653 return DM_MAPIO_REMAPPED;
654
655 /* The BIO should be block aligned */
656 if ((nr_sectors & DMZ_BLOCK_SECTORS_MASK) || (sector & DMZ_BLOCK_SECTORS_MASK))
657 return DM_MAPIO_KILL;
658
659 /* Initialize the BIO context */
Hannes Reinecke52d67752020-05-11 10:24:25 +0200660 bioctx->dev = NULL;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900661 bioctx->zone = NULL;
662 bioctx->bio = bio;
John Pittman092b56482018-08-23 13:35:57 -0400663 refcount_set(&bioctx->ref, 1);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900664
665 /* Set the BIO pending in the flush list */
Mikulas Patockaedbe9592017-07-21 11:56:46 -0400666 if (!nr_sectors && bio_op(bio) == REQ_OP_WRITE) {
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900667 spin_lock(&dmz->flush_lock);
668 bio_list_add(&dmz->flush_list, bio);
669 spin_unlock(&dmz->flush_lock);
670 mod_delayed_work(dmz->flush_wq, &dmz->flush_work, 0);
671 return DM_MAPIO_SUBMITTED;
672 }
673
674 /* Split zone BIOs to fit entirely into a zone */
Hannes Reinecke36820562020-05-11 10:24:21 +0200675 chunk_sector = sector & (dmz_zone_nr_sectors(zmd) - 1);
676 if (chunk_sector + nr_sectors > dmz_zone_nr_sectors(zmd))
677 dm_accept_partial_bio(bio, dmz_zone_nr_sectors(zmd) - chunk_sector);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900678
679 /* Now ready to handle this BIO */
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700680 ret = dmz_queue_chunk_work(dmz, bio);
681 if (ret) {
Hannes Reinecke2234e732020-05-11 10:24:22 +0200682 DMDEBUG("(%s): BIO op %d, can't process chunk %llu, err %i\n",
683 dmz_metadata_label(zmd),
684 bio_op(bio), (u64)dmz_bio_chunk(zmd, bio),
685 ret);
Dmitry Fomichevd7428c52019-08-10 14:43:10 -0700686 return DM_MAPIO_REQUEUE;
687 }
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900688
689 return DM_MAPIO_SUBMITTED;
690}
691
692/*
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900693 * Get zoned device information.
694 */
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200695static int dmz_get_zoned_device(struct dm_target *ti, char *path,
696 int idx, int nr_devs)
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900697{
698 struct dmz_target *dmz = ti->private;
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200699 struct dm_dev *ddev;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900700 struct dmz_dev *dev;
701 int ret;
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200702 struct block_device *bdev;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900703
704 /* Get the target device */
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200705 ret = dm_get_device(ti, path, dm_table_get_mode(ti->table), &ddev);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900706 if (ret) {
707 ti->error = "Get target device failed";
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900708 return ret;
709 }
710
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200711 bdev = ddev->bdev;
712 if (bdev_zoned_model(bdev) == BLK_ZONED_NONE) {
713 if (nr_devs == 1) {
714 ti->error = "Invalid regular device";
715 goto err;
716 }
717 if (idx != 0) {
718 ti->error = "First device must be a regular device";
719 goto err;
720 }
721 if (dmz->ddev[0]) {
722 ti->error = "Too many regular devices";
723 goto err;
724 }
725 dev = &dmz->dev[idx];
726 dev->flags = DMZ_BDEV_REGULAR;
727 } else {
728 if (dmz->ddev[idx]) {
729 ti->error = "Too many zoned devices";
730 goto err;
731 }
732 if (nr_devs > 1 && idx == 0) {
733 ti->error = "First device must be a regular device";
734 goto err;
735 }
736 dev = &dmz->dev[idx];
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900737 }
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200738 dev->bdev = bdev;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900739 (void)bdevname(dev->bdev, dev->name);
740
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200741 dev->capacity = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
742 if (ti->begin) {
743 ti->error = "Partial mapping is not supported";
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900744 goto err;
745 }
746
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200747 dmz->ddev[idx] = ddev;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900748
749 return 0;
750err:
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200751 dm_put_device(ti, ddev);
752 return -EINVAL;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900753}
754
755/*
756 * Cleanup zoned device information.
757 */
758static void dmz_put_zoned_device(struct dm_target *ti)
759{
760 struct dmz_target *dmz = ti->private;
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200761 int i;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900762
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200763 for (i = 0; i < DMZ_MAX_DEVS; i++) {
764 if (dmz->ddev[i]) {
765 dm_put_device(ti, dmz->ddev[i]);
766 dmz->ddev[i] = NULL;
767 }
768 }
769}
770
771static int dmz_fixup_devices(struct dm_target *ti)
772{
773 struct dmz_target *dmz = ti->private;
774 struct dmz_dev *reg_dev, *zoned_dev;
775 struct request_queue *q;
776
777 /*
778 * When we have two devices, the first one must be a regular block
779 * device and the second a zoned block device.
780 */
781 if (dmz->ddev[0] && dmz->ddev[1]) {
782 reg_dev = &dmz->dev[0];
783 if (!(reg_dev->flags & DMZ_BDEV_REGULAR)) {
784 ti->error = "Primary disk is not a regular device";
785 return -EINVAL;
786 }
787 zoned_dev = &dmz->dev[1];
788 if (zoned_dev->flags & DMZ_BDEV_REGULAR) {
789 ti->error = "Secondary disk is not a zoned device";
790 return -EINVAL;
791 }
792 } else {
793 reg_dev = NULL;
794 zoned_dev = &dmz->dev[0];
795 if (zoned_dev->flags & DMZ_BDEV_REGULAR) {
796 ti->error = "Disk is not a zoned device";
797 return -EINVAL;
798 }
799 }
800 q = bdev_get_queue(zoned_dev->bdev);
801 zoned_dev->zone_nr_sectors = blk_queue_zone_sectors(q);
802 zoned_dev->nr_zones = blkdev_nr_zones(zoned_dev->bdev->bd_disk);
803
804 if (reg_dev) {
805 reg_dev->zone_nr_sectors = zoned_dev->zone_nr_sectors;
806 reg_dev->nr_zones = DIV_ROUND_UP(reg_dev->capacity,
807 reg_dev->zone_nr_sectors);
808 zoned_dev->zone_offset = reg_dev->nr_zones;
809 }
810 return 0;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900811}
812
813/*
814 * Setup target.
815 */
816static int dmz_ctr(struct dm_target *ti, unsigned int argc, char **argv)
817{
818 struct dmz_target *dmz;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900819 int ret;
820
821 /* Check arguments */
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200822 if (argc < 1 || argc > 2) {
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900823 ti->error = "Invalid argument count";
824 return -EINVAL;
825 }
826
827 /* Allocate and initialize the target descriptor */
828 dmz = kzalloc(sizeof(struct dmz_target), GFP_KERNEL);
829 if (!dmz) {
830 ti->error = "Unable to allocate the zoned target descriptor";
831 return -ENOMEM;
832 }
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200833 dmz->dev = kcalloc(2, sizeof(struct dmz_dev), GFP_KERNEL);
834 if (!dmz->dev) {
835 ti->error = "Unable to allocate the zoned device descriptors";
836 kfree(dmz);
837 return -ENOMEM;
838 }
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900839 ti->private = dmz;
840
841 /* Get the target zoned block device */
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200842 ret = dmz_get_zoned_device(ti, argv[0], 0, argc);
843 if (ret)
844 goto err;
845
846 if (argc == 2) {
847 ret = dmz_get_zoned_device(ti, argv[1], 1, argc);
848 if (ret) {
849 dmz_put_zoned_device(ti);
850 goto err;
851 }
852 }
853 ret = dmz_fixup_devices(ti);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900854 if (ret) {
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200855 dmz_put_zoned_device(ti);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900856 goto err;
857 }
858
859 /* Initialize metadata */
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200860 ret = dmz_ctr_metadata(dmz->dev, argc, &dmz->metadata,
Hannes Reinecke2234e732020-05-11 10:24:22 +0200861 dm_table_device_name(ti->table));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900862 if (ret) {
863 ti->error = "Metadata initialization failed";
864 goto err_dev;
865 }
866
867 /* Set target (no write same support) */
Hannes Reinecke36820562020-05-11 10:24:21 +0200868 ti->max_io_len = dmz_zone_nr_sectors(dmz->metadata) << 9;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900869 ti->num_flush_bios = 1;
870 ti->num_discard_bios = 1;
871 ti->num_write_zeroes_bios = 1;
872 ti->per_io_data_size = sizeof(struct dmz_bioctx);
873 ti->flush_supported = true;
874 ti->discards_supported = true;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900875
876 /* The exposed capacity is the number of chunks that can be mapped */
Hannes Reinecke36820562020-05-11 10:24:21 +0200877 ti->len = (sector_t)dmz_nr_chunks(dmz->metadata) <<
878 dmz_zone_nr_sectors_shift(dmz->metadata);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900879
880 /* Zone BIO */
Kent Overstreet6f1c8192018-05-20 18:25:53 -0400881 ret = bioset_init(&dmz->bio_set, DMZ_MIN_BIOS, 0, 0);
882 if (ret) {
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900883 ti->error = "Create BIO set failed";
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900884 goto err_meta;
885 }
886
887 /* Chunk BIO work */
888 mutex_init(&dmz->chunk_lock);
Bart Van Assche2d0b2d62018-06-22 08:09:11 -0700889 INIT_RADIX_TREE(&dmz->chunk_rxtree, GFP_NOIO);
Hannes Reinecke2234e732020-05-11 10:24:22 +0200890 dmz->chunk_wq = alloc_workqueue("dmz_cwq_%s",
891 WQ_MEM_RECLAIM | WQ_UNBOUND, 0,
892 dmz_metadata_label(dmz->metadata));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900893 if (!dmz->chunk_wq) {
894 ti->error = "Create chunk workqueue failed";
895 ret = -ENOMEM;
896 goto err_bio;
897 }
898
899 /* Flush work */
900 spin_lock_init(&dmz->flush_lock);
901 bio_list_init(&dmz->flush_list);
902 INIT_DELAYED_WORK(&dmz->flush_work, dmz_flush_work);
903 dmz->flush_wq = alloc_ordered_workqueue("dmz_fwq_%s", WQ_MEM_RECLAIM,
Hannes Reinecke2234e732020-05-11 10:24:22 +0200904 dmz_metadata_label(dmz->metadata));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900905 if (!dmz->flush_wq) {
906 ti->error = "Create flush workqueue failed";
907 ret = -ENOMEM;
908 goto err_cwq;
909 }
910 mod_delayed_work(dmz->flush_wq, &dmz->flush_work, DMZ_FLUSH_PERIOD);
911
912 /* Initialize reclaim */
Hannes Reinecke6c805f72020-05-11 10:24:24 +0200913 ret = dmz_ctr_reclaim(dmz->metadata, &dmz->reclaim);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900914 if (ret) {
915 ti->error = "Zone reclaim initialization failed";
916 goto err_fwq;
917 }
918
Hannes Reinecke2234e732020-05-11 10:24:22 +0200919 DMINFO("(%s): Target device: %llu 512-byte logical sectors (%llu blocks)",
920 dmz_metadata_label(dmz->metadata),
921 (unsigned long long)ti->len,
922 (unsigned long long)dmz_sect2blk(ti->len));
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900923
924 return 0;
925err_fwq:
926 destroy_workqueue(dmz->flush_wq);
927err_cwq:
928 destroy_workqueue(dmz->chunk_wq);
929err_bio:
Mike Snitzerd5ffebd2018-01-05 21:17:20 -0500930 mutex_destroy(&dmz->chunk_lock);
Kent Overstreet6f1c8192018-05-20 18:25:53 -0400931 bioset_exit(&dmz->bio_set);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900932err_meta:
933 dmz_dtr_metadata(dmz->metadata);
934err_dev:
935 dmz_put_zoned_device(ti);
936err:
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200937 kfree(dmz->dev);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900938 kfree(dmz);
939
940 return ret;
941}
942
943/*
944 * Cleanup target.
945 */
946static void dmz_dtr(struct dm_target *ti)
947{
948 struct dmz_target *dmz = ti->private;
949
950 flush_workqueue(dmz->chunk_wq);
951 destroy_workqueue(dmz->chunk_wq);
952
953 dmz_dtr_reclaim(dmz->reclaim);
954
955 cancel_delayed_work_sync(&dmz->flush_work);
956 destroy_workqueue(dmz->flush_wq);
957
958 (void) dmz_flush_metadata(dmz->metadata);
959
960 dmz_dtr_metadata(dmz->metadata);
961
Kent Overstreet6f1c8192018-05-20 18:25:53 -0400962 bioset_exit(&dmz->bio_set);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900963
964 dmz_put_zoned_device(ti);
965
Mike Snitzerd5ffebd2018-01-05 21:17:20 -0500966 mutex_destroy(&dmz->chunk_lock);
967
Hannes Reineckebd5c4032020-05-11 10:24:30 +0200968 kfree(dmz->dev);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900969 kfree(dmz);
970}
971
972/*
973 * Setup target request queue limits.
974 */
975static void dmz_io_hints(struct dm_target *ti, struct queue_limits *limits)
976{
977 struct dmz_target *dmz = ti->private;
Hannes Reinecke36820562020-05-11 10:24:21 +0200978 unsigned int chunk_sectors = dmz_zone_nr_sectors(dmz->metadata);
Damien Le Moal3b1a94c2017-06-07 15:55:39 +0900979
980 limits->logical_block_size = DMZ_BLOCK_SIZE;
981 limits->physical_block_size = DMZ_BLOCK_SIZE;
982
983 blk_limits_io_min(limits, DMZ_BLOCK_SIZE);
984 blk_limits_io_opt(limits, DMZ_BLOCK_SIZE);
985
986 limits->discard_alignment = DMZ_BLOCK_SIZE;
987 limits->discard_granularity = DMZ_BLOCK_SIZE;
988 limits->max_discard_sectors = chunk_sectors;
989 limits->max_hw_discard_sectors = chunk_sectors;
990 limits->max_write_zeroes_sectors = chunk_sectors;
991
992 /* FS hint to try to align to the device zone size */
993 limits->chunk_sectors = chunk_sectors;
994 limits->max_sectors = chunk_sectors;
995
996 /* We are exposing a drive-managed zoned block device */
997 limits->zoned = BLK_ZONED_NONE;
998}
999
1000/*
1001 * Pass on ioctl to the backend device.
1002 */
Mike Snitzer5bd5e8d2018-04-03 16:54:10 -04001003static int dmz_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001004{
1005 struct dmz_target *dmz = ti->private;
Hannes Reinecke52d67752020-05-11 10:24:25 +02001006 struct dmz_dev *dev = &dmz->dev[0];
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001007
Hannes Reinecke52d67752020-05-11 10:24:25 +02001008 if (!dmz_check_bdev(dev))
Dmitry Fomicheve7fad902019-11-06 14:34:35 -08001009 return -EIO;
Dmitry Fomichev75d66ff2019-08-10 14:43:11 -07001010
Hannes Reinecke52d67752020-05-11 10:24:25 +02001011 *bdev = dev->bdev;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001012
1013 return 0;
1014}
1015
1016/*
1017 * Stop works on suspend.
1018 */
1019static void dmz_suspend(struct dm_target *ti)
1020{
1021 struct dmz_target *dmz = ti->private;
1022
1023 flush_workqueue(dmz->chunk_wq);
1024 dmz_suspend_reclaim(dmz->reclaim);
1025 cancel_delayed_work_sync(&dmz->flush_work);
1026}
1027
1028/*
1029 * Restart works on resume or if suspend failed.
1030 */
1031static void dmz_resume(struct dm_target *ti)
1032{
1033 struct dmz_target *dmz = ti->private;
1034
1035 queue_delayed_work(dmz->flush_wq, &dmz->flush_work, DMZ_FLUSH_PERIOD);
1036 dmz_resume_reclaim(dmz->reclaim);
1037}
1038
1039static int dmz_iterate_devices(struct dm_target *ti,
1040 iterate_devices_callout_fn fn, void *data)
1041{
1042 struct dmz_target *dmz = ti->private;
Hannes Reineckebd5c4032020-05-11 10:24:30 +02001043 unsigned int zone_nr_sectors = dmz_zone_nr_sectors(dmz->metadata);
1044 sector_t capacity;
1045 int r;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001046
Hannes Reineckebd5c4032020-05-11 10:24:30 +02001047 capacity = dmz->dev[0].capacity & ~(zone_nr_sectors - 1);
1048 r = fn(ti, dmz->ddev[0], 0, capacity, data);
1049 if (!r && dmz->ddev[1]) {
1050 capacity = dmz->dev[1].capacity & ~(zone_nr_sectors - 1);
1051 r = fn(ti, dmz->ddev[1], 0, capacity, data);
1052 }
1053 return r;
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001054}
1055
Hannes Reineckebc3d5712020-05-11 10:24:16 +02001056static void dmz_status(struct dm_target *ti, status_type_t type,
1057 unsigned int status_flags, char *result,
1058 unsigned int maxlen)
1059{
1060 struct dmz_target *dmz = ti->private;
1061 ssize_t sz = 0;
1062 char buf[BDEVNAME_SIZE];
Hannes Reineckebd5c4032020-05-11 10:24:30 +02001063 struct dmz_dev *dev;
Hannes Reineckebc3d5712020-05-11 10:24:16 +02001064
1065 switch (type) {
1066 case STATUSTYPE_INFO:
1067 DMEMIT("%u zones %u/%u random %u/%u sequential",
1068 dmz_nr_zones(dmz->metadata),
1069 dmz_nr_unmap_rnd_zones(dmz->metadata),
1070 dmz_nr_rnd_zones(dmz->metadata),
1071 dmz_nr_unmap_seq_zones(dmz->metadata),
1072 dmz_nr_seq_zones(dmz->metadata));
1073 break;
1074 case STATUSTYPE_TABLE:
Hannes Reineckebd5c4032020-05-11 10:24:30 +02001075 dev = &dmz->dev[0];
1076 format_dev_t(buf, dev->bdev->bd_dev);
Hannes Reineckebc3d5712020-05-11 10:24:16 +02001077 DMEMIT("%s", buf);
Hannes Reineckebd5c4032020-05-11 10:24:30 +02001078 if (dmz->dev[1].bdev) {
1079 dev = &dmz->dev[1];
1080 format_dev_t(buf, dev->bdev->bd_dev);
1081 DMEMIT(" %s", buf);
1082 }
Hannes Reineckebc3d5712020-05-11 10:24:16 +02001083 break;
1084 }
1085 return;
1086}
1087
Hannes Reinecke90b39d52020-05-11 10:24:17 +02001088static int dmz_message(struct dm_target *ti, unsigned int argc, char **argv,
1089 char *result, unsigned int maxlen)
1090{
1091 struct dmz_target *dmz = ti->private;
1092 int r = -EINVAL;
1093
1094 if (!strcasecmp(argv[0], "reclaim")) {
1095 dmz_schedule_reclaim(dmz->reclaim);
1096 r = 0;
1097 } else
1098 DMERR("unrecognized message %s", argv[0]);
1099 return r;
1100}
1101
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001102static struct target_type dmz_type = {
1103 .name = "zoned",
Hannes Reineckebd5c4032020-05-11 10:24:30 +02001104 .version = {2, 0, 0},
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001105 .features = DM_TARGET_SINGLETON | DM_TARGET_ZONED_HM,
1106 .module = THIS_MODULE,
1107 .ctr = dmz_ctr,
1108 .dtr = dmz_dtr,
1109 .map = dmz_map,
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001110 .io_hints = dmz_io_hints,
1111 .prepare_ioctl = dmz_prepare_ioctl,
1112 .postsuspend = dmz_suspend,
1113 .resume = dmz_resume,
1114 .iterate_devices = dmz_iterate_devices,
Hannes Reineckebc3d5712020-05-11 10:24:16 +02001115 .status = dmz_status,
Hannes Reinecke90b39d52020-05-11 10:24:17 +02001116 .message = dmz_message,
Damien Le Moal3b1a94c2017-06-07 15:55:39 +09001117};
1118
1119static int __init dmz_init(void)
1120{
1121 return dm_register_target(&dmz_type);
1122}
1123
1124static void __exit dmz_exit(void)
1125{
1126 dm_unregister_target(&dmz_type);
1127}
1128
1129module_init(dmz_init);
1130module_exit(dmz_exit);
1131
1132MODULE_DESCRIPTION(DM_NAME " target for zoned block devices");
1133MODULE_AUTHOR("Damien Le Moal <damien.lemoal@wdc.com>");
1134MODULE_LICENSE("GPL");