libublksrv: ABI change: re-arrange fields of ublksrv_tgt_type and cache tgt_ops in ublksrv_queue

Re-arrange fields of ublksrv_tgt_type, so that fields referred in fast
path are put into the 1st cache line.

Also add one field to ublksrv_queue, so that tgt_ops can be cached,
given it is always referred in fast path.

This change breaks ABI of libublksrv, so please update libublksrv.

It is planned to stabilize libublksrv ABI when releasing v1.0, and hope it
can be done soon.

Signed-off-by: Ming Lei <[email protected]>
diff --git a/include/ublksrv.h b/include/ublksrv.h
index 445f8d5..8ad5364 100644
--- a/include/ublksrv.h
+++ b/include/ublksrv.h
@@ -161,6 +161,9 @@
 	/* eventfd */
 	int efd;
 
+	/* cache tgt ops */
+	const struct ublksrv_tgt_type *tgt_ops;
+
 	/*
 	 * ring for submit io command to ublk driver, can only be issued
 	 * from ublksrv daemon.
@@ -169,8 +172,8 @@
 	 */
 	struct io_uring ring;
 
-	unsigned  tid;
 	struct ublksrv_dev *dev;
+	unsigned  tid;
 
 #define UBLKSRV_NR_CTX_BATCH 4
 	int nr_ctxs;
@@ -202,20 +205,6 @@
 };
 
 struct ublksrv_tgt_type {
-	int  type;
-	unsigned ublk_flags;	//flags required for ublk driver
-	unsigned ublksrv_flags;	//flags required for ublksrv
-	int extra_ios;		//extra io slots allocated for handling
-				//target specific IOs, such as meta io
-	const char *name;
-
-	/*
-	 * initialize this new target, argc/argv includes target specific
-	 * command line parameters
-	 */
-	int (*init_tgt)(struct ublksrv_dev *, int type, int argc,
-			char *argv[]);
-
 	/*
 	 * One IO request comes from /dev/ublkbN, so notify target code
 	 * for handling the IO. Inside target code, the IO can be handled
@@ -277,11 +266,25 @@
 	 */
 	void (*usage_for_add)(void);
 
+	/*
+	 * initialize this new target, argc/argv includes target specific
+	 * command line parameters
+	 */
+	int (*init_tgt)(struct ublksrv_dev *, int type, int argc,
+			char *argv[]);
+
 	/* deinitialize this target */
 	void (*deinit_tgt)(struct ublksrv_dev *);
 
 	void *(*alloc_io_buf)(struct ublksrv_queue *q, int tag, int size);
 	void (*free_io_buf)(struct ublksrv_queue *q, void *buf, int tag);
+
+	int  type;
+	unsigned ublk_flags;	//flags required for ublk driver
+	unsigned ublksrv_flags;	//flags required for ublksrv
+	int extra_ios;		//extra io slots allocated for handling
+				//target specific IOs, such as meta io
+	const char *name;
 };
 
 struct ublksrv_dev {
diff --git a/lib/ublksrv.c b/lib/ublksrv.c
index 1ee655d..2b39258 100644
--- a/lib/ublksrv.c
+++ b/lib/ublksrv.c
@@ -536,6 +536,7 @@
 			sizeof(struct ublk_io) * nr_ios);
 	dev->__queues[q_id] = q;
 
+	q->tgt_ops = dev->tgt.ops;	//cache ops for fast path
 	q->dev = dev;
 	q->state = 0;
 	q->q_id = q_id;
@@ -738,8 +739,8 @@
 }
 
 /* Be careful, target io may not have one ublk_io associated with  */
-static inline void ublksrv_handle_tgt_cqe(struct ublksrv_tgt_info *tgt,
-	struct ublksrv_queue *q, struct io_uring_cqe *cqe)
+static inline void ublksrv_handle_tgt_cqe(struct ublksrv_queue *q,
+		struct io_uring_cqe *cqe)
 {
 	unsigned tag = user_data_to_tag(cqe->user_data);
 
@@ -752,11 +753,11 @@
 	}
 
 	if (is_eventfd_io(cqe->user_data)) {
-		if (tgt->ops->handle_event)
-			tgt->ops->handle_event(q);
+		if (q->tgt_ops->handle_event)
+			q->tgt_ops->handle_event(q);
 	} else {
-		if (tgt->ops->tgt_io_done)
-			tgt->ops->tgt_io_done(q, cqe);
+		if (q->tgt_ops->tgt_io_done)
+			q->tgt_ops->tgt_io_done(q, cqe);
 	}
 }
 
@@ -764,9 +765,6 @@
 		struct io_uring_cqe *cqe, void *data)
 {
 	struct ublksrv_queue *q = container_of(r, struct ublksrv_queue, ring);
-	struct ublksrv_dev *dev = q->dev;
-	const struct ublksrv_ctrl_dev *ctrl_dev = dev->ctrl_dev;
-	struct ublksrv_tgt_info *tgt = &dev->tgt;
 	unsigned tag = user_data_to_tag(cqe->user_data);
 	unsigned cmd_op = user_data_to_op(cqe->user_data);
 	int fetch = (cqe->res != UBLK_IO_RES_ABORT) &&
@@ -781,7 +779,7 @@
 
 	/* Don't retrieve io in case of target io */
 	if (is_target_io(cqe->user_data)) {
-		ublksrv_handle_tgt_cqe(tgt, q, cqe);
+		ublksrv_handle_tgt_cqe(q, cqe);
 		return;
 	}
 
@@ -800,7 +798,7 @@
 	 * daemon can poll on both two rings.
 	 */
 	if (cqe->res == UBLK_IO_RES_OK) {
-		tgt->ops->handle_io_async(q, tag);
+		q->tgt_ops->handle_io_async(q, tag);
 	} else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) {
 		io->flags |= UBLKSRV_NEED_GET_DATA | UBLKSRV_IO_FREE;
 		ublksrv_queue_io_cmd(q, io, tag);
@@ -889,8 +887,8 @@
 	reapped = ublksrv_reap_events_uring(&q->ring);
 	ublksrv_submit_aio_batch(q);
 
-	if (q->dev->tgt.ops->handle_io_background)
-		q->dev->tgt.ops->handle_io_background(q,
+	if (q->tgt_ops->handle_io_background)
+		q->tgt_ops->handle_io_background(q,
 				io_uring_sq_ready(&q->ring));
 
 	ublksrv_log(LOG_INFO, "submit result %d, reapped %d stop %d idle %d",
diff --git a/tgt_loop.cpp b/tgt_loop.cpp
index 72bf2d3..ee068de 100644
--- a/tgt_loop.cpp
+++ b/tgt_loop.cpp
@@ -387,15 +387,15 @@
 }
 
 struct ublksrv_tgt_type  loop_tgt_type = {
-	.type	= UBLKSRV_TGT_TYPE_LOOP,
-	.ublksrv_flags  = UBLKSRV_F_NEED_EVENTFD,
-	.name	=  "loop",
-	.init_tgt = loop_init_tgt,
 	.handle_io_async = loop_handle_io_async,
 	.tgt_io_done = loop_tgt_io_done,
 	.handle_event = loop_handle_event,
 	.usage_for_add	=  loop_usage_for_add,
+	.init_tgt = loop_init_tgt,
 	.deinit_tgt	=  loop_deinit_tgt,
+	.type	= UBLKSRV_TGT_TYPE_LOOP,
+	.ublksrv_flags  = UBLKSRV_F_NEED_EVENTFD,
+	.name	=  "loop",
 };
 
 static void tgt_loop_init() __attribute__((constructor));
diff --git a/tgt_null.cpp b/tgt_null.cpp
index 751f771..a3ef799 100644
--- a/tgt_null.cpp
+++ b/tgt_null.cpp
@@ -69,10 +69,10 @@
 }
 
 struct ublksrv_tgt_type  null_tgt_type = {
+	.handle_io_async = null_handle_io_async,
+	.init_tgt = null_init_tgt,
 	.type	= UBLKSRV_TGT_TYPE_NULL,
 	.name	=  "null",
-	.init_tgt = null_init_tgt,
-	.handle_io_async = null_handle_io_async,
 };
 
 static void tgt_null_init() __attribute__((constructor));