From nobody Sat Feb 7 10:44:45 2026 Received: from mail-pl1-f225.google.com (mail-pl1-f225.google.com [209.85.214.225]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD06242A589 for ; Thu, 8 Jan 2026 09:20:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.225 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767864024; cv=none; b=mNeossCJ8uM0ukZz/2CywpNGt1yZ+wAQAyHd3fETAWRQG7bREtHfzndMtvnjzJJGdI5GF+t5PV6OjfyL0IThAhF9EU25Q/NsUug5DuI/T/DrfGy0a/fv47wkHGKvRX3uxVhMxc2v+jswyU/6D+BsTYostSo7LIEbGjumn8zCZhc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767864024; c=relaxed/simple; bh=1aDaPI2v4Zeo3O7crQSGBiyCnNWXJlIc6pmuFCCIr2A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MqZMYHdEO90TvGsjqPd4ZlFeXoYx4uQITce6pWOA6V3aW9JWjn/+1a6Fa/PSfWaIM6tXJOa0C5hGZbibD2bcNBdWFTQhJjNU5Koms9vFrYy0CZGjngTpJDyhfdwDgr6cAe6MSbZp2EMUujXRCJFjIVbFq+NF/rEo/2/o1Zb9Ha0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=purestorage.com; spf=fail smtp.mailfrom=purestorage.com; dkim=pass (2048-bit key) header.d=purestorage.com header.i=@purestorage.com header.b=MkaV3Y+5; arc=none smtp.client-ip=209.85.214.225 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=purestorage.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=purestorage.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=purestorage.com header.i=@purestorage.com header.b="MkaV3Y+5" Received: by mail-pl1-f225.google.com with SMTP id d9443c01a7336-2a3051bc432so2656735ad.3 for ; Thu, 08 Jan 2026 01:20:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=purestorage.com; s=google2022; t=1767863998; x=1768468798; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UaTLRDW4Ok0YQzJQSdBnJS8VxZrTaE64GOqPLwd4Ilc=; b=MkaV3Y+53v5MSqIJ18b/X+HkU+ZUsCyvrP+tJLpAEDJaNnJ8dIv/Dwwr2E9pkl0HSp xx3OIIkflJbJYuNugqmNeylb8mfIAFHqsxGUxMypZ9hurizBOXOBCQVvcVHLuqYBqBNx eiMDXVeBTMFMX5kujBQBk7xBjyxx5+ghIi7TfbGC8+6PXL4SwYSHyW3YRI1J6OX+dxyy BdWBz0G/QZiB3j+ecgtX5TtUTa+z/FvgiW7nrrrK0vWmTCkQm94eXLdZVSwCXyic8Ozd wS1jLUkse8u7GFTeMq78QcJOkIji2JZbGlR5vD6z3KB0OqCNzNEW8qQE6s7EnCuywsXv keRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1767863998; x=1768468798; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=UaTLRDW4Ok0YQzJQSdBnJS8VxZrTaE64GOqPLwd4Ilc=; b=o/Et68ko0RMW21BKc7vNClvA/SY5lnDxSJHy9ZbTa+YkZA5xnkxEIR0D8osgcwOZ0N ShKaSXOpvtO9A6gr1lGiEZ68AFqrFKejs4g8B4nw9hvsBKh11jvSUihUXcmyeMLV9XJu Cio+eWFm5HAIdw47SU60+mCeR96Oxwqed4CYHORVE9nD7djus4YNTzPM+y0wzrHX9AXl b8O46cwwq5yiBNOmt3vkod7OqsrkqZY7K//gx44DmGATYyglUct/cUfYhpqCbEZApQoe OVqv2LE1IC+jDGs1Er6p5Pw14+hJXcNO42OIY9sy2EeVIVByEa4lQk1wfxIAJraFIIMS /70Q== X-Forwarded-Encrypted: i=1; AJvYcCXQHOtHr6cSgQbpc+C2ikQh5nbEZJ86NkG5P2k7C8Oo5htYlVIf1qhVLdOM4GvBuem6nS4HlIGMQTN+rkk=@vger.kernel.org X-Gm-Message-State: AOJu0Yz39qID3Jyp05fB+H0eRA0kg217SPAlJlBCMXYj6vWRni38rry9 wKVA6FmTgMAVDjX3XQIhjU0WtetP/78ZaxcMWIWDgSyjNxLa4fH60zbx9Trj4+rKba0STGl199j eRgN/nYQP9dfrk03t/kynDp8YoObY+juKtHV4RU8oPDVN7k9GUkjY X-Gm-Gg: AY/fxX47yyNFBxe+ycBnL2HCTOXw+3UgQWE0+kazpu/zfxYlmq2CJ2e5pv4rFWnD8D8 s/6g8xoGkSaxUdESw8DE1/bdXrnZWdqIN2cckgTAaNZ35I+CLoBQDhh7TAT1vSIvL/8skBB7nCg 4ur15HaoDpNwJg0ySl6XBEZLxodg18GQGb8d9tjDFBB9XBgH4nGRv7q22ez0T2MADE9+vPbNz9L DB6hfEx9l6bqbR0xtwIqtTMM0Qqp94wz+kwwacW3cXgosbb+dTs/uFSbeu/go4ohCsA9VZThmeb g8iOGjHB4431zhblmy0O65drWhMob2gkiEaSjo0omUW2dXduecll6DS5/jJC/lQqK/gYRH9fiUz gHDIP3HwHmYSAcku9yNtOUcX2pnw= X-Google-Smtp-Source: AGHT+IGYbi8ogzRt0rfT3w7cbk4NGI6oUYGnuR0yEq1AIRmedSlfeRJenAraivQu7TSxjwkdAacjLt1JQOC7 X-Received: by 2002:a17:902:ce03:b0:2a0:ccee:b356 with SMTP id d9443c01a7336-2a3ee4252f2mr39375565ad.1.1767863998032; Thu, 08 Jan 2026 01:19:58 -0800 (PST) Received: from c7-smtp-2023.dev.purestorage.com ([2620:125:9017:12:36:3:5:0]) by smtp-relay.gmail.com with ESMTPS id d9443c01a7336-2a3e3cc4ab4sm8606025ad.44.2026.01.08.01.19.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Jan 2026 01:19:58 -0800 (PST) X-Relaying-Domain: purestorage.com Received: from dev-csander.dev.purestorage.com (dev-csander.dev.purestorage.com [10.112.6.120]) by c7-smtp-2023.dev.purestorage.com (Postfix) with ESMTP id 7767C34223B; Thu, 8 Jan 2026 02:19:57 -0700 (MST) Received: by dev-csander.dev.purestorage.com (Postfix, from userid 1557716354) id 72926E42F2C; Thu, 8 Jan 2026 02:19:57 -0700 (MST) From: Caleb Sander Mateos To: Ming Lei , Jens Axboe , Shuah Khan Cc: linux-block@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, Stanley Zhang , Uday Shankar , "Martin K . Petersen" , Caleb Sander Mateos Subject: [PATCH v4 17/19] selftests: ublk: add integrity data support to loop target Date: Thu, 8 Jan 2026 02:19:45 -0700 Message-ID: <20260108091948.1099139-18-csander@purestorage.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20260108091948.1099139-1-csander@purestorage.com> References: <20260108091948.1099139-1-csander@purestorage.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Ming Lei --- tools/testing/selftests/ublk/file_backed.c | 92 +++++++++++++++++----- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/tools/testing/selftests/ublk/file_backed.c b/tools/testing/sel= ftests/ublk/file_backed.c index db4c176a4f28..c3ce5ff72422 100644 --- a/tools/testing/selftests/ublk/file_backed.c +++ b/tools/testing/selftests/ublk/file_backed.c @@ -33,48 +33,62 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, = struct ublk_queue *q, unsigned ublk_op =3D ublksrv_get_op(iod); unsigned zc =3D ublk_queue_use_zc(q); unsigned auto_zc =3D ublk_queue_use_auto_zc(q); enum io_uring_op op =3D ublk_to_uring_op(iod, zc | auto_zc); struct ublk_io *io =3D ublk_get_io(q, tag); + __u64 offset =3D iod->start_sector << 9; + __u32 len =3D iod->nr_sectors << 9; struct io_uring_sqe *sqe[3]; void *addr =3D io->buf_addr; =20 + if (iod->op_flags & UBLK_IO_F_INTEGRITY) { + ublk_io_alloc_sqes(t, sqe, 1); + /* Use second backing file for integrity data */ + io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 2), + io->integrity_buf, + ublk_integrity_len(q, len), + ublk_integrity_len(q, offset)); + sqe[0]->flags =3D IOSQE_FIXED_FILE; + /* tgt_data =3D 1 indicates integrity I/O */ + sqe[0]->user_data =3D build_user_data(tag, ublk_op, 1, q->q_id, 1); + } + if (!zc || auto_zc) { ublk_io_alloc_sqes(t, sqe, 1); if (!sqe[0]) return -ENOMEM; =20 io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/, addr, - iod->nr_sectors << 9, - iod->start_sector << 9); + len, + offset); if (auto_zc) sqe[0]->buf_index =3D tag; io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE); /* bit63 marks us as tgt io */ sqe[0]->user_data =3D build_user_data(tag, ublk_op, 0, q->q_id, 1); - return 1; + return !!(iod->op_flags & UBLK_IO_F_INTEGRITY) + 1; } =20 ublk_io_alloc_sqes(t, sqe, 3); =20 io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index); sqe[0]->flags |=3D IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; sqe[0]->user_data =3D build_user_data(tag, ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); =20 io_uring_prep_rw(op, sqe[1], ublk_get_registered_fd(q, 1) /*fds[1]*/, 0, - iod->nr_sectors << 9, - iod->start_sector << 9); + len, + offset); sqe[1]->buf_index =3D tag; sqe[1]->flags |=3D IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK; sqe[1]->user_data =3D build_user_data(tag, ublk_op, 0, q->q_id, 1); =20 io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, io->buf_index); sqe[2]->user_data =3D build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op)= , 0, q->q_id, 1); =20 - return 2; + return !!(iod->op_flags & UBLK_IO_F_INTEGRITY) + 2; } =20 static int loop_queue_tgt_io(struct ublk_thread *t, struct ublk_queue *q, = int tag) { const struct ublksrv_io_desc *iod =3D ublk_get_iod(q, tag); @@ -117,29 +131,55 @@ static void ublk_loop_io_done(struct ublk_thread *t, = struct ublk_queue *q, { unsigned tag =3D user_data_to_tag(cqe->user_data); unsigned op =3D user_data_to_op(cqe->user_data); struct ublk_io *io =3D ublk_get_io(q, tag); =20 - if (cqe->res < 0 || op !=3D ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) { - if (!io->result) - io->result =3D cqe->res; - if (cqe->res < 0) - ublk_err("%s: io failed op %x user_data %lx\n", - __func__, op, cqe->user_data); + if (cqe->res < 0) { + io->result =3D cqe->res; + ublk_err("%s: io failed op %x user_data %lx\n", + __func__, op, cqe->user_data); + } else if (op !=3D ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) { + __s32 data_len =3D user_data_to_tgt_data(cqe->user_data) + ? ublk_integrity_data_len(q, cqe->res) + : cqe->res; + + if (!io->result || data_len < io->result) + io->result =3D data_len; } =20 /* buffer register op is IOSQE_CQE_SKIP_SUCCESS */ if (op =3D=3D ublk_cmd_op_nr(UBLK_U_IO_REGISTER_IO_BUF)) io->tgt_ios +=3D 1; =20 if (ublk_completed_tgt_io(t, q, tag)) ublk_complete_io(t, q, tag, io->result); } =20 +static int ublk_loop_memset_file(int fd, __u8 byte, size_t len) +{ + off_t offset =3D 0; + __u8 buf[4096]; + + memset(buf, byte, sizeof(buf)); + while (len) { + int ret =3D pwrite(fd, buf, min(len, sizeof(buf)), offset); + + if (ret < 0) + return -errno; + if (!ret) + return -EIO; + + len -=3D ret; + offset +=3D ret; + } + return 0; +} + static int ublk_loop_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *= dev) { unsigned long long bytes; + unsigned long blocks; int ret; struct ublk_params p =3D { .types =3D UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DMA_ALIGN, .basic =3D { .attrs =3D UBLK_ATTR_VOLATILE_CACHE, @@ -152,27 +192,43 @@ static int ublk_loop_tgt_init(const struct dev_ctx *c= tx, struct ublk_dev *dev) .dma =3D { .alignment =3D 511, }, }; =20 + ublk_set_integrity_params(ctx, &p); if (ctx->auto_zc_fallback) { ublk_err("%s: not support auto_zc_fallback\n", __func__); return -EINVAL; } - if (ctx->metadata_size) { - ublk_err("%s: integrity not supported\n", __func__); - return -EINVAL; - } =20 + /* Use O_DIRECT only for data file */ ret =3D backing_file_tgt_init(dev, 1); if (ret) return ret; =20 - if (dev->tgt.nr_backing_files !=3D 1) + /* Expect a second file for integrity data */ + if (dev->tgt.nr_backing_files !=3D 1 + !!ctx->metadata_size) return -EINVAL; =20 - bytes =3D dev->tgt.backing_file_size[0]; + blocks =3D dev->tgt.backing_file_size[0] >> p.basic.logical_bs_shift; + if (ctx->metadata_size) { + unsigned long metadata_blocks =3D + dev->tgt.backing_file_size[1] / ctx->metadata_size; + unsigned long integrity_len; + + /* Ensure both data and integrity data fit in backing files */ + blocks =3D min(blocks, metadata_blocks); + integrity_len =3D blocks * ctx->metadata_size; + /* + * Initialize PI app tag and ref tag to 0xFF + * to disable bio-integrity-auto checks + */ + ret =3D ublk_loop_memset_file(dev->fds[2], 0xFF, integrity_len); + if (ret) + return ret; + } + bytes =3D blocks << p.basic.logical_bs_shift; dev->tgt.dev_size =3D bytes; p.basic.dev_sectors =3D bytes >> 9; dev->tgt.params =3D p; =20 return 0; --=20 2.45.2