Open YoSTEALTH opened 1 month ago
What's the sequence that reproduces it? I have no issues here, and since it's not a header function, we should be running the exact same thing.
I am, once again, suspicious of the other code! But if you can boil it down to something that reproduces it reliably for you, then it'd be easier to take a look at. You can also try and re-compile liburing and add:
assert(i == count);
to src/queue.c:io_uring_peek_batch_cqe() before where it does
return count;
as that should be the only way that we'd ever return a value that doesn't match what we already looped and filled it with. In diff form, it'd look like:
diff --git a/src/queue.c b/src/queue.c
index c4360614ecf3..b74f984fa631 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: MIT */
#define _POSIX_C_SOURCE 200112L
+#include <assert.h>
#include "lib.h"
#include "syscall.h"
#include "liburing.h"
@@ -182,6 +183,7 @@ again:
for (;head != last; head++, i++)
cqes[i] = &ring->cq.cqes[(head & mask) << shift];
+ assert(count == i);
return count;
}
Oh, and you'd need to configure liburing as:
$ ./configure --use-libc
for that to work.
--use-libc
Its happens over FFI
though! So in C I am sure there isn't an issues with it.
I have changed the code to use io_uring_for_each_cqe
vs io_uring_peek_batch_cqe
and it doesn't segfault, though its not perfect yet.
while counter := ((io_uring_submit(ring) if io_uring_sq_ready(ring) else 0) + counter - cq_ready):
# get count of how many event(s) are ready and fill `cqe`
while not (cq_ready := io_uring_cq_ready(ring)):
io_uring_wait_cqe_nr(ring, cqe, 1) # wait for at least `1` event to be ready.
io_uring_for_each_cqe(ring, cqe)
for i in range(cq_ready):
# do stuff ...
You can see it effect this library https://github.com/YoSTEALTH/Shakti
I have changed the code to use io_uring_for_each_cqe vs io_uring_peek_batch_cqe and it doesn't segfault, though its not perfect yet.
That doesn't really help me, as I'd be interested in figuring out if there is an issue with io_uring_peek_batch_cqe(). If you could try what I suggested, that'd be awesome, thanks!
Added that C code and compiled, getting:
In file included from queue.c:4:
queue.c: In function ‘io_uring_peek_batch_cqe’:
queue.c:186:22: warning: comparison of integer expressions of different signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]
186 | assert(count == i);
| ^~
Not sure if you wanting me to run some test file!. Running python test code, back to segfault.
io_uring_for_each_cqe
kinda works! But there are certain time where cqe
doesn't get populated as well. Its just running bunch of io_uring_prep_nop
. I am not sure how to reproduce this in C its clearly C data not reaching Python side (sometimes).
As for the wrapper, there isn't much to it:
cdef class io_uring_cqe:
cdef __io_uring_cqe* ptr
it just a pointer and Liburing does all the assigning memory, data, removal.
Not sure why but io_uring_peek_cqe(ring, cqe)
returns 4294967285
vs 0
on success and -EAGAIN
on failure
Finally!!! after 2 whole months of dealing with segfault, solution was to use different Liburing functions?!
while counter := ((io_uring_submit(ring) if io_uring_sq_ready(ring) else 0)+counter-cq_ready):
cq_ready = 0
while io_uring_peek_cqe(ring, cqe) == -EAGAIN:
io_uring_wait_cqe_nr(ring, cqe, 1) # wait for at least `1` event to be ready.
for index in range(io_uring_for_each_cqe(ring, cqe)):
# do stuff ...
Not sure why io_uring_peek_batch_cqe
was glitching out like that. Sure needs an investigation.
Today was the last day I could work on fixing this bug, glad I burnt the midnight oil to get er done :)
Not sure why but io_uring_peek_cqe(ring, cqe) returns 4294967285 vs 0 on success and -EAGAIN on failure
4294967285 is -EAGAIN, it sounds like a casting issue on your side where it becomes unsigned.
Not sure what was happening with 4294967285
same code working fine now, maybe a glitch in the matrix.
Real issues is to figure out whats happening with io_uring_peek_batch_cqe
, I must have compiled (gcc, clang) near to thousand times, many different codes, I had over 10GB+ core dump files ...
Looking at your earlier segfaults, are you sure it isn't some random memory issues you are seeing? Eg a bug in the other code, or something like that. They don't look related to liburing, but they do look like use-after-free issues, or double frees.
io_uring_for_each_cqe kinda works! But there are certain time where cqe doesn't get populated as well.
Things like that make me very suspicious, because while the peek batch API is probably not that widely used, io_uring_for_each_cqe() certainly is. And I find it very hard to believe there's a bug in that.
Looking at your earlier segfaults, are you sure it isn't some random memory issues you are seeing? Eg a bug in the other code, or something like that. They don't look related to liburing, but they do look like use-after-free issues, or double frees.
These could certainly be true, I could have goofed somewhere! For now same code io_uring_peek_cqe
& io_uring_for_each_cqe
works, and io_uring_peek_batch_cqe
is a glitch.
Things like that make me very suspicious
This is actually my fault, I was calling io_uring_for_each_cqe
without calling io_uring_peek_cqe
first, thus the weirdness.
O, I forgot to mention.
In my code I have it so sqe->user_data
can not be set to 0
, if so it will raise error.
While using cq_ready = io_uring_peek_batch_cqe(...)
many times I noticed cq_ready
would return value like 10
later processing cqe[i].user_data
only say 5
of those 10
would have valid user_data
and rest would be 0
.
Either cq_ready
would give higher ready cqe
count while in reality only few were ready, or all ready cqe
weren't inserted into array?
Another problem was when I would io_uring_cq_advance(ring, cq_ready)
this would segfault, maybe because cq_ready
was trying to free more cqe
than actually available?
I still think this is exceedingly likely an issue in your code or interface. Given that there are demonstrable signedness issues etc, I have to say I don't have a lot of faith in the stability of it.
In terms of completions, you can do:
# echo 1 > /sys/kernel/debug/tracing/events/io_uring/enable
and then view a trace with:
# cat /sys/kernel/debug/tracing/trace
which will tell you what completions came in.
io_uring_cq_advance()
only adds an integer to the shared CQ ring head, there's no way that can segfault unless you overwrote some memory, for example.
Ok, trying with trace enabled.
For both same exact code, only thing changed are:
Segfault:
# event manager
while counter := ((io_uring_submit(ring) if io_uring_sq_ready(ring) else 0)+counter-cq_ready):
# get count of how many event(s) are ready and fill `cqe`
while not (cq_ready := io_uring_peek_batch_cqe(ring, cqe, counter)):
io_uring_wait_cqe_nr(ring, cqe, 1) # wait for at least `1` event to be ready.
for index in range(cq_ready):
res, user_data = cqe.get_index(index)
if (ptr := <PyObject*><uintptr_t>user_data) is NULL:
raise RuntimeError('`engine()` - received `NULL` from `user_data`')
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 34/34 #P:24
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
python3-8831 [001] ..... 4383.112306: io_uring_create: ring 000000001a5a30d7, fd 3 sq size 1024, cq size 2048, flags 0x10000
python3-8831 [001] ..... 4383.112357: io_uring_submit_req: ring 000000001a5a30d7, req 00000000d8323653, user_data 0x72576c6665c0, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112359: io_uring_queue_async_work: ring 000000001a5a30d7, request 00000000d8323653, user_data 0x72576c6665c0, opcode NOP, flags 0x40010, normal queue, work 00000000f99d6224
python3-8831 [001] ..... 4383.112405: io_uring_submit_req: ring 000000001a5a30d7, req 000000008934bb66, user_data 0x72576c666500, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112406: io_uring_queue_async_work: ring 000000001a5a30d7, request 000000008934bb66, user_data 0x72576c666500, opcode NOP, flags 0x40010, normal queue, work 00000000135de4eb
python3-8831 [001] ..... 4383.112406: io_uring_submit_req: ring 000000001a5a30d7, req 0000000026f87a3a, user_data 0x72576c5f0dc0, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112407: io_uring_queue_async_work: ring 000000001a5a30d7, request 0000000026f87a3a, user_data 0x72576c5f0dc0, opcode NOP, flags 0x40010, normal queue, work 00000000070c4c78
python3-8831 [001] ..... 4383.112407: io_uring_submit_req: ring 000000001a5a30d7, req 000000004e1b7af5, user_data 0x72576c5f0460, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112407: io_uring_queue_async_work: ring 000000001a5a30d7, request 000000004e1b7af5, user_data 0x72576c5f0460, opcode NOP, flags 0x40010, normal queue, work 0000000022d91cea
python3-8831 [001] ..... 4383.112407: io_uring_submit_req: ring 000000001a5a30d7, req 000000009ac3c392, user_data 0x72576c5f0fa0, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112408: io_uring_queue_async_work: ring 000000001a5a30d7, request 000000009ac3c392, user_data 0x72576c5f0fa0, opcode NOP, flags 0x40010, normal queue, work 00000000508922c1
python3-8831 [001] ..... 4383.112408: io_uring_submit_req: ring 000000001a5a30d7, req 00000000cc5b0bf8, user_data 0x72576c5f1000, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112408: io_uring_queue_async_work: ring 000000001a5a30d7, request 00000000cc5b0bf8, user_data 0x72576c5f1000, opcode NOP, flags 0x40010, normal queue, work 00000000e31f5469
python3-8831 [001] ..... 4383.112408: io_uring_submit_req: ring 000000001a5a30d7, req 00000000dbbc1472, user_data 0x72576c5f1060, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112409: io_uring_queue_async_work: ring 000000001a5a30d7, request 00000000dbbc1472, user_data 0x72576c5f1060, opcode NOP, flags 0x40010, normal queue, work 000000006dcad57f
python3-8831 [001] ..... 4383.112409: io_uring_submit_req: ring 000000001a5a30d7, req 000000009b5895d6, user_data 0x72576c5f10c0, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112409: io_uring_queue_async_work: ring 000000001a5a30d7, request 000000009b5895d6, user_data 0x72576c5f10c0, opcode NOP, flags 0x40010, normal queue, work 0000000090a2d9f8
python3-8831 [001] ..... 4383.112411: io_uring_submit_req: ring 000000001a5a30d7, req 0000000096f98703, user_data 0x72576c4a6500, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112411: io_uring_queue_async_work: ring 000000001a5a30d7, request 0000000096f98703, user_data 0x72576c4a6500, opcode NOP, flags 0x40010, normal queue, work 000000001313aa14
python3-8831 [001] ..... 4383.112411: io_uring_submit_req: ring 000000001a5a30d7, req 00000000e4e79765, user_data 0x72576c4a6560, opcode NOP, flags 0x10, sq_thread 0
python3-8831 [001] ..... 4383.112411: io_uring_queue_async_work: ring 000000001a5a30d7, request 00000000e4e79765, user_data 0x72576c4a6560, opcode NOP, flags 0x40010, normal queue, work 000000008b7003de
python3-8831 [001] ..... 4383.112415: io_uring_cqring_wait: ring 000000001a5a30d7, min_events 1
iou-wrk-8831-8833 [009] ...1. 4383.112478: io_uring_complete: ring 000000001a5a30d7, req 00000000d8323653, user_data 0x72576c6665c0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112485: io_uring_complete: ring 000000001a5a30d7, req 000000008934bb66, user_data 0x72576c666500, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112486: io_uring_complete: ring 000000001a5a30d7, req 0000000026f87a3a, user_data 0x72576c5f0dc0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112487: io_uring_complete: ring 000000001a5a30d7, req 000000004e1b7af5, user_data 0x72576c5f0460, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112488: io_uring_complete: ring 000000001a5a30d7, req 000000009ac3c392, user_data 0x72576c5f0fa0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112489: io_uring_complete: ring 000000001a5a30d7, req 00000000cc5b0bf8, user_data 0x72576c5f1000, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112490: io_uring_complete: ring 000000001a5a30d7, req 00000000dbbc1472, user_data 0x72576c5f1060, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112492: io_uring_complete: ring 000000001a5a30d7, req 000000009b5895d6, user_data 0x72576c5f10c0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112493: io_uring_complete: ring 000000001a5a30d7, req 0000000096f98703, user_data 0x72576c4a6500, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-8831-8833 [009] ...1. 4383.112493: io_uring_complete: ring 000000001a5a30d7, req 00000000e4e79765, user_data 0x72576c4a6560, result 0, cflags 0x0 extra1 0 extra2 0
python3-8831 [001] ..... 4383.112493: io_uring_task_work_run: tctx 00000000b66e999c, count 6, loops 1
python3-8831 [001] ..... 4383.112495: io_uring_task_work_run: tctx 00000000b66e999c, count 4, loops 1
No problems:
# event manager
while counter := ((io_uring_submit(ring) if io_uring_sq_ready(ring) else 0)+counter-cq_ready):
cq_ready = 0
if io_uring_peek_cqe(ring, cqe) == -EAGAIN:
io_uring_wait_cqe_nr(ring, cqe, 1) # wait for at least `1` event to be ready.
for index in range(io_uring_for_each_cqe(ring, cqe)):
res, user_data = cqe.get_index(index)
if (ptr := <PyObject*><uintptr_t>user_data) is NULL:
raise RuntimeError('`engine()` - received `NULL` from `user_data`')
cq_ready += 1
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 33/33 #P:24
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
python3-9116 [019] ..... 4526.441262: io_uring_create: ring 000000002032cb5a, fd 3 sq size 1024, cq size 2048, flags 0x10000
python3-9116 [019] ..... 4526.441325: io_uring_submit_req: ring 000000002032cb5a, req 0000000032288e5f, user_data 0x71785ba665c0, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441327: io_uring_queue_async_work: ring 000000002032cb5a, request 0000000032288e5f, user_data 0x71785ba665c0, opcode NOP, flags 0x40010, normal queue, work 00000000b6665afe
python3-9116 [019] ..... 4526.441371: io_uring_submit_req: ring 000000002032cb5a, req 000000006190a3eb, user_data 0x71785ba66500, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441371: io_uring_queue_async_work: ring 000000002032cb5a, request 000000006190a3eb, user_data 0x71785ba66500, opcode NOP, flags 0x40010, normal queue, work 00000000ab7055aa
python3-9116 [019] ..... 4526.441372: io_uring_submit_req: ring 000000002032cb5a, req 00000000086a43f0, user_data 0x71785b9f0dc0, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441372: io_uring_queue_async_work: ring 000000002032cb5a, request 00000000086a43f0, user_data 0x71785b9f0dc0, opcode NOP, flags 0x40010, normal queue, work 0000000017f0a9ea
python3-9116 [019] ..... 4526.441372: io_uring_submit_req: ring 000000002032cb5a, req 00000000042a093c, user_data 0x71785b9f0460, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441372: io_uring_queue_async_work: ring 000000002032cb5a, request 00000000042a093c, user_data 0x71785b9f0460, opcode NOP, flags 0x40010, normal queue, work 00000000d429ca3b
python3-9116 [019] ..... 4526.441373: io_uring_submit_req: ring 000000002032cb5a, req 000000007097a369, user_data 0x71785b9f0fa0, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441373: io_uring_queue_async_work: ring 000000002032cb5a, request 000000007097a369, user_data 0x71785b9f0fa0, opcode NOP, flags 0x40010, normal queue, work 00000000cdf133ed
python3-9116 [019] ..... 4526.441373: io_uring_submit_req: ring 000000002032cb5a, req 00000000069f27ce, user_data 0x71785b9f1000, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441373: io_uring_queue_async_work: ring 000000002032cb5a, request 00000000069f27ce, user_data 0x71785b9f1000, opcode NOP, flags 0x40010, normal queue, work 00000000ac14df27
python3-9116 [019] ..... 4526.441374: io_uring_submit_req: ring 000000002032cb5a, req 00000000382bb775, user_data 0x71785b9f1060, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441374: io_uring_queue_async_work: ring 000000002032cb5a, request 00000000382bb775, user_data 0x71785b9f1060, opcode NOP, flags 0x40010, normal queue, work 0000000020ed3eeb
python3-9116 [019] ..... 4526.441374: io_uring_submit_req: ring 000000002032cb5a, req 00000000f59b29d9, user_data 0x71785b9f10c0, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441374: io_uring_queue_async_work: ring 000000002032cb5a, request 00000000f59b29d9, user_data 0x71785b9f10c0, opcode NOP, flags 0x40010, normal queue, work 000000008d6e0cbe
python3-9116 [019] ..... 4526.441376: io_uring_submit_req: ring 000000002032cb5a, req 000000006b26aef7, user_data 0x71785b8a6500, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441376: io_uring_queue_async_work: ring 000000002032cb5a, request 000000006b26aef7, user_data 0x71785b8a6500, opcode NOP, flags 0x40010, normal queue, work 0000000004825a78
python3-9116 [019] ..... 4526.441376: io_uring_submit_req: ring 000000002032cb5a, req 000000008326ecfe, user_data 0x71785b8a6560, opcode NOP, flags 0x10, sq_thread 0
python3-9116 [019] ..... 4526.441376: io_uring_queue_async_work: ring 000000002032cb5a, request 000000008326ecfe, user_data 0x71785b8a6560, opcode NOP, flags 0x40010, normal queue, work 00000000a2ab8519
python3-9116 [019] ..... 4526.441379: io_uring_cqring_wait: ring 000000002032cb5a, min_events 1
iou-wrk-9116-9118 [012] ...1. 4526.441415: io_uring_complete: ring 000000002032cb5a, req 0000000032288e5f, user_data 0x71785ba665c0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441422: io_uring_complete: ring 000000002032cb5a, req 000000006190a3eb, user_data 0x71785ba66500, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441423: io_uring_complete: ring 000000002032cb5a, req 00000000086a43f0, user_data 0x71785b9f0dc0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441424: io_uring_complete: ring 000000002032cb5a, req 00000000042a093c, user_data 0x71785b9f0460, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441425: io_uring_complete: ring 000000002032cb5a, req 000000007097a369, user_data 0x71785b9f0fa0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441426: io_uring_complete: ring 000000002032cb5a, req 00000000069f27ce, user_data 0x71785b9f1000, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441426: io_uring_complete: ring 000000002032cb5a, req 00000000382bb775, user_data 0x71785b9f1060, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441427: io_uring_complete: ring 000000002032cb5a, req 00000000f59b29d9, user_data 0x71785b9f10c0, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441428: io_uring_complete: ring 000000002032cb5a, req 000000006b26aef7, user_data 0x71785b8a6500, result 0, cflags 0x0 extra1 0 extra2 0
iou-wrk-9116-9118 [012] ...1. 4526.441429: io_uring_complete: ring 000000002032cb5a, req 000000008326ecfe, user_data 0x71785b8a6560, result 0, cflags 0x0 extra1 0 extra2 0
python3-9116 [019] ..... 4526.441433: io_uring_task_work_run: tctx 000000009c113459, count 10, loops 1
After couple months of segfaults(over interpreter) narrowed the bug down to
io_uring_peek_batch_cqe
not populatingcqe
consistently. The first loop always runs fine after its anyone's guess as to when it will segfault from not populatingcqe