ublk: support UBLK_PARAM_TYPE_INTEGRITY in device creation

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.

[csander: drop redundant pi_tuple_size field, use block metadata UAPI
 constants, add param validation]

Signed-off-by: Stanley Zhang <stazhang@purestorage.com>
Signed-off-by: Caleb Sander Mateos <csander@purestorage.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Stanley Zhang 2026-01-08 02:19:31 -07:00 committed by Jens Axboe
parent e859e7c26a
commit 98bf225685
2 changed files with 119 additions and 1 deletions

View File

@ -44,6 +44,8 @@
#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)
@ -83,7 +85,8 @@
#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 {
/*
@ -301,6 +304,11 @@ static inline bool ublk_queue_is_zoned(const struct ublk_queue *ubq)
return ubq->flags & UBLK_F_ZONED;
}
static inline bool ublk_dev_support_integrity(const struct ublk_device *ub)
{
return ub->dev_info.flags & UBLK_F_INTEGRITY;
}
#ifdef CONFIG_BLK_DEV_ZONED
struct ublk_zoned_report_desc {
@ -616,6 +624,53 @@ static void ublk_dev_param_basic_apply(struct ublk_device *ub)
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 */
@ -678,6 +733,29 @@ static int ublk_validate_params(const struct ublk_device *ub)
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 (!ublk_dev_support_integrity(ub))
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;
}
@ -2950,6 +3028,23 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub,
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.max_integrity_segments =
p->max_integrity_segments ?: USHRT_MAX;
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;
@ -3140,6 +3235,10 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
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);

View File

@ -311,6 +311,12 @@
*/
#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
@ -600,6 +606,17 @@ struct ublk_param_segment {
__u8 pad[2];
};
struct ublk_param_integrity {
__u32 flags; /* LBMD_PI_CAP_* from linux/fs.h */
__u16 max_integrity_segments; /* 0 means no limit */
__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[5];
};
struct ublk_params {
/*
* Total length of parameters, userspace has to set 'len' for both
@ -614,6 +631,7 @@ struct ublk_params {
#define UBLK_PARAM_TYPE_ZONED (1 << 3)
#define UBLK_PARAM_TYPE_DMA_ALIGN (1 << 4)
#define UBLK_PARAM_TYPE_SEGMENT (1 << 5)
#define UBLK_PARAM_TYPE_INTEGRITY (1 << 6) /* requires UBLK_F_INTEGRITY */
__u32 types; /* types of parameter included */
struct ublk_param_basic basic;
@ -622,6 +640,7 @@ struct ublk_params {
struct ublk_param_zoned zoned;
struct ublk_param_dma_align dma;
struct ublk_param_segment seg;
struct ublk_param_integrity integrity;
};
#endif