Skip to content

Commit 5290dd8

Browse files
committed
Merge branch 'fixes'
Set of fixes, and a test case for one of the issues. * fixes: liburing.h: avoid OOL round trip in io_uring_peek_cqe() on empty CQ liburing.h: drop acquire ordering on CQ head load in __io_uring_peek_cqe() src/setup: explicitly request 2MB huge pages src/arch: use sysconf() for page size on aarch64/riscv64 libc builds src/setup: account SQ array in rings_size() src/sanitize: check secondary user pointers as well src/sanitize: initialize the handler table at compile time src/queue: io_uring_wait_cqes_min_timeout() should -EINVAL if unsupported liburing.h: don't return stale CQE pointer with timeout error src/arch: use prlimit64 for the rlimit syscall wrappers src/setup: account SQ array in the rings region in io_uring_alloc_huge() src/setup: restore ring fd and flags init in io_uring_queue_mmap() test/cq-peek-batch-mixed: add peek-batch test for mixed CQE rings src/queue: handle mixed CQEs in io_uring_peek_batch_cqe() src/register: fix mmap leak in io_uring_resize_rings()
2 parents eed315f + cd78157 commit 5290dd8

10 files changed

Lines changed: 391 additions & 122 deletions

File tree

src/arch/aarch64/lib.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66
#include <elf.h>
77
#include "../../syscall.h"
88

9+
#ifndef CONFIG_NOLIBC
10+
#include <unistd.h>
11+
12+
static inline long __get_page_size(void)
13+
{
14+
long ret = sysconf(_SC_PAGESIZE);
15+
16+
if (ret < 0)
17+
ret = 4096;
18+
return ret;
19+
}
20+
#else
921
static inline long __get_page_size(void)
1022
{
1123
Elf64_Off buf[2];
@@ -32,6 +44,7 @@ static inline long __get_page_size(void)
3244
__sys_close(fd);
3345
return ret;
3446
}
47+
#endif
3548

3649
static inline long get_page_size(void)
3750
{

src/arch/riscv64/lib.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77
#include <sys/auxv.h>
88
#include "../../syscall.h"
99

10+
#ifndef CONFIG_NOLIBC
11+
#include <unistd.h>
12+
13+
static inline long __get_page_size(void)
14+
{
15+
long ret = sysconf(_SC_PAGESIZE);
16+
17+
if (ret < 0)
18+
ret = 4096;
19+
return ret;
20+
}
21+
#else
1022
static inline long __get_page_size(void)
1123
{
1224
Elf64_Off buf[2];
@@ -33,6 +45,7 @@ static inline long __get_page_size(void)
3345
__sys_close(fd);
3446
return ret;
3547
}
48+
#endif
3649

3750
static inline long get_page_size(void)
3851
{

src/arch/syscall-defs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ static inline int __sys_madvise(void *addr, size_t length, int advice)
4848

4949
static inline int __sys_getrlimit(int resource, struct rlimit *rlim)
5050
{
51-
return (int) __do_syscall2(__NR_getrlimit, resource, rlim);
51+
return (int) __do_syscall4(__NR_prlimit64, 0, resource, NULL, rlim);
5252
}
5353

5454
static inline int __sys_setrlimit(int resource, const struct rlimit *rlim)
5555
{
56-
return (int) __do_syscall2(__NR_setrlimit, resource, rlim);
56+
return (int) __do_syscall4(__NR_prlimit64, 0, resource, rlim, NULL);
5757
}
5858

5959
static inline int __sys_close(int fd)

src/include/liburing.h

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,20 +1881,30 @@ IOURINGINLINE int __io_uring_peek_cqe(struct io_uring *ring,
18811881
do {
18821882
unsigned tail = io_uring_smp_load_acquire(ring->cq.ktail);
18831883

1884-
/**
1885-
* A load_acquire on the head prevents reordering with the
1886-
* cqe load below, ensuring that we see the correct cq entry.
1884+
/*
1885+
* The acquire ordering on the tail load pairs with the kernel
1886+
* side publishing CQEs, and guarantees the contents of any
1887+
* entry in [head, tail). The CQ head is only ever written by
1888+
* the application, so a plain load is sufficient.
18871889
*/
1888-
unsigned head = io_uring_smp_load_acquire(ring->cq.khead);
1890+
unsigned head = *ring->cq.khead;
18891891

18901892
cqe = NULL;
18911893
available = tail - head;
18921894
if (!available)
18931895
break;
18941896

18951897
cqe = &ring->cq.cqes[(head & mask) << shift];
1896-
if (!io_uring_skip_cqe(ring, cqe, &err))
1898+
if (!io_uring_skip_cqe(ring, cqe, &err)) {
1899+
/*
1900+
* If an error was set, the CQE was an internal
1901+
* timeout and has already been consumed - don't
1902+
* return a pointer to it.
1903+
*/
1904+
if (err)
1905+
cqe = NULL;
18971906
break;
1907+
}
18981908
cqe = NULL;
18991909
} while (1);
19001910

@@ -1912,8 +1922,20 @@ IOURINGINLINE int io_uring_peek_cqe(struct io_uring *ring,
19121922
struct io_uring_cqe **cqe_ptr)
19131923
LIBURING_NOEXCEPT
19141924
{
1915-
if (!__io_uring_peek_cqe(ring, cqe_ptr, NULL) && *cqe_ptr)
1916-
return 0;
1925+
if (!__io_uring_peek_cqe(ring, cqe_ptr, NULL)) {
1926+
if (*cqe_ptr)
1927+
return 0;
1928+
/*
1929+
* If the CQ is empty and there's nothing the kernel could
1930+
* flush to it (no IOPOLL completions to reap, no overflown
1931+
* CQEs, no pending task work), avoid the round trip into
1932+
* the full get_cqe machinery.
1933+
*/
1934+
if (!(ring->flags & IORING_SETUP_IOPOLL) &&
1935+
!(IO_URING_READ_ONCE(*ring->sq.kflags) &
1936+
(IORING_SQ_CQ_OVERFLOW | IORING_SQ_TASKRUN)))
1937+
return -EAGAIN;
1938+
}
19171939

19181940
return io_uring_wait_cqe_nr(ring, cqe_ptr, 0);
19191941
}

src/queue.c

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,24 +169,55 @@ static inline bool io_uring_peek_batch_cqe_(struct io_uring *ring,
169169
unsigned *count)
170170
{
171171
unsigned ready = io_uring_cq_ready(ring);
172-
unsigned shift;
173172
unsigned head;
174173
unsigned mask;
175174
unsigned last;
175+
unsigned nr;
176176

177177
if (!ready)
178178
return false;
179179

180-
shift = io_uring_cqe_shift(ring);
181180
head = *ring->cq.khead;
182181
mask = ring->cq.ring_mask;
183-
if (ready < *count)
184-
*count = ready;
185-
last = head + *count;
186-
for (;head != last; head++)
187-
*(cqes++) = &ring->cq.cqes[(head & mask) << shift];
182+
if (!(ring->flags & IORING_SETUP_CQE_MIXED)) {
183+
unsigned shift = io_uring_cqe_shift(ring);
188184

189-
return true;
185+
if (ready < *count)
186+
*count = ready;
187+
last = head + *count;
188+
for (;head != last; head++)
189+
*(cqes++) = &ring->cq.cqes[(head & mask) << shift];
190+
191+
return true;
192+
}
193+
194+
/*
195+
* For mixed CQE rings, CQEs take up one or two slots, and the kernel
196+
* may post skip entries to pad out the ring at wrap time. Only return
197+
* pointers to real CQEs, with *count denoting the number of CQEs.
198+
*/
199+
last = head + ready;
200+
nr = 0;
201+
while (head != last && nr < *count) {
202+
struct io_uring_cqe *cqe = &ring->cq.cqes[head & mask];
203+
204+
if (cqe->flags & IORING_CQE_F_SKIP) {
205+
/*
206+
* A skip entry can only be consumed if it's at the
207+
* current CQ head, stop the batch otherwise. It'll
208+
* be at the head for the next peek.
209+
*/
210+
if (nr)
211+
break;
212+
io_uring_cq_advance(ring, 1);
213+
head++;
214+
continue;
215+
}
216+
head += io_uring_cqe_nr(cqe);
217+
cqes[nr++] = cqe;
218+
}
219+
*count = nr;
220+
return nr != 0;
190221
}
191222

192223
/*
@@ -344,6 +375,8 @@ int io_uring_wait_cqes_min_timeout(struct io_uring *ring,
344375
struct __kernel_timespec *ts,
345376
unsigned int min_wait_usec, sigset_t *sigmask)
346377
{
378+
if (!(ring->features & IORING_FEAT_MIN_TIMEOUT))
379+
return -EINVAL;
347380
return io_uring_wait_cqes_new(ring, cqe_ptr, wait_nr, ts, min_wait_usec,
348381
sigmask);
349382
}

src/register.c

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -482,26 +482,8 @@ int io_uring_resize_rings(struct io_uring *ring, struct io_uring_params *p)
482482
__sys_munmap(ring->sq.sqes, ring->sq.sqes_sz);
483483
io_uring_unmap_rings(&ring->sq, &ring->cq);
484484

485-
486-
memset(&ring->sq, 0, sizeof(ring->sq));
487-
memset(&ring->cq, 0, sizeof(ring->cq));
488-
ret = io_uring_mmap(ring->ring_fd, p, &ring->sq, &ring->cq);
489-
if (ret) {
490-
/*
491-
* The kernel has already committed to the new ring layout,
492-
* but we failed to mmap it. io_uring_mmap() cleaned up its
493-
* own partial mappings but may leave stale or error-encoded
494-
* pointers in sq/cq (e.g. ring_ptr = (void *)-ENOMEM).
495-
* Re-zero to scrub those so io_uring_queue_exit() won't
496-
* pass dangling pointers to munmap. The ring is
497-
* unrecoverable — destroy it.
498-
*/
499-
memset(&ring->sq, 0, sizeof(ring->sq));
500-
memset(&ring->cq, 0, sizeof(ring->cq));
501-
io_uring_queue_exit(ring);
502-
goto out;
503-
}
504-
485+
ring->sq = sq;
486+
ring->cq = cq;
505487
ring->sq.sqe_head = sq_head;
506488
ring->sq.sqe_tail = sq_tail;
507489

0 commit comments

Comments
 (0)