Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

block: Implement zone append emulation

Given that zone write plugging manages all writes to zones of a zoned
block device and tracks the write pointer position of all zones that are
not full nor empty, emulating zone append operations using regular
writes can be implemented generically, without relying on the underlying
device driver to implement such emulation. This is needed for devices
that do not natively support the zone append command (e.g. SMR
hard-disks).

A device may request zone append emulation by setting its
max_zone_append_sectors queue limit to 0. For such device, the function
blk_zone_wplug_prepare_bio() changes zone append BIOs into
non-mergeable regular write BIOs. Modified zone append BIOs are flagged
with the new BIO flag BIO_EMULATES_ZONE_APPEND. This flag is checked
on completion of the BIO in blk_zone_write_plug_bio_endio() to restore
the original REQ_OP_ZONE_APPEND operation code of the BIO.

The block layer internal inline helper function bio_is_zone_append() is
added to test if a BIO is either a native zone append operation
(REQ_OP_ZONE_APPEND operation code) or if it is flagged with
BIO_EMULATES_ZONE_APPEND. Given that both native and emulated zone
append BIO completion handling should be similar, The functions
blk_update_request() and blk_zone_complete_request_bio() are modified to
use bio_is_zone_append() to execute blk_zone_update_request_bio() for
both native and emulated zone append operations.

This commit contains contributions from Christoph Hellwig <hch@lst.de>.

Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Tested-by: Hans Holmberg <hans.holmberg@wdc.com>
Tested-by: Dennis Maisenbacher <dennis.maisenbacher@wdc.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Link: https://lore.kernel.org/r/20240408014128.205141-11-dlemoal@kernel.org
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Damien Le Moal and committed by
Jens Axboe
9b1ce7f0 ccdbf0aa

+67 -15
+1 -2
block/blk-mq.c
··· 906 906 907 907 if (bio_bytes == bio->bi_iter.bi_size) { 908 908 req->bio = bio->bi_next; 909 - } else if (req_op(req) == REQ_OP_ZONE_APPEND && 910 - error == BLK_STS_OK) { 909 + } else if (bio_is_zone_append(bio) && error == BLK_STS_OK) { 911 910 /* 912 911 * Partial zone append completions cannot be supported 913 912 * as the BIO fragments may end up not being written
+53 -11
block/blk-zoned.c
··· 689 689 690 690 while ((bio = bio_list_pop(&zwplug->bio_list))) { 691 691 if (wp_offset >= zone_capacity || 692 - bio_offset_from_zone_start(bio) != wp_offset) { 692 + (bio_op(bio) != REQ_OP_ZONE_APPEND && 693 + bio_offset_from_zone_start(bio) != wp_offset)) { 693 694 blk_zone_wplug_bio_io_error(bio); 694 695 disk_put_zone_wplug(zwplug); 695 696 continue; ··· 952 951 953 952 /* 954 953 * Check and prepare a BIO for submission by incrementing the write pointer 955 - * offset of its zone write plug. 954 + * offset of its zone write plug and changing zone append operations into 955 + * regular write when zone append emulation is needed. 956 956 */ 957 957 static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug, 958 958 struct bio *bio) ··· 968 966 if (zwplug->wp_offset >= disk->zone_capacity) 969 967 goto err; 970 968 971 - /* 972 - * Check for non-sequential writes early because we avoid a 973 - * whole lot of error handling trouble if we don't send it off 974 - * to the driver. 975 - */ 976 - if (bio_offset_from_zone_start(bio) != zwplug->wp_offset) 977 - goto err; 969 + if (bio_op(bio) == REQ_OP_ZONE_APPEND) { 970 + /* 971 + * Use a regular write starting at the current write pointer. 972 + * Similarly to native zone append operations, do not allow 973 + * merging. 974 + */ 975 + bio->bi_opf &= ~REQ_OP_MASK; 976 + bio->bi_opf |= REQ_OP_WRITE | REQ_NOMERGE; 977 + bio->bi_iter.bi_sector += zwplug->wp_offset; 978 + 979 + /* 980 + * Remember that this BIO is in fact a zone append operation 981 + * so that we can restore its operation code on completion. 982 + */ 983 + bio_set_flag(bio, BIO_EMULATES_ZONE_APPEND); 984 + } else { 985 + /* 986 + * Check for non-sequential writes early because we avoid a 987 + * whole lot of error handling trouble if we don't send it off 988 + * to the driver. 989 + */ 990 + if (bio_offset_from_zone_start(bio) != zwplug->wp_offset) 991 + goto err; 992 + } 978 993 979 994 /* Advance the zone write pointer offset. */ 980 995 zwplug->wp_offset += bio_sectors(bio); ··· 1027 1008 } 1028 1009 1029 1010 /* Conventional zones do not need write plugging. */ 1030 - if (disk_zone_is_conv(disk, sector)) 1011 + if (disk_zone_is_conv(disk, sector)) { 1012 + /* Zone append to conventional zones is not allowed. */ 1013 + if (bio_op(bio) == REQ_OP_ZONE_APPEND) { 1014 + bio_io_error(bio); 1015 + return true; 1016 + } 1031 1017 return false; 1018 + } 1032 1019 1033 1020 if (bio->bi_opf & REQ_NOWAIT) 1034 1021 gfp_mask = GFP_NOWAIT; ··· 1082 1057 * @bio: The BIO being submitted 1083 1058 * @nr_segs: The number of physical segments of @bio 1084 1059 * 1085 - * Handle write and write zeroes operations using zone write plugging. 1060 + * Handle write, write zeroes and zone append operations requiring emulation 1061 + * using zone write plugging. 1086 1062 * 1087 1063 * Return true whenever @bio execution needs to be delayed through the zone 1088 1064 * write plug. Otherwise, return false to let the submission path process ··· 1122 1096 * machinery operates at the request level, below the plug, and 1123 1097 * completion of the flush sequence will go through the regular BIO 1124 1098 * completion, which will handle zone write plugging. 1099 + * Zone append operations for devices that requested emulation must 1100 + * also be plugged so that these BIOs can be changed into regular 1101 + * write BIOs. 1125 1102 * Zone reset, reset all and finish commands need special treatment 1126 1103 * to correctly track the write pointer offset of zones. These commands 1127 1104 * are not plugged as we do not need serialization with write ··· 1132 1103 * and finish commands when write operations are in flight. 1133 1104 */ 1134 1105 switch (bio_op(bio)) { 1106 + case REQ_OP_ZONE_APPEND: 1107 + if (!bdev_emulates_zone_append(bdev)) 1108 + return false; 1109 + fallthrough; 1135 1110 case REQ_OP_WRITE: 1136 1111 case REQ_OP_WRITE_ZEROES: 1137 1112 return blk_zone_wplug_handle_write(bio, nr_segs); ··· 1203 1170 1204 1171 /* Make sure we do not see this BIO again by clearing the plug flag. */ 1205 1172 bio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING); 1173 + 1174 + /* 1175 + * If this is a regular write emulating a zone append operation, 1176 + * restore the original operation code. 1177 + */ 1178 + if (bio_flagged(bio, BIO_EMULATES_ZONE_APPEND)) { 1179 + bio->bi_opf &= ~REQ_OP_MASK; 1180 + bio->bi_opf |= REQ_OP_ZONE_APPEND; 1181 + } 1206 1182 1207 1183 /* 1208 1184 * If the BIO failed, mark the plug as having an error to trigger
+12 -2
block/blk.h
··· 421 421 { 422 422 return bio_flagged(bio, BIO_ZONE_WRITE_PLUGGING); 423 423 } 424 + static inline bool bio_is_zone_append(struct bio *bio) 425 + { 426 + return bio_op(bio) == REQ_OP_ZONE_APPEND || 427 + bio_flagged(bio, BIO_EMULATES_ZONE_APPEND); 428 + } 424 429 void blk_zone_write_plug_bio_merged(struct bio *bio); 425 430 void blk_zone_write_plug_attempt_merge(struct request *rq); 426 431 static inline void blk_zone_update_request_bio(struct request *rq, ··· 435 430 * For zone append requests, the request sector indicates the location 436 431 * at which the BIO data was written. Return this value to the BIO 437 432 * issuer through the BIO iter sector. 438 - * For plugged zone writes, we need the original BIO sector so 439 - * that blk_zone_write_plug_bio_endio() can lookup the zone write plug. 433 + * For plugged zone writes, which include emulated zone append, we need 434 + * the original BIO sector so that blk_zone_write_plug_bio_endio() can 435 + * lookup the zone write plug. 440 436 */ 441 437 if (req_op(rq) == REQ_OP_ZONE_APPEND || bio_zone_write_plugging(bio)) 442 438 bio->bi_iter.bi_sector = rq->__sector; ··· 471 465 { 472 466 } 473 467 static inline bool bio_zone_write_plugging(struct bio *bio) 468 + { 469 + return false; 470 + } 471 + static inline bool bio_is_zone_append(struct bio *bio) 474 472 { 475 473 return false; 476 474 }
+1
include/linux/blk_types.h
··· 311 311 BIO_REMAPPED, 312 312 BIO_ZONE_WRITE_LOCKED, /* Owns a zoned device zone write lock */ 313 313 BIO_ZONE_WRITE_PLUGGING, /* bio handled through zone write plugging */ 314 + BIO_EMULATES_ZONE_APPEND, /* bio emulates a zone append operation */ 314 315 BIO_FLAG_LAST 315 316 }; 316 317