17fc18728SDamien Le Moal // SPDX-License-Identifier: GPL-2.0 27fc18728SDamien Le Moal /* 37fc18728SDamien Le Moal * Copyright (C) 2021 Western Digital Corporation or its affiliates. 47fc18728SDamien Le Moal */ 57fc18728SDamien Le Moal 67fc18728SDamien Le Moal #include <linux/blkdev.h> 77fc18728SDamien Le Moal 87fc18728SDamien Le Moal #include "dm-core.h" 97fc18728SDamien Le Moal 107fc18728SDamien Le Moal /* 117fc18728SDamien Le Moal * User facing dm device block device report zone operation. This calls the 127fc18728SDamien Le Moal * report_zones operation for each target of a device table. This operation is 137fc18728SDamien Le Moal * generally implemented by targets using dm_report_zones(). 147fc18728SDamien Le Moal */ 157fc18728SDamien Le Moal int dm_blk_report_zones(struct gendisk *disk, sector_t sector, 167fc18728SDamien Le Moal unsigned int nr_zones, report_zones_cb cb, void *data) 177fc18728SDamien Le Moal { 187fc18728SDamien Le Moal struct mapped_device *md = disk->private_data; 197fc18728SDamien Le Moal struct dm_table *map; 207fc18728SDamien Le Moal int srcu_idx, ret; 217fc18728SDamien Le Moal struct dm_report_zones_args args = { 227fc18728SDamien Le Moal .next_sector = sector, 237fc18728SDamien Le Moal .orig_data = data, 247fc18728SDamien Le Moal .orig_cb = cb, 257fc18728SDamien Le Moal }; 267fc18728SDamien Le Moal 277fc18728SDamien Le Moal if (dm_suspended_md(md)) 287fc18728SDamien Le Moal return -EAGAIN; 297fc18728SDamien Le Moal 307fc18728SDamien Le Moal map = dm_get_live_table(md, &srcu_idx); 317fc18728SDamien Le Moal if (!map) { 327fc18728SDamien Le Moal ret = -EIO; 337fc18728SDamien Le Moal goto out; 347fc18728SDamien Le Moal } 357fc18728SDamien Le Moal 367fc18728SDamien Le Moal do { 377fc18728SDamien Le Moal struct dm_target *tgt; 387fc18728SDamien Le Moal 397fc18728SDamien Le Moal tgt = dm_table_find_target(map, args.next_sector); 407fc18728SDamien Le Moal if (WARN_ON_ONCE(!tgt->type->report_zones)) { 417fc18728SDamien Le Moal ret = -EIO; 427fc18728SDamien Le Moal goto out; 437fc18728SDamien Le Moal } 447fc18728SDamien Le Moal 457fc18728SDamien Le Moal args.tgt = tgt; 467fc18728SDamien Le Moal ret = tgt->type->report_zones(tgt, &args, 477fc18728SDamien Le Moal nr_zones - args.zone_idx); 487fc18728SDamien Le Moal if (ret < 0) 497fc18728SDamien Le Moal goto out; 507fc18728SDamien Le Moal } while (args.zone_idx < nr_zones && 517fc18728SDamien Le Moal args.next_sector < get_capacity(disk)); 527fc18728SDamien Le Moal 537fc18728SDamien Le Moal ret = args.zone_idx; 547fc18728SDamien Le Moal out: 557fc18728SDamien Le Moal dm_put_live_table(md, srcu_idx); 567fc18728SDamien Le Moal return ret; 577fc18728SDamien Le Moal } 587fc18728SDamien Le Moal 59912e8875SDamien Le Moal static int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx, 60912e8875SDamien Le Moal void *data) 617fc18728SDamien Le Moal { 627fc18728SDamien Le Moal struct dm_report_zones_args *args = data; 637fc18728SDamien Le Moal sector_t sector_diff = args->tgt->begin - args->start; 647fc18728SDamien Le Moal 657fc18728SDamien Le Moal /* 667fc18728SDamien Le Moal * Ignore zones beyond the target range. 677fc18728SDamien Le Moal */ 687fc18728SDamien Le Moal if (zone->start >= args->start + args->tgt->len) 697fc18728SDamien Le Moal return 0; 707fc18728SDamien Le Moal 717fc18728SDamien Le Moal /* 727fc18728SDamien Le Moal * Remap the start sector and write pointer position of the zone 737fc18728SDamien Le Moal * to match its position in the target range. 747fc18728SDamien Le Moal */ 757fc18728SDamien Le Moal zone->start += sector_diff; 767fc18728SDamien Le Moal if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) { 777fc18728SDamien Le Moal if (zone->cond == BLK_ZONE_COND_FULL) 787fc18728SDamien Le Moal zone->wp = zone->start + zone->len; 797fc18728SDamien Le Moal else if (zone->cond == BLK_ZONE_COND_EMPTY) 807fc18728SDamien Le Moal zone->wp = zone->start; 817fc18728SDamien Le Moal else 827fc18728SDamien Le Moal zone->wp += sector_diff; 837fc18728SDamien Le Moal } 847fc18728SDamien Le Moal 857fc18728SDamien Le Moal args->next_sector = zone->start + zone->len; 867fc18728SDamien Le Moal return args->orig_cb(zone, args->zone_idx++, args->orig_data); 877fc18728SDamien Le Moal } 88912e8875SDamien Le Moal 89912e8875SDamien Le Moal /* 90912e8875SDamien Le Moal * Helper for drivers of zoned targets to implement struct target_type 91912e8875SDamien Le Moal * report_zones operation. 92912e8875SDamien Le Moal */ 93912e8875SDamien Le Moal int dm_report_zones(struct block_device *bdev, sector_t start, sector_t sector, 94912e8875SDamien Le Moal struct dm_report_zones_args *args, unsigned int nr_zones) 95912e8875SDamien Le Moal { 96912e8875SDamien Le Moal /* 97912e8875SDamien Le Moal * Set the target mapping start sector first so that 98912e8875SDamien Le Moal * dm_report_zones_cb() can correctly remap zone information. 99912e8875SDamien Le Moal */ 100912e8875SDamien Le Moal args->start = start; 101912e8875SDamien Le Moal 102912e8875SDamien Le Moal return blkdev_report_zones(bdev, sector, nr_zones, 103912e8875SDamien Le Moal dm_report_zones_cb, args); 104912e8875SDamien Le Moal } 105912e8875SDamien Le Moal EXPORT_SYMBOL_GPL(dm_report_zones); 1067fc18728SDamien Le Moal 107*bf14e2b2SDamien Le Moal bool dm_is_zone_write(struct mapped_device *md, struct bio *bio) 108*bf14e2b2SDamien Le Moal { 109*bf14e2b2SDamien Le Moal struct request_queue *q = md->queue; 110*bf14e2b2SDamien Le Moal 111*bf14e2b2SDamien Le Moal if (!blk_queue_is_zoned(q)) 112*bf14e2b2SDamien Le Moal return false; 113*bf14e2b2SDamien Le Moal 114*bf14e2b2SDamien Le Moal switch (bio_op(bio)) { 115*bf14e2b2SDamien Le Moal case REQ_OP_WRITE_ZEROES: 116*bf14e2b2SDamien Le Moal case REQ_OP_WRITE_SAME: 117*bf14e2b2SDamien Le Moal case REQ_OP_WRITE: 118*bf14e2b2SDamien Le Moal return !op_is_flush(bio->bi_opf) && bio_sectors(bio); 119*bf14e2b2SDamien Le Moal default: 120*bf14e2b2SDamien Le Moal return false; 121*bf14e2b2SDamien Le Moal } 122*bf14e2b2SDamien Le Moal } 123*bf14e2b2SDamien Le Moal 1247fc18728SDamien Le Moal void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q) 1257fc18728SDamien Le Moal { 1267fc18728SDamien Le Moal if (!blk_queue_is_zoned(q)) 1277fc18728SDamien Le Moal return; 1287fc18728SDamien Le Moal 1297fc18728SDamien Le Moal /* 1307fc18728SDamien Le Moal * For a zoned target, the number of zones should be updated for the 1317fc18728SDamien Le Moal * correct value to be exposed in sysfs queue/nr_zones. For a BIO based 1327fc18728SDamien Le Moal * target, this is all that is needed. 1337fc18728SDamien Le Moal */ 1347fc18728SDamien Le Moal WARN_ON_ONCE(queue_is_mq(q)); 1357fc18728SDamien Le Moal q->nr_zones = blkdev_nr_zones(t->md->disk); 1367fc18728SDamien Le Moal } 137