Skip to content

Commit bf14e2b

Browse files
damien-lemoalsnitm
authored andcommitted
dm: Forbid requeue of writes to zones
A target map method requesting the requeue of a bio with DM_MAPIO_REQUEUE or completing it with DM_ENDIO_REQUEUE can cause unaligned write errors if the bio is a write operation targeting a sequential zone. If a zoned target request such a requeue, warn about it and kill the IO. The function dm_is_zone_write() is introduced to detect write operations to zoned targets. This change does not affect the target drivers supporting zoned devices and exposing a zoned device, namely dm-crypt, dm-linear and dm-flakey as none of these targets ever request a requeue. Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
1 parent 912e887 commit bf14e2b

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

drivers/md/dm-zone.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,23 @@ int dm_report_zones(struct block_device *bdev, sector_t start, sector_t sector,
104104
}
105105
EXPORT_SYMBOL_GPL(dm_report_zones);
106106

107+
bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)
108+
{
109+
struct request_queue *q = md->queue;
110+
111+
if (!blk_queue_is_zoned(q))
112+
return false;
113+
114+
switch (bio_op(bio)) {
115+
case REQ_OP_WRITE_ZEROES:
116+
case REQ_OP_WRITE_SAME:
117+
case REQ_OP_WRITE:
118+
return !op_is_flush(bio->bi_opf) && bio_sectors(bio);
119+
default:
120+
return false;
121+
}
122+
}
123+
107124
void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q)
108125
{
109126
if (!blk_queue_is_zoned(q))

drivers/md/dm.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -841,22 +841,27 @@ static void dec_pending(struct dm_io *io, blk_status_t error)
841841
}
842842

843843
if (atomic_dec_and_test(&io->io_count)) {
844+
bio = io->orig_bio;
844845
if (io->status == BLK_STS_DM_REQUEUE) {
845846
/*
846847
* Target requested pushing back the I/O.
847848
*/
848849
spin_lock_irqsave(&md->deferred_lock, flags);
849-
if (__noflush_suspending(md))
850+
if (__noflush_suspending(md) &&
851+
!WARN_ON_ONCE(dm_is_zone_write(md, bio))) {
850852
/* NOTE early return due to BLK_STS_DM_REQUEUE below */
851-
bio_list_add_head(&md->deferred, io->orig_bio);
852-
else
853-
/* noflush suspend was interrupted. */
853+
bio_list_add_head(&md->deferred, bio);
854+
} else {
855+
/*
856+
* noflush suspend was interrupted or this is
857+
* a write to a zoned target.
858+
*/
854859
io->status = BLK_STS_IOERR;
860+
}
855861
spin_unlock_irqrestore(&md->deferred_lock, flags);
856862
}
857863

858864
io_error = io->status;
859-
bio = io->orig_bio;
860865
end_io_acct(io);
861866
free_io(md, io);
862867

@@ -947,7 +952,15 @@ static void clone_endio(struct bio *bio)
947952
int r = endio(tio->ti, bio, &error);
948953
switch (r) {
949954
case DM_ENDIO_REQUEUE:
950-
error = BLK_STS_DM_REQUEUE;
955+
/*
956+
* Requeuing writes to a sequential zone of a zoned
957+
* target will break the sequential write pattern:
958+
* fail such IO.
959+
*/
960+
if (WARN_ON_ONCE(dm_is_zone_write(md, bio)))
961+
error = BLK_STS_IOERR;
962+
else
963+
error = BLK_STS_DM_REQUEUE;
951964
fallthrough;
952965
case DM_ENDIO_DONE:
953966
break;

drivers/md/dm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,13 @@ void dm_set_zones_restrictions(struct dm_table *t, struct request_queue *q);
107107
#ifdef CONFIG_BLK_DEV_ZONED
108108
int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
109109
unsigned int nr_zones, report_zones_cb cb, void *data);
110+
bool dm_is_zone_write(struct mapped_device *md, struct bio *bio);
110111
#else
111112
#define dm_blk_report_zones NULL
113+
static inline bool dm_is_zone_write(struct mapped_device *md, struct bio *bio)
114+
{
115+
return false;
116+
}
112117
#endif
113118

114119
/*-----------------------------------------------------------------

0 commit comments

Comments
 (0)