]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/md/dm.c
dm: introduce dm_remap_zone_report()
[karo-tx-linux.git] / drivers / md / dm.c
index e38d1d7d17d6ce1c576e79cc5622743361021b34..96bd13e581cd69c78f61e38ec9364c33109025b4 100644 (file)
@@ -1012,6 +1012,85 @@ 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 device. The zone descriptors
+ * must be remapped to match their 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 target device mapping to the dm device.
+ */
+void dm_remap_zone_report(struct dm_target *ti, struct bio *bio, sector_t start)
+{
+#ifdef CONFIG_BLK_DEV_ZONED
+       struct dm_target_io *tio = container_of(bio, struct dm_target_io, clone);
+       struct bio *report_bio = tio->io->bio;
+       struct blk_zone_report_hdr *hdr = NULL;
+       struct blk_zone *zone;
+       unsigned int nr_rep = 0;
+       unsigned int ofst;
+       struct bio_vec bvec;
+       struct bvec_iter iter;
+       void *addr;
+
+       if (bio->bi_status)
+               return;
+
+       /*
+        * Remap the start sector of the reported zones. For sequential zones,
+        * also remap the write pointer position.
+        */
+       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;
+                       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;
+                       }
+                       ofst += sizeof(struct blk_zone);
+                       hdr->nr_zones--;
+                       nr_rep++;
+               }
+
+               if (addr != hdr)
+                       kunmap_atomic(addr);
+
+               if (!hdr->nr_zones)
+                       break;
+       }
+
+       if (hdr) {
+               hdr->nr_zones = nr_rep;
+               kunmap_atomic(hdr);
+       }
+
+       bio_advance(report_bio, report_bio->bi_iter.bi_size);
+
+#else /* !CONFIG_BLK_DEV_ZONED */
+       bio->bi_status = BLK_STS_NOTSUPP;
+#endif
+}
+EXPORT_SYMBOL_GPL(dm_remap_zone_report);
+
 /*
  * Flush current->bio_list when the target map method blocks.
  * This fixes deadlocks in snapshot and possibly in other targets.