]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Dec 2018 15:45:26 +0000 (16:45 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Dec 2018 15:45:26 +0000 (16:45 +0100)
added patches:
dm-zoned-fix-target-bio-completion-handling.patch

queue-4.19/dm-zoned-fix-target-bio-completion-handling.patch [new file with mode: 0644]
queue-4.19/series

diff --git a/queue-4.19/dm-zoned-fix-target-bio-completion-handling.patch b/queue-4.19/dm-zoned-fix-target-bio-completion-handling.patch
new file mode 100644 (file)
index 0000000..20ac9db
--- /dev/null
@@ -0,0 +1,261 @@
+From d57f9da890696af1484f4a47f7f123560197865a Mon Sep 17 00:00:00 2001
+From: Damien Le Moal <damien.lemoal@wdc.com>
+Date: Fri, 30 Nov 2018 15:31:48 +0900
+Subject: dm zoned: Fix target BIO completion handling
+
+From: Damien Le Moal <damien.lemoal@wdc.com>
+
+commit d57f9da890696af1484f4a47f7f123560197865a upstream.
+
+struct bioctx includes the ref refcount_t to track the number of I/O
+fragments used to process a target BIO as well as ensure that the zone
+of the BIO is kept in the active state throughout the lifetime of the
+BIO. However, since decrementing of this reference count is done in the
+target .end_io method, the function bio_endio() must be called multiple
+times for read and write target BIOs, which causes problems with the
+value of the __bi_remaining struct bio field for chained BIOs (e.g. the
+clone BIO passed by dm core is large and splits into fragments by the
+block layer), resulting in incorrect values and inconsistencies with the
+BIO_CHAIN flag setting. This is turn triggers the BUG_ON() call:
+
+BUG_ON(atomic_read(&bio->__bi_remaining) <= 0);
+
+in bio_remaining_done() called from bio_endio().
+
+Fix this ensuring that bio_endio() is called only once for any target
+BIO by always using internal clone BIOs for processing any read or
+write target BIO. This allows reference counting using the target BIO
+context counter to trigger the target BIO completion bio_endio() call
+once all data, metadata and other zone work triggered by the BIO
+complete.
+
+Overall, this simplifies the code too as the target .end_io becomes
+unnecessary and differences between read and write BIO issuing and
+completion processing disappear.
+
+Fixes: 3b1a94c88b79 ("dm zoned: drive-managed zoned block device target")
+Cc: stable@vger.kernel.org
+Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
+Signed-off-by: Mike Snitzer <snitzer@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/md/dm-zoned-target.c |  122 +++++++++++++------------------------------
+ 1 file changed, 38 insertions(+), 84 deletions(-)
+
+--- a/drivers/md/dm-zoned-target.c
++++ b/drivers/md/dm-zoned-target.c
+@@ -20,7 +20,6 @@ struct dmz_bioctx {
+       struct dm_zone          *zone;
+       struct bio              *bio;
+       atomic_t                ref;
+-      blk_status_t            status;
+ };
+ /*
+@@ -78,65 +77,66 @@ static inline void dmz_bio_endio(struct
+ {
+       struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
+-      if (bioctx->status == BLK_STS_OK && status != BLK_STS_OK)
+-              bioctx->status = status;
+-      bio_endio(bio);
++      if (status != BLK_STS_OK && bio->bi_status == BLK_STS_OK)
++              bio->bi_status = status;
++
++      if (atomic_dec_and_test(&bioctx->ref)) {
++              struct dm_zone *zone = bioctx->zone;
++
++              if (zone) {
++                      if (bio->bi_status != BLK_STS_OK &&
++                          bio_op(bio) == REQ_OP_WRITE &&
++                          dmz_is_seq(zone))
++                              set_bit(DMZ_SEQ_WRITE_ERR, &zone->flags);
++                      dmz_deactivate_zone(zone);
++              }
++              bio_endio(bio);
++      }
+ }
+ /*
+- * Partial clone read BIO completion callback. This terminates the
++ * Completion callback for an internally cloned target BIO. This terminates the
+  * target BIO when there are no more references to its context.
+  */
+-static void dmz_read_bio_end_io(struct bio *bio)
++static void dmz_clone_endio(struct bio *clone)
+ {
+-      struct dmz_bioctx *bioctx = bio->bi_private;
+-      blk_status_t status = bio->bi_status;
++      struct dmz_bioctx *bioctx = clone->bi_private;
++      blk_status_t status = clone->bi_status;
+-      bio_put(bio);
++      bio_put(clone);
+       dmz_bio_endio(bioctx->bio, status);
+ }
+ /*
+- * Issue a BIO to a zone. The BIO may only partially process the
++ * Issue a clone of a target BIO. The clone may only partially process the
+  * original target BIO.
+  */
+-static int dmz_submit_read_bio(struct dmz_target *dmz, struct dm_zone *zone,
+-                             struct bio *bio, sector_t chunk_block,
+-                             unsigned int nr_blocks)
++static int dmz_submit_bio(struct dmz_target *dmz, struct dm_zone *zone,
++                        struct bio *bio, sector_t chunk_block,
++                        unsigned int nr_blocks)
+ {
+       struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
+-      sector_t sector;
+       struct bio *clone;
+-      /* BIO remap sector */
+-      sector = dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
+-
+-      /* If the read is not partial, there is no need to clone the BIO */
+-      if (nr_blocks == dmz_bio_blocks(bio)) {
+-              /* Setup and submit the BIO */
+-              bio->bi_iter.bi_sector = sector;
+-              atomic_inc(&bioctx->ref);
+-              generic_make_request(bio);
+-              return 0;
+-      }
+-
+-      /* Partial BIO: we need to clone the BIO */
+       clone = bio_clone_fast(bio, GFP_NOIO, &dmz->bio_set);
+       if (!clone)
+               return -ENOMEM;
+-      /* Setup the clone */
+-      clone->bi_iter.bi_sector = sector;
++      bio_set_dev(clone, dmz->dev->bdev);
++      clone->bi_iter.bi_sector =
++              dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
+       clone->bi_iter.bi_size = dmz_blk2sect(nr_blocks) << SECTOR_SHIFT;
+-      clone->bi_end_io = dmz_read_bio_end_io;
++      clone->bi_end_io = dmz_clone_endio;
+       clone->bi_private = bioctx;
+       bio_advance(bio, clone->bi_iter.bi_size);
+-      /* Submit the clone */
+       atomic_inc(&bioctx->ref);
+       generic_make_request(clone);
++      if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone))
++              zone->wp_block += nr_blocks;
++
+       return 0;
+ }
+@@ -214,7 +214,7 @@ static int dmz_handle_read(struct dmz_ta
+               if (nr_blocks) {
+                       /* Valid blocks found: read them */
+                       nr_blocks = min_t(unsigned int, nr_blocks, end_block - chunk_block);
+-                      ret = dmz_submit_read_bio(dmz, rzone, bio, chunk_block, nr_blocks);
++                      ret = dmz_submit_bio(dmz, rzone, bio, chunk_block, nr_blocks);
+                       if (ret)
+                               return ret;
+                       chunk_block += nr_blocks;
+@@ -229,25 +229,6 @@ static int dmz_handle_read(struct dmz_ta
+ }
+ /*
+- * Issue a write BIO to a zone.
+- */
+-static void dmz_submit_write_bio(struct dmz_target *dmz, struct dm_zone *zone,
+-                               struct bio *bio, sector_t chunk_block,
+-                               unsigned int nr_blocks)
+-{
+-      struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
+-
+-      /* Setup and submit the BIO */
+-      bio_set_dev(bio, dmz->dev->bdev);
+-      bio->bi_iter.bi_sector = dmz_start_sect(dmz->metadata, zone) + dmz_blk2sect(chunk_block);
+-      atomic_inc(&bioctx->ref);
+-      generic_make_request(bio);
+-
+-      if (dmz_is_seq(zone))
+-              zone->wp_block += nr_blocks;
+-}
+-
+-/*
+  * Write blocks directly in a data zone, at the write pointer.
+  * If a buffer zone is assigned, invalidate the blocks written
+  * in place.
+@@ -265,7 +246,9 @@ static int dmz_handle_direct_write(struc
+               return -EROFS;
+       /* Submit write */
+-      dmz_submit_write_bio(dmz, zone, bio, chunk_block, nr_blocks);
++      ret = dmz_submit_bio(dmz, zone, bio, chunk_block, nr_blocks);
++      if (ret)
++              return ret;
+       /*
+        * Validate the blocks in the data zone and invalidate
+@@ -301,7 +284,9 @@ static int dmz_handle_buffered_write(str
+               return -EROFS;
+       /* Submit write */
+-      dmz_submit_write_bio(dmz, bzone, bio, chunk_block, nr_blocks);
++      ret = dmz_submit_bio(dmz, bzone, bio, chunk_block, nr_blocks);
++      if (ret)
++              return ret;
+       /*
+        * Validate the blocks in the buffer zone
+@@ -600,7 +585,6 @@ static int dmz_map(struct dm_target *ti,
+       bioctx->zone = NULL;
+       bioctx->bio = bio;
+       atomic_set(&bioctx->ref, 1);
+-      bioctx->status = BLK_STS_OK;
+       /* Set the BIO pending in the flush list */
+       if (!nr_sectors && bio_op(bio) == REQ_OP_WRITE) {
+@@ -624,35 +608,6 @@ static int dmz_map(struct dm_target *ti,
+ }
+ /*
+- * Completed target BIO processing.
+- */
+-static int dmz_end_io(struct dm_target *ti, struct bio *bio, blk_status_t *error)
+-{
+-      struct dmz_bioctx *bioctx = dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
+-
+-      if (bioctx->status == BLK_STS_OK && *error)
+-              bioctx->status = *error;
+-
+-      if (!atomic_dec_and_test(&bioctx->ref))
+-              return DM_ENDIO_INCOMPLETE;
+-
+-      /* Done */
+-      bio->bi_status = bioctx->status;
+-
+-      if (bioctx->zone) {
+-              struct dm_zone *zone = bioctx->zone;
+-
+-              if (*error && bio_op(bio) == REQ_OP_WRITE) {
+-                      if (dmz_is_seq(zone))
+-                              set_bit(DMZ_SEQ_WRITE_ERR, &zone->flags);
+-              }
+-              dmz_deactivate_zone(zone);
+-      }
+-
+-      return DM_ENDIO_DONE;
+-}
+-
+-/*
+  * Get zoned device information.
+  */
+ static int dmz_get_zoned_device(struct dm_target *ti, char *path)
+@@ -947,7 +902,6 @@ static struct target_type dmz_type = {
+       .ctr             = dmz_ctr,
+       .dtr             = dmz_dtr,
+       .map             = dmz_map,
+-      .end_io          = dmz_end_io,
+       .io_hints        = dmz_io_hints,
+       .prepare_ioctl   = dmz_prepare_ioctl,
+       .postsuspend     = dmz_suspend,
index ebab32197be7c74ef52b2dfeefab92f5d707560d..ffffa423f44cf7bf6b79df8d8ebe89ccfd9a1bc4 100644 (file)
@@ -40,3 +40,4 @@ drm-amdkfd-add-new-vega10-pci-ids.patch
 drm-amdgpu-add-some-additional-vega10-pci-ids.patch
 drm-amdgpu-update-smu-firmware-images-for-vi-variants-v2.patch
 drm-amdgpu-update-smc-firmware-image-for-polaris10-variants.patch
+dm-zoned-fix-target-bio-completion-handling.patch