All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mohamed Khalfella <mkhalfella@purestorage.com>
To: linux-block@vger.kernel.org
Cc: shinichiro.kawasaki@wdc.com, Keith Busch <kbusch@kernel.org>,
	Jens Axboe <axboe@kernel.dk>, Christoph Hellwig <hch@lst.de>,
	Sagi Grimberg <sagi@grimberg.me>, Hannes Reinecke <hare@suse.de>,
	John Meneghini <jmeneghi@redhat.com>,
	Jesse Taube <jtaubepe@redhat.com>,
	Randy Jennings <randyj@purestorage.com>,
	Dhaval Giani <dgiani@purestorage.com>,
	Mohamed Khalfella <mkhalfella@purestorage.com>
Subject: [PATCH blktests 2/5] src/miniublk: add IO delay injection
Date: Wed, 16 Sep 2026 20:06:22 -0600	[thread overview]
Message-ID: <20260917020752.1672578-3-mkhalfella@purestorage.com> (raw)
In-Reply-To: <20260917020752.1672578-1-mkhalfella@purestorage.com>

Tests that exercise error handling in a block driver stacked on ublk need
a way to hold an IO back without failing it. Add an INJECT_DELAY control
command which delays a given number of reads or writes before they are
issued to the target.

The delay is an io_uring timeout on the same ring rather than a sleep in
the queue thread, marked with UBLK_DELAY_MARK in tgt_data to tell its
completion apart from a real IO. It is counted in io_inflight to be
waited for.

Signed-off-by: Mohamed Khalfella <mkhalfella@purestorage.com>
---
 src/miniublk.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 109 insertions(+), 2 deletions(-)

diff --git a/src/miniublk.c b/src/miniublk.c
index 30bd7f5..8dc2a56 100644
--- a/src/miniublk.c
+++ b/src/miniublk.c
@@ -19,6 +19,7 @@
 #include <limits.h>
 #include <string.h>
 #include <errno.h>
+#include <stdatomic.h>
 #include <sys/syscall.h>
 #include <sys/mman.h>
 #include <sys/ioctl.h>
@@ -49,7 +50,8 @@
 #define UBLK_CTRL_MSG_MAGIC	0x6b6c6275
 
 enum {
-	UBLK_CTRL_CMD_PING	= 0,
+	UBLK_CTRL_CMD_PING		= 0,
+	UBLK_CTRL_CMD_INJECT_DELAY	= 1,
 };
 
 struct ublk_ctrl_msg {
@@ -84,6 +86,8 @@ struct ublk_io {
 	unsigned int flags;
 
 	unsigned int result;
+
+	struct __kernel_timespec delay_ts;
 };
 
 struct ublk_tgt_ops {
@@ -136,6 +140,10 @@ struct ublk_dev {
 	/* daemon control channel, 0 means it is disabled */
 	int ctrl_port;
 	int ctrl_sock;
+
+	atomic_int inject_op;
+	atomic_int inject_secs;
+	atomic_int inject_count;
 };
 
 #ifndef offsetof
@@ -190,6 +198,13 @@ static inline unsigned int user_data_to_op(__u64 user_data)
 	return (user_data >> 16) & 0xff;
 }
 
+#define UBLK_DELAY_MARK		1
+
+static inline unsigned int user_data_to_tgt_data(__u64 user_data)
+{
+	return (user_data >> 24) & 0xff;
+}
+
 static void ublk_err(const char *fmt, ...)
 {
 	va_list ap;
@@ -509,6 +524,7 @@ static struct ublk_dev *ublk_ctrl_init()
 
 	dev->use_ioctl = true; /* use ioctl opcodes by default */
 	dev->ctrl_sock = -1;
+	dev->inject_op = -1;
 
 	dev->ctrl_fd = open(CTRL_DEV, O_RDWR);
 	if (dev->ctrl_fd < 0) {
@@ -637,6 +653,26 @@ static int ublk_ctrl_msg_handle(struct ublk_dev *dev,
 	case UBLK_CTRL_CMD_PING:
 		rsp->data[0] = dev->dev_info.dev_id;
 		return 0;
+	case UBLK_CTRL_CMD_INJECT_DELAY: {
+		int op = req->data[0];
+		int secs = req->data[1];
+		int count = req->data[2];
+
+		if (op != UBLK_IO_OP_READ && op != UBLK_IO_OP_WRITE)
+			return -EINVAL;
+		if (secs <= 0 || count <= 0)
+			return -EINVAL;
+
+		dev->inject_op = op;
+		dev->inject_secs = secs;
+		/* arm the count last, the request becomes visible at once */
+		dev->inject_count = count;
+
+		ublk_log("dev %d: delay next %d %s io(s) by %d secs\n",
+				dev->dev_info.dev_id, count,
+				op == UBLK_IO_OP_READ ? "read" : "write", secs);
+		return 0;
+	}
 	default:
 		ublk_dbg(UBLK_DBG_DEV, "%s: unknown control command %u\n",
 				__func__, req->cmd);
@@ -773,6 +809,66 @@ static void ublk_dev_unprep(struct ublk_dev *dev)
 	close(dev->fds[0]);
 }
 
+static int ublk_inject_claim(struct ublk_queue *q, unsigned int ublk_op)
+{
+	struct ublk_dev *dev = q->dev;
+	int secs;
+
+	if (dev->inject_count <= 0)
+		return 0;
+
+	if (dev->inject_op != (int)ublk_op)
+		return 0;
+
+	secs = dev->inject_secs;
+	if (secs <= 0)
+		return 0;
+
+	if (atomic_fetch_sub(&dev->inject_count, 1) <= 0) {
+		/* another queue took the last one */
+		atomic_fetch_add(&dev->inject_count, 1);
+		return 0;
+	}
+
+	return secs;
+}
+
+static int ublk_delay_io(struct ublk_queue *q, int tag)
+{
+	const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag);
+	unsigned int ublk_op = ublksrv_get_op(iod);
+	struct ublk_io *io = &q->ios[tag];
+	struct io_uring_sqe *sqe;
+	int secs;
+
+	secs = ublk_inject_claim(q, ublk_op);
+	if (!secs)
+		return 0;
+
+	sqe = io_uring_get_sqe(&q->ring);
+	if (!sqe) {
+		ublk_err("%s: run out of sqe %d, tag %d\n", __func__,
+				q->q_id, tag);
+		atomic_fetch_add(&q->dev->inject_count, 1);
+		return 0;
+	}
+
+	io->delay_ts.tv_sec = secs;
+	io->delay_ts.tv_nsec = 0;
+
+	io_uring_prep_timeout(sqe, &io->delay_ts, 0, 0);
+	/* bit63 marks us as tgt io, tgt_data marks us as the delay timeout */
+	sqe->user_data = build_user_data(tag, ublk_op, UBLK_DELAY_MARK, 1);
+
+	q->io_inflight++;
+
+	ublk_dbg(UBLK_DBG_IO, "%s: dev %d q %d tag %d: delay %s io by %d secs\n",
+			__func__, q->dev->dev_info.dev_id, q->q_id, tag,
+			ublk_op == UBLK_IO_OP_READ ? "read" : "write", secs);
+
+	return 1;
+}
+
 static int ublk_queue_io_cmd(struct ublk_queue *q,
 		struct ublk_io *io, unsigned tag)
 {
@@ -896,6 +992,16 @@ static inline void ublksrv_handle_tgt_cqe(struct ublk_queue *q,
 {
 	unsigned tag = user_data_to_tag(cqe->user_data);
 
+	/*
+	 * A delay timeout completes with -ETIME. It is not a real io, so hand
+	 * the tag back to queue_io() instead of completing it.
+	 */
+	if (user_data_to_tgt_data(cqe->user_data) == UBLK_DELAY_MARK) {
+		q->io_inflight--;
+		q->tgt_ops->queue_io(q, tag);
+		return;
+	}
+
 	if (cqe->res < 0 && cqe->res != -EAGAIN)
 		ublk_err("%s: failed tgt io: res %d qid %u tag %u, cmd_op %u\n",
 			__func__, cqe->res, q->q_id,
@@ -937,7 +1043,8 @@ static void ublk_handle_cqe(struct io_uring *r,
 
 	if (cqe->res == UBLK_IO_RES_OK) {
 		ublk_assert(tag < q->q_depth);
-		q->tgt_ops->queue_io(q, tag);
+		if (!ublk_delay_io(q, tag))
+			q->tgt_ops->queue_io(q, tag);
 	} else {
 		/*
 		 * COMMIT_REQ will be completed immediately since no fetching
-- 
2.55.0


  parent reply	other threads:[~2026-09-17  2:09 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-17  2:06 [PATCH blktests 0/5] nvme: detect ABA ghost writes on multipath fabrics Mohamed Khalfella
2026-09-17  2:06 ` [PATCH blktests 1/5] src/miniublk: add a control channel to the daemon Mohamed Khalfella
2026-09-17  2:06 ` Mohamed Khalfella [this message]
2026-09-17  2:06 ` [PATCH blktests 3/5] src/miniublk: add the inject command Mohamed Khalfella
2026-09-17  2:06 ` [PATCH blktests 4/5] src/nvme-ghost-write-detector: add an ABA ghost write detector Mohamed Khalfella
2026-09-21 18:38   ` Jesse Taube
2026-09-23 17:00     ` Mohamed Khalfella
2026-09-17  2:06 ` [PATCH blktests 5/5] nvme/070: test for ABA ghost writes on a multipath fabrics namespace Mohamed Khalfella
2026-09-22 17:07   ` Jesse Taube
2026-09-23 16:56     ` Mohamed Khalfella
2026-09-23  8:18 ` [PATCH blktests 0/5] nvme: detect ABA ghost writes on multipath fabrics Shin'ichiro Kawasaki
2026-09-23 17:02   ` Mohamed Khalfella
2026-10-02  3:03     ` Shin'ichiro Kawasaki

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260917020752.1672578-3-mkhalfella@purestorage.com \
    --to=mkhalfella@purestorage.com \
    --cc=axboe@kernel.dk \
    --cc=dgiani@purestorage.com \
    --cc=hare@suse.de \
    --cc=hch@lst.de \
    --cc=jmeneghi@redhat.com \
    --cc=jtaubepe@redhat.com \
    --cc=kbusch@kernel.org \
    --cc=linux-block@vger.kernel.org \
    --cc=randyj@purestorage.com \
    --cc=sagi@grimberg.me \
    --cc=shinichiro.kawasaki@wdc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.