aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2024-03-26 21:12:32 -0600
committerJens Axboe <axboe@kernel.dk>2024-03-26 21:12:32 -0600
commitbaeaccd5ae5f1a637a5fb8f4df15e9ee211711dc (patch)
tree2573c80f0fb72fc12915869929b3fe46dfedb4ef
parenta8e5f9583ae7383bcd9edfed319c9480221514ad (diff)
parentf03becf5d7c1bc3fd9a426ba8b013346847f8b9e (diff)
downloadliburing-baeaccd5ae5f1a637a5fb8f4df15e9ee211711dc.tar.gz
Merge branch 'memory-ordering-and-atomicity' of https://github.com/shachaf/liburing
* 'memory-ordering-and-atomicity' of https://github.com/shachaf/liburing: Fix memory ordering/atomic access
-rw-r--r--src/include/liburing.h6
-rw-r--r--src/queue.c20
-rw-r--r--test/helpers.c20
3 files changed, 20 insertions, 26 deletions
diff --git a/src/include/liburing.h b/src/include/liburing.h
index c8701e72..3e472982 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -1217,7 +1217,7 @@ IOURINGINLINE void io_uring_prep_ftruncate(struct io_uring_sqe *sqe,
*/
IOURINGINLINE unsigned io_uring_sq_ready(const struct io_uring *ring)
{
- unsigned khead = *ring->sq.khead;
+ unsigned khead;
/*
* Without a barrier, we could miss an update and think the SQ wasn't
@@ -1226,6 +1226,8 @@ IOURINGINLINE unsigned io_uring_sq_ready(const struct io_uring *ring)
*/
if (ring->flags & IORING_SETUP_SQPOLL)
khead = io_uring_smp_load_acquire(ring->sq.khead);
+ else
+ khead = *ring->sq.khead;
/* always use real head, to avoid losing sync for short submit */
return ring->sq.sqe_tail - khead;
@@ -1412,7 +1414,7 @@ IOURINGINLINE struct io_uring_sqe *_io_uring_get_sqe(struct io_uring *ring)
if (ring->flags & IORING_SETUP_SQE128)
shift = 1;
if (!(ring->flags & IORING_SETUP_SQPOLL))
- head = IO_URING_READ_ONCE(*sq->khead);
+ head = *sq->khead;
else
head = io_uring_smp_load_acquire(sq->khead);
diff --git a/src/queue.c b/src/queue.c
index b784b10c..c4360614 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -212,22 +212,18 @@ static unsigned __io_uring_flush_sq(struct io_uring *ring)
* Ensure kernel sees the SQE updates before the tail update.
*/
if (!(ring->flags & IORING_SETUP_SQPOLL))
- IO_URING_WRITE_ONCE(*sq->ktail, tail);
+ *sq->ktail = tail;
else
io_uring_smp_store_release(sq->ktail, tail);
}
/*
- * This _may_ look problematic, as we're not supposed to be reading
- * SQ->head without acquire semantics. When we're in SQPOLL mode, the
- * kernel submitter could be updating this right now. For non-SQPOLL,
- * task itself does it, and there's no potential race. But even for
- * SQPOLL, the load is going to be potentially out-of-date the very
- * instant it's done, regardless or whether or not it's done
- * atomically. Worst case, we're going to be over-estimating what
- * we can submit. The point is, we need to be able to deal with this
- * situation regardless of any perceived atomicity.
- */
- return tail - *sq->khead;
+ * This load needs to be atomic, since sq->khead is written concurrently
+ * by the kernel, but it doesn't need to be load_acquire, since the
+ * kernel doesn't store to the submission queue; it advances khead just
+ * to indicate that it's finished reading the submission queue entries
+ * so they're available for us to write to.
+ */
+ return tail - IO_URING_READ_ONCE(*sq->khead);
}
/*
diff --git a/test/helpers.c b/test/helpers.c
index 24fd4ce3..779347f6 100644
--- a/test/helpers.c
+++ b/test/helpers.c
@@ -286,22 +286,18 @@ unsigned __io_uring_flush_sq(struct io_uring *ring)
* Ensure kernel sees the SQE updates before the tail update.
*/
if (!(ring->flags & IORING_SETUP_SQPOLL))
- IO_URING_WRITE_ONCE(*sq->ktail, tail);
+ *sq->ktail = tail;
else
io_uring_smp_store_release(sq->ktail, tail);
}
/*
- * This _may_ look problematic, as we're not supposed to be reading
- * SQ->head without acquire semantics. When we're in SQPOLL mode, the
- * kernel submitter could be updating this right now. For non-SQPOLL,
- * task itself does it, and there's no potential race. But even for
- * SQPOLL, the load is going to be potentially out-of-date the very
- * instant it's done, regardless or whether or not it's done
- * atomically. Worst case, we're going to be over-estimating what
- * we can submit. The point is, we need to be able to deal with this
- * situation regardless of any perceived atomicity.
- */
- return tail - *sq->khead;
+ * This load needs to be atomic, since sq->khead is written concurrently
+ * by the kernel, but it doesn't need to be load_acquire, since the
+ * kernel doesn't store to the submission queue; it advances khead just
+ * to indicate that it's finished reading the submission queue entries
+ * so they're available for us to write to.
+ */
+ return tail - IO_URING_READ_ONCE(*sq->khead);
}
/*