|
@@ -458,6 +458,57 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
|
|
return dm_get_geometry(md, geo);
|
|
|
}
|
|
|
|
|
|
+static int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
|
|
|
+ struct blk_zone *zones, unsigned int *nr_zones,
|
|
|
+ gfp_t gfp_mask)
|
|
|
+{
|
|
|
+#ifdef CONFIG_BLK_DEV_ZONED
|
|
|
+ struct mapped_device *md = disk->private_data;
|
|
|
+ struct dm_target *tgt;
|
|
|
+ struct dm_table *map;
|
|
|
+ int srcu_idx, ret;
|
|
|
+
|
|
|
+ if (dm_suspended_md(md))
|
|
|
+ return -EAGAIN;
|
|
|
+
|
|
|
+ map = dm_get_live_table(md, &srcu_idx);
|
|
|
+ if (!map)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ tgt = dm_table_find_target(map, sector);
|
|
|
+ if (!dm_target_is_valid(tgt)) {
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we are executing this, we already know that the block device
|
|
|
+ * is a zoned device and so each target should have support for that
|
|
|
+ * type of drive. A missing report_zones method means that the target
|
|
|
+ * driver has a problem.
|
|
|
+ */
|
|
|
+ if (WARN_ON(!tgt->type->report_zones)) {
|
|
|
+ ret = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * blkdev_report_zones() will loop and call this again to cover all the
|
|
|
+ * zones of the target, eventually moving on to the next target.
|
|
|
+ * So there is no need to loop here trying to fill the entire array
|
|
|
+ * of zones.
|
|
|
+ */
|
|
|
+ ret = tgt->type->report_zones(tgt, sector, zones,
|
|
|
+ nr_zones, gfp_mask);
|
|
|
+
|
|
|
+out:
|
|
|
+ dm_put_live_table(md, srcu_idx);
|
|
|
+ return ret;
|
|
|
+#else
|
|
|
+ return -ENOTSUPP;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
|
|
|
struct block_device **bdev)
|
|
|
__acquires(md->io_barrier)
|
|
@@ -1155,93 +1206,49 @@ void dm_accept_partial_bio(struct bio *bio, unsigned n_sectors)
|
|
|
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
|
|
|
|
|
|
/*
|
|
|
- * The zone descriptors obtained with a zone report indicate zone positions
|
|
|
- * within the target backing device, regardless of that device is a partition
|
|
|
- * and regardless of the target mapping start sector on the device or partition.
|
|
|
- * The zone descriptors start sector and write pointer position must be adjusted
|
|
|
- * to match their relative position within the dm device.
|
|
|
- * A target may call dm_remap_zone_report() after completion of a
|
|
|
- * REQ_OP_ZONE_REPORT bio to remap the zone descriptors obtained from the
|
|
|
- * backing device.
|
|
|
+ * The zone descriptors obtained with a zone report indicate
|
|
|
+ * zone positions within the underlying device of the target. The zone
|
|
|
+ * descriptors must be remapped to match their position within the dm device.
|
|
|
+ * The caller target should obtain the zones information using
|
|
|
+ * blkdev_report_zones() to ensure that remapping for partition offset is
|
|
|
+ * already handled.
|
|
|
*/
|
|
|
-void dm_remap_zone_report(struct dm_target *ti, struct bio *bio, sector_t start)
|
|
|
+void dm_remap_zone_report(struct dm_target *ti, sector_t start,
|
|
|
+ struct blk_zone *zones, unsigned int *nr_zones)
|
|
|
{
|
|
|
#ifdef CONFIG_BLK_DEV_ZONED
|
|
|
- struct dm_target_io *tio = container_of(bio, struct dm_target_io, clone);
|
|
|
- struct bio *report_bio = tio->io->orig_bio;
|
|
|
- struct blk_zone_report_hdr *hdr = NULL;
|
|
|
struct blk_zone *zone;
|
|
|
- unsigned int nr_rep = 0;
|
|
|
- unsigned int ofst;
|
|
|
- sector_t part_offset;
|
|
|
- struct bio_vec bvec;
|
|
|
- struct bvec_iter iter;
|
|
|
- void *addr;
|
|
|
-
|
|
|
- if (bio->bi_status)
|
|
|
- return;
|
|
|
-
|
|
|
- /*
|
|
|
- * bio sector was incremented by the request size on completion. Taking
|
|
|
- * into account the original request sector, the target start offset on
|
|
|
- * the backing device and the target mapping offset (ti->begin), the
|
|
|
- * start sector of the backing device. The partition offset is always 0
|
|
|
- * if the target uses a whole device.
|
|
|
- */
|
|
|
- part_offset = bio->bi_iter.bi_sector + ti->begin - (start + bio_end_sector(report_bio));
|
|
|
+ unsigned int nrz = *nr_zones;
|
|
|
+ int i;
|
|
|
|
|
|
/*
|
|
|
- * Remap the start sector of the reported zones. For sequential zones,
|
|
|
- * also remap the write pointer position.
|
|
|
+ * Remap the start sector and write pointer position of the zones in
|
|
|
+ * the array. Since we may have obtained from the target underlying
|
|
|
+ * device more zones that the target size, also adjust the number
|
|
|
+ * of zones.
|
|
|
*/
|
|
|
- bio_for_each_segment(bvec, report_bio, iter) {
|
|
|
- addr = kmap_atomic(bvec.bv_page);
|
|
|
-
|
|
|
- /* Remember the report header in the first page */
|
|
|
- if (!hdr) {
|
|
|
- hdr = addr;
|
|
|
- ofst = sizeof(struct blk_zone_report_hdr);
|
|
|
- } else
|
|
|
- ofst = 0;
|
|
|
-
|
|
|
- /* Set zones start sector */
|
|
|
- while (hdr->nr_zones && ofst < bvec.bv_len) {
|
|
|
- zone = addr + ofst;
|
|
|
- zone->start -= part_offset;
|
|
|
- if (zone->start >= start + ti->len) {
|
|
|
- hdr->nr_zones = 0;
|
|
|
- break;
|
|
|
- }
|
|
|
- zone->start = zone->start + ti->begin - start;
|
|
|
- if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL) {
|
|
|
- if (zone->cond == BLK_ZONE_COND_FULL)
|
|
|
- zone->wp = zone->start + zone->len;
|
|
|
- else if (zone->cond == BLK_ZONE_COND_EMPTY)
|
|
|
- zone->wp = zone->start;
|
|
|
- else
|
|
|
- zone->wp = zone->wp + ti->begin - start - part_offset;
|
|
|
- }
|
|
|
- ofst += sizeof(struct blk_zone);
|
|
|
- hdr->nr_zones--;
|
|
|
- nr_rep++;
|
|
|
+ for (i = 0; i < nrz; i++) {
|
|
|
+ zone = zones + i;
|
|
|
+ if (zone->start >= start + ti->len) {
|
|
|
+ memset(zone, 0, sizeof(struct blk_zone) * (nrz - i));
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
- if (addr != hdr)
|
|
|
- kunmap_atomic(addr);
|
|
|
+ zone->start = zone->start + ti->begin - start;
|
|
|
+ if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
|
|
|
+ continue;
|
|
|
|
|
|
- if (!hdr->nr_zones)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (hdr) {
|
|
|
- hdr->nr_zones = nr_rep;
|
|
|
- kunmap_atomic(hdr);
|
|
|
+ if (zone->cond == BLK_ZONE_COND_FULL)
|
|
|
+ zone->wp = zone->start + zone->len;
|
|
|
+ else if (zone->cond == BLK_ZONE_COND_EMPTY)
|
|
|
+ zone->wp = zone->start;
|
|
|
+ else
|
|
|
+ zone->wp = zone->wp + ti->begin - start;
|
|
|
}
|
|
|
|
|
|
- bio_advance(report_bio, report_bio->bi_iter.bi_size);
|
|
|
-
|
|
|
+ *nr_zones = i;
|
|
|
#else /* !CONFIG_BLK_DEV_ZONED */
|
|
|
- bio->bi_status = BLK_STS_NOTSUPP;
|
|
|
+ *nr_zones = 0;
|
|
|
#endif
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(dm_remap_zone_report);
|
|
@@ -1327,8 +1334,7 @@ static int clone_bio(struct dm_target_io *tio, struct bio *bio,
|
|
|
return r;
|
|
|
}
|
|
|
|
|
|
- if (bio_op(bio) != REQ_OP_ZONE_REPORT)
|
|
|
- bio_advance(clone, to_bytes(sector - clone->bi_iter.bi_sector));
|
|
|
+ bio_advance(clone, to_bytes(sector - clone->bi_iter.bi_sector));
|
|
|
clone->bi_iter.bi_size = to_bytes(len);
|
|
|
|
|
|
if (unlikely(bio_integrity(bio) != NULL))
|
|
@@ -1541,7 +1547,6 @@ static bool __process_abnormal_io(struct clone_info *ci, struct dm_target *ti,
|
|
|
*/
|
|
|
static int __split_and_process_non_flush(struct clone_info *ci)
|
|
|
{
|
|
|
- struct bio *bio = ci->bio;
|
|
|
struct dm_target *ti;
|
|
|
unsigned len;
|
|
|
int r;
|
|
@@ -1553,11 +1558,7 @@ static int __split_and_process_non_flush(struct clone_info *ci)
|
|
|
if (unlikely(__process_abnormal_io(ci, ti, &r)))
|
|
|
return r;
|
|
|
|
|
|
- if (bio_op(bio) == REQ_OP_ZONE_REPORT)
|
|
|
- len = ci->sector_count;
|
|
|
- else
|
|
|
- len = min_t(sector_t, max_io_len(ci->sector, ti),
|
|
|
- ci->sector_count);
|
|
|
+ len = min_t(sector_t, max_io_len(ci->sector, ti), ci->sector_count);
|
|
|
|
|
|
r = __clone_and_map_data_bio(ci, ti, ci->sector, &len);
|
|
|
if (r < 0)
|
|
@@ -1616,9 +1617,6 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md,
|
|
|
* We take a clone of the original to store in
|
|
|
* ci.io->orig_bio to be used by end_io_acct() and
|
|
|
* for dec_pending to use for completion handling.
|
|
|
- * As this path is not used for REQ_OP_ZONE_REPORT,
|
|
|
- * the usage of io->orig_bio in dm_remap_zone_report()
|
|
|
- * won't be affected by this reassignment.
|
|
|
*/
|
|
|
struct bio *b = bio_split(bio, bio_sectors(bio) - ci.sector_count,
|
|
|
GFP_NOIO, &md->queue->bio_split);
|
|
@@ -3167,6 +3165,7 @@ static const struct block_device_operations dm_blk_dops = {
|
|
|
.release = dm_blk_close,
|
|
|
.ioctl = dm_blk_ioctl,
|
|
|
.getgeo = dm_blk_getgeo,
|
|
|
+ .report_zones = dm_blk_report_zones,
|
|
|
.pr_ops = &dm_pr_ops,
|
|
|
.owner = THIS_MODULE
|
|
|
};
|