On Tue, Jan 6, 2026 at 5:09 AM Ming Lei <[email protected]> wrote:
>
> On Mon, Jan 05, 2026 at 05:57:35PM -0700, Caleb Sander Mateos wrote:
> > From: Stanley Zhang <[email protected]>
> >
> > Add a feature flag UBLK_F_INTEGRITY for a ublk server to request
> > integrity/metadata support when creating a ublk device. The ublk server
> > can also check for the feature flag on the created device or the result
> > of UBLK_U_CMD_GET_FEATURES to tell if the ublk driver supports it.
> > UBLK_F_INTEGRITY requires UBLK_F_USER_COPY, as user copy is the only
> > data copy mode initially supported for integrity data.
> > Add UBLK_PARAM_TYPE_INTEGRITY and struct ublk_param_integrity to struct
> > ublk_params to specify the integrity params of a ublk device.
> > UBLK_PARAM_TYPE_INTEGRITY requires UBLK_F_INTEGRITY and a nonzero
> > metadata_size. The LBMD_PI_CAP_* and LBMD_PI_CSUM_* values from the
> > linux/fs.h UAPI header are used for the flags and csum_type fields.
> > If the UBLK_PARAM_TYPE_INTEGRITY flag is set, validate the integrity
> > parameters and apply them to the blk_integrity limits.
> > The struct ublk_param_integrity validations are based on the checks in
> > blk_validate_integrity_limits(). Any invalid parameters should be
> > rejected before being applied to struct blk_integrity.
> >
> > Signed-off-by: Stanley Zhang <[email protected]>
> > [csander: drop redundant pi_tuple_size field, use block metadata UAPI
> >  constants, add param validation]
> > Signed-off-by: Caleb Sander Mateos <[email protected]>
> > ---
> >  drivers/block/ublk_drv.c      | 94 ++++++++++++++++++++++++++++++++++-
> >  include/uapi/linux/ublk_cmd.h | 18 +++++++
> >  2 files changed, 111 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > index 8e3da9b2b93a..066c6ae062a0 100644
> > --- a/drivers/block/ublk_drv.c
> > +++ b/drivers/block/ublk_drv.c
> > @@ -42,10 +42,12 @@
> >  #include <linux/mm.h>
> >  #include <asm/page.h>
> >  #include <linux/task_work.h>
> >  #include <linux/namei.h>
> >  #include <linux/kref.h>
> > +#include <linux/blk-integrity.h>
> > +#include <uapi/linux/fs.h>
> >  #include <uapi/linux/ublk_cmd.h>
> >
> >  #define UBLK_MINORS          (1U << MINORBITS)
> >
> >  #define UBLK_INVALID_BUF_IDX         ((u16)-1)
> > @@ -81,11 +83,12 @@
> >
> >  /* All UBLK_PARAM_TYPE_* should be included here */
> >  #define UBLK_PARAM_TYPE_ALL                                \
> >       (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
> >        UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED |    \
> > -      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT)
> > +      UBLK_PARAM_TYPE_DMA_ALIGN | UBLK_PARAM_TYPE_SEGMENT | \
> > +      UBLK_PARAM_TYPE_INTEGRITY)
> >
> >  struct ublk_uring_cmd_pdu {
> >       /*
> >        * Store requests in same batch temporarily for queuing them to
> >        * daemon context.
> > @@ -628,10 +631,57 @@ static void ublk_dev_param_basic_apply(struct 
> > ublk_device *ub)
> >               set_disk_ro(ub->ub_disk, true);
> >
> >       set_capacity(ub->ub_disk, p->dev_sectors);
> >  }
> >
> > +static int ublk_integrity_flags(u32 flags)
> > +{
> > +     int ret_flags = 0;
> > +
> > +     if (flags & LBMD_PI_CAP_INTEGRITY) {
> > +             flags &= ~LBMD_PI_CAP_INTEGRITY;
> > +             ret_flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
> > +     }
> > +     if (flags & LBMD_PI_CAP_REFTAG) {
> > +             flags &= ~LBMD_PI_CAP_REFTAG;
> > +             ret_flags |= BLK_INTEGRITY_REF_TAG;
> > +     }
> > +     return flags ? -EINVAL : ret_flags;
> > +}
> > +
> > +static int ublk_integrity_pi_tuple_size(u8 csum_type)
> > +{
> > +     switch (csum_type) {
> > +     case LBMD_PI_CSUM_NONE:
> > +             return 0;
> > +     case LBMD_PI_CSUM_IP:
> > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > +             return 8;
> > +     case LBMD_PI_CSUM_CRC64_NVME:
> > +             return 16;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
> > +static enum blk_integrity_checksum ublk_integrity_csum_type(u8 csum_type)
> > +{
> > +     switch (csum_type) {
> > +     case LBMD_PI_CSUM_NONE:
> > +             return BLK_INTEGRITY_CSUM_NONE;
> > +     case LBMD_PI_CSUM_IP:
> > +             return BLK_INTEGRITY_CSUM_IP;
> > +     case LBMD_PI_CSUM_CRC16_T10DIF:
> > +             return BLK_INTEGRITY_CSUM_CRC;
> > +     case LBMD_PI_CSUM_CRC64_NVME:
> > +             return BLK_INTEGRITY_CSUM_CRC64;
> > +     default:
> > +             WARN_ON_ONCE(1);
> > +             return BLK_INTEGRITY_CSUM_NONE;
> > +     }
> > +}
> > +
> >  static int ublk_validate_params(const struct ublk_device *ub)
> >  {
> >       /* basic param is the only one which must be set */
> >       if (ub->params.types & UBLK_PARAM_TYPE_BASIC) {
> >               const struct ublk_param_basic *p = &ub->params.basic;
> > @@ -690,10 +740,33 @@ static int ublk_validate_params(const struct 
> > ublk_device *ub)
> >                       return -EINVAL;
> >               if (p->max_segment_size < UBLK_MIN_SEGMENT_SIZE)
> >                       return -EINVAL;
> >       }
> >
> > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > +             int pi_tuple_size = 
> > ublk_integrity_pi_tuple_size(p->csum_type);
> > +             int flags = ublk_integrity_flags(p->flags);
> > +
> > +             if (!(ub->dev_info.flags & UBLK_F_INTEGRITY))
> > +                     return -EINVAL;
> > +             if (flags < 0)
> > +                     return flags;
> > +             if (pi_tuple_size < 0)
> > +                     return pi_tuple_size;
> > +             if (!p->metadata_size)
> > +                     return -EINVAL;
> > +             if (p->csum_type == LBMD_PI_CSUM_NONE &&
> > +                 p->flags & LBMD_PI_CAP_REFTAG)
> > +                     return -EINVAL;
> > +             if (p->pi_offset + pi_tuple_size > p->metadata_size)
> > +                     return -EINVAL;
> > +             if (p->interval_exp < SECTOR_SHIFT ||
> > +                 p->interval_exp > ub->params.basic.logical_bs_shift)
> > +                     return -EINVAL;
> > +     }
> > +
> >       return 0;
> >  }
> >
> >  static void ublk_apply_params(struct ublk_device *ub)
> >  {
> > @@ -2941,10 +3014,25 @@ static int ublk_ctrl_start_dev(struct ublk_device 
> > *ub,
> >               lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
> >               lim.max_segment_size = ub->params.seg.max_segment_size;
> >               lim.max_segments = ub->params.seg.max_segments;
> >       }
> >
> > +     if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
> > +             const struct ublk_param_integrity *p = &ub->params.integrity;
> > +             int pi_tuple_size = 
> > ublk_integrity_pi_tuple_size(p->csum_type);
> > +
> > +             lim.integrity = (struct blk_integrity) {
> > +                     .flags = ublk_integrity_flags(p->flags),
> > +                     .csum_type = ublk_integrity_csum_type(p->csum_type),
> > +                     .metadata_size = p->metadata_size,
> > +                     .pi_offset = p->pi_offset,
> > +                     .interval_exp = p->interval_exp,
> > +                     .tag_size = p->tag_size,
> > +                     .pi_tuple_size = pi_tuple_size,
> > +             };
> > +     }
> > +
> >       if (wait_for_completion_interruptible(&ub->completion) != 0)
> >               return -EINTR;
> >
> >       if (ub->ublksrv_tgid != ublksrv_pid)
> >               return -EINVAL;
> > @@ -3131,10 +3219,14 @@ static int ublk_ctrl_add_dev(const struct 
> > ublksrv_ctrl_cmd *header)
> >               if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY 
> > |
> >                                       UBLK_F_AUTO_BUF_REG))
> >                       return -EINVAL;
> >       }
> >
> > +     /* User copy is required to access integrity buffer */
> > +     if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
> > +             return -EINVAL;
> > +
> >       /* the created device is always owned by current user */
> >       ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
> >
> >       if (header->dev_id != info.dev_id) {
> >               pr_warn("%s: dev id not match %u %u\n",
> > diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
> > index ec77dabba45b..a54c47832fa2 100644
> > --- a/include/uapi/linux/ublk_cmd.h
> > +++ b/include/uapi/linux/ublk_cmd.h
> > @@ -309,10 +309,16 @@
> >   * the I/O's daemon task. The q_id and tag of the registered buffer are 
> > required
> >   * in UBLK_U_IO_UNREGISTER_IO_BUF's ublksrv_io_cmd.
> >   */
> >  #define UBLK_F_BUF_REG_OFF_DAEMON (1ULL << 14)
> >
> > +/*
> > + * ublk device supports requests with integrity/metadata buffer.
> > + * Requires UBLK_F_USER_COPY.
> > + */
> > +#define UBLK_F_INTEGRITY (1ULL << 16)
> > +
> >  /* device state */
> >  #define UBLK_S_DEV_DEAD      0
> >  #define UBLK_S_DEV_LIVE      1
> >  #define UBLK_S_DEV_QUIESCED  2
> >  #define UBLK_S_DEV_FAIL_IO   3
> > @@ -598,10 +604,20 @@ struct ublk_param_segment {
> >       __u32   max_segment_size;
> >       __u16   max_segments;
> >       __u8    pad[2];
> >  };
> >
> > +struct ublk_param_integrity {
> > +     __u32   flags; /* LBMD_PI_CAP_* from linux/fs.h */
> > +     __u8    interval_exp;
> > +     __u8    metadata_size; /* UBLK_PARAM_TYPE_INTEGRITY requires nonzero 
> > */
> > +     __u8    pi_offset;
> > +     __u8    csum_type; /* LBMD_PI_CSUM_* from linux/fs.h */
> > +     __u8    tag_size;
> > +     __u8    pad[7];
> > +};
>
> Looks max_integrity_segments is missed, otherwise this patch is fine for me.

My thinking was that there isn't any reason why a ublk server would
need to limit the number of integrity segments. The request integrity
segments aren't directly exposed to the ublk server through user copy.
And since zero copy isn't supported for integrity data, there's no
concern about needing to align the ublk integrity limits with the
backing device limits. What do you think?

Thanks,
Caleb

Reply via email to