On Wed, Nov 05, 2025 at 04:02:36PM +0100, Mikulas Patocka wrote:
> Allow handling of bios with REQ_ATOMIC flag set.
>
> Don't split these bios and fail them if they overrun the hard limit
> "BIO_MAX_VECS << PAGE_SHIFT".
>
> This commit joins the logic that avoids splitting emulated zone append
> bios with atomic write bios.
>
> Signed-off-by: John Garry <[email protected]>
> Signed-off-by: Mikulas Patocka <[email protected]>
>
> ---
> drivers/md/dm-crypt.c | 39 ++++++++++++++++++++++++---------------
> 1 file changed, 24 insertions(+), 15 deletions(-)
>
> Index: linux-2.6/drivers/md/dm-crypt.c
> ===================================================================
> --- linux-2.6.orig/drivers/md/dm-crypt.c 2025-11-05 14:50:28.000000000
> +0100
> +++ linux-2.6/drivers/md/dm-crypt.c 2025-11-05 14:50:28.000000000 +0100
> @@ -254,22 +254,15 @@ static unsigned int max_write_size = 0;
> module_param(max_write_size, uint, 0644);
> MODULE_PARM_DESC(max_write_size, "Maximum size of a write request");
>
> -static unsigned get_max_request_sectors(struct dm_target *ti, struct bio
> *bio)
> +static unsigned get_max_request_sectors(struct dm_target *ti, struct bio
> *bio, bool no_split)
> {
> struct crypt_config *cc = ti->private;
> unsigned val, sector_align;
> bool wrt = op_is_write(bio_op(bio));
>
> - if (wrt) {
> - /*
> - * For zoned devices, splitting write operations creates the
> - * risk of deadlocking queue freeze operations with zone write
> - * plugging BIO work when the reminder of a split BIO is
> - * issued. So always allow the entire BIO to proceed.
> - */
> - if (ti->emulate_zone_append)
> - return bio_sectors(bio);
> -
> + if (no_split) {
> + val = -1;
> + } else if (wrt) {
> val = min_not_zero(READ_ONCE(max_write_size),
> DM_CRYPT_DEFAULT_MAX_WRITE_SIZE);
> } else {
> @@ -3496,6 +3489,7 @@ static int crypt_map(struct dm_target *t
> struct dm_crypt_io *io;
> struct crypt_config *cc = ti->private;
> unsigned max_sectors;
> + bool no_split;
>
> /*
> * If bio is REQ_PREFLUSH or REQ_OP_DISCARD, just bypass crypt queues.
> @@ -3513,10 +3507,20 @@ static int crypt_map(struct dm_target *t
>
> /*
> * Check if bio is too large, split as needed.
> - */
> - max_sectors = get_max_request_sectors(ti, bio);
> - if (unlikely(bio_sectors(bio) > max_sectors))
> + *
> + * For zoned devices, splitting write operations creates the
> + * risk of deadlocking queue freeze operations with zone write
> + * plugging BIO work when the reminder of a split BIO is
> + * issued. So always allow the entire BIO to proceed.
> + */
> + no_split = (ti->emulate_zone_append && op_is_write(bio_op(bio))) ||
> + (bio->bi_opf & REQ_ATOMIC);
> + max_sectors = get_max_request_sectors(ti, bio, no_split);
> + if (unlikely(bio_sectors(bio) > max_sectors)) {
> + if (unlikely(no_split))
> + return DM_MAPIO_KILL;
> dm_accept_partial_bio(bio, max_sectors);
> + }
>
> /*
> * Ensure that bio is a multiple of internal sector encryption size
> @@ -3762,6 +3766,11 @@ static void crypt_io_hints(struct dm_tar
> if (ti->emulate_zone_append)
> limits->max_hw_sectors = min(limits->max_hw_sectors,
> BIO_MAX_VECS <<
> PAGE_SECTORS_SHIFT);
> +
> + limits->atomic_write_hw_unit_max = min(limits->atomic_write_hw_unit_max,
> + BIO_MAX_VECS << PAGE_SHIFT);
> + limits->atomic_write_hw_max = min(limits->atomic_write_hw_max,
> + BIO_MAX_VECS << PAGE_SHIFT);
> }
Do we need to cap these limits, instead of just accepting the underlying
device limits? Neither of them are really used for IO.
atomic_write_unit_max, which is used for IO, will already get a capped
value from atomic_write_hw_unit_max in
blk_atomic_writes_update_limits(). And capping atomic_write_hw_max seems
wrong, since atomic_write_hw_max == UINT_MAX is used
blk_validate_atomic_write_limits() to indicate that atomic writes were
never set up, because no underlying device supported them. I don't think
these caps will actually break things, but in my mind they make some
already confusing limits even more confusing.
Or am I missing some reason why this is needed?
-Ben
>
> static struct target_type crypt_target = {
> @@ -3770,7 +3779,7 @@ static struct target_type crypt_target =
> .module = THIS_MODULE,
> .ctr = crypt_ctr,
> .dtr = crypt_dtr,
> - .features = DM_TARGET_ZONED_HM,
> + .features = DM_TARGET_ZONED_HM | DM_TARGET_ATOMIC_WRITES,
> .report_zones = crypt_report_zones,
> .map = crypt_map,
> .status = crypt_status,
>