Skip to content

Commit f48250d

Browse files
calebsanderaxboe
authored andcommitted
selftests: ublk: add integrity data support to loop target
To perform and end-to-end test of integrity information through a ublk device, we need to actually store it somewhere and retrieve it. Add this support to kublk's loop target. It uses a second backing file for the integrity data corresponding to the data stored in the first file. The integrity file is initialized with byte 0xFF, which ensures the app and reference tags are set to the "escape" pattern to disable the bio-integrity-auto guard and reftag checks until the blocks are written. The integrity file is opened without O_DIRECT since it will be accessed at sub-block granularity. Each incoming read/write results in a pair of reads/writes, one to the data file, and one to the integrity file. If either backing I/O fails, the error is propagated to the ublk request. If both backing I/Os read/write some bytes, the ublk request is completed with the smaller of the number of blocks accessed by each I/O. 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>
1 parent a180544 commit f48250d

1 file changed

Lines changed: 74 additions & 18 deletions

File tree

tools/testing/selftests/ublk/file_backed.c

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,38 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
3535
unsigned auto_zc = ublk_queue_use_auto_zc(q);
3636
enum io_uring_op op = ublk_to_uring_op(iod, zc | auto_zc);
3737
struct ublk_io *io = ublk_get_io(q, tag);
38+
__u64 offset = iod->start_sector << 9;
39+
__u32 len = iod->nr_sectors << 9;
3840
struct io_uring_sqe *sqe[3];
3941
void *addr = io->buf_addr;
4042

43+
if (iod->op_flags & UBLK_IO_F_INTEGRITY) {
44+
ublk_io_alloc_sqes(t, sqe, 1);
45+
/* Use second backing file for integrity data */
46+
io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 2),
47+
io->integrity_buf,
48+
ublk_integrity_len(q, len),
49+
ublk_integrity_len(q, offset));
50+
sqe[0]->flags = IOSQE_FIXED_FILE;
51+
/* tgt_data = 1 indicates integrity I/O */
52+
sqe[0]->user_data = build_user_data(tag, ublk_op, 1, q->q_id, 1);
53+
}
54+
4155
if (!zc || auto_zc) {
4256
ublk_io_alloc_sqes(t, sqe, 1);
4357
if (!sqe[0])
4458
return -ENOMEM;
4559

4660
io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/,
4761
addr,
48-
iod->nr_sectors << 9,
49-
iod->start_sector << 9);
62+
len,
63+
offset);
5064
if (auto_zc)
5165
sqe[0]->buf_index = tag;
5266
io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE);
5367
/* bit63 marks us as tgt io */
5468
sqe[0]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);
55-
return 1;
69+
return !!(iod->op_flags & UBLK_IO_F_INTEGRITY) + 1;
5670
}
5771

5872
ublk_io_alloc_sqes(t, sqe, 3);
@@ -63,16 +77,16 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
6377
ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1);
6478

6579
io_uring_prep_rw(op, sqe[1], ublk_get_registered_fd(q, 1) /*fds[1]*/, 0,
66-
iod->nr_sectors << 9,
67-
iod->start_sector << 9);
80+
len,
81+
offset);
6882
sqe[1]->buf_index = tag;
6983
sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK;
7084
sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);
7185

7286
io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, io->buf_index);
7387
sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1);
7488

75-
return 2;
89+
return !!(iod->op_flags & UBLK_IO_F_INTEGRITY) + 2;
7690
}
7791

7892
static int loop_queue_tgt_io(struct ublk_thread *t, struct ublk_queue *q, int tag)
@@ -119,12 +133,17 @@ static void ublk_loop_io_done(struct ublk_thread *t, struct ublk_queue *q,
119133
unsigned op = user_data_to_op(cqe->user_data);
120134
struct ublk_io *io = ublk_get_io(q, tag);
121135

122-
if (cqe->res < 0 || op != ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) {
123-
if (!io->result)
124-
io->result = cqe->res;
125-
if (cqe->res < 0)
126-
ublk_err("%s: io failed op %x user_data %lx\n",
127-
__func__, op, cqe->user_data);
136+
if (cqe->res < 0) {
137+
io->result = cqe->res;
138+
ublk_err("%s: io failed op %x user_data %lx\n",
139+
__func__, op, cqe->user_data);
140+
} else if (op != ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) {
141+
__s32 data_len = user_data_to_tgt_data(cqe->user_data)
142+
? ublk_integrity_data_len(q, cqe->res)
143+
: cqe->res;
144+
145+
if (!io->result || data_len < io->result)
146+
io->result = data_len;
128147
}
129148

130149
/* buffer register op is IOSQE_CQE_SKIP_SUCCESS */
@@ -135,9 +154,30 @@ static void ublk_loop_io_done(struct ublk_thread *t, struct ublk_queue *q,
135154
ublk_complete_io(t, q, tag, io->result);
136155
}
137156

157+
static int ublk_loop_memset_file(int fd, __u8 byte, size_t len)
158+
{
159+
off_t offset = 0;
160+
__u8 buf[4096];
161+
162+
memset(buf, byte, sizeof(buf));
163+
while (len) {
164+
int ret = pwrite(fd, buf, min(len, sizeof(buf)), offset);
165+
166+
if (ret < 0)
167+
return -errno;
168+
if (!ret)
169+
return -EIO;
170+
171+
len -= ret;
172+
offset += ret;
173+
}
174+
return 0;
175+
}
176+
138177
static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
139178
{
140179
unsigned long long bytes;
180+
unsigned long blocks;
141181
int ret;
142182
struct ublk_params p = {
143183
.types = UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DMA_ALIGN,
@@ -154,23 +194,39 @@ static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
154194
},
155195
};
156196

197+
ublk_set_integrity_params(ctx, &p);
157198
if (ctx->auto_zc_fallback) {
158199
ublk_err("%s: not support auto_zc_fallback\n", __func__);
159200
return -EINVAL;
160201
}
161-
if (ctx->metadata_size) {
162-
ublk_err("%s: integrity not supported\n", __func__);
163-
return -EINVAL;
164-
}
165202

203+
/* Use O_DIRECT only for data file */
166204
ret = backing_file_tgt_init(dev, 1);
167205
if (ret)
168206
return ret;
169207

170-
if (dev->tgt.nr_backing_files != 1)
208+
/* Expect a second file for integrity data */
209+
if (dev->tgt.nr_backing_files != 1 + !!ctx->metadata_size)
171210
return -EINVAL;
172211

173-
bytes = dev->tgt.backing_file_size[0];
212+
blocks = dev->tgt.backing_file_size[0] >> p.basic.logical_bs_shift;
213+
if (ctx->metadata_size) {
214+
unsigned long metadata_blocks =
215+
dev->tgt.backing_file_size[1] / ctx->metadata_size;
216+
unsigned long integrity_len;
217+
218+
/* Ensure both data and integrity data fit in backing files */
219+
blocks = min(blocks, metadata_blocks);
220+
integrity_len = blocks * ctx->metadata_size;
221+
/*
222+
* Initialize PI app tag and ref tag to 0xFF
223+
* to disable bio-integrity-auto checks
224+
*/
225+
ret = ublk_loop_memset_file(dev->fds[2], 0xFF, integrity_len);
226+
if (ret)
227+
return ret;
228+
}
229+
bytes = blocks << p.basic.logical_bs_shift;
174230
dev->tgt.dev_size = bytes;
175231
p.basic.dev_sectors = bytes >> 9;
176232
dev->tgt.params = p;

0 commit comments

Comments
 (0)