@@ -233,21 +233,21 @@ class IoUringAsyncFileIO : public FileIOLayer {
233233 struct io_uring_sqe * sqe = io_uring_get_sqe (&ring_);
234234 if (sqe == nullptr ) {
235235 // Ring full: flush and retry once.
236- io_uring_submit (&ring_ );
236+ flush_submissions_ ( );
237237 sqe = io_uring_get_sqe (&ring_);
238238 if (sqe == nullptr ) return false ;
239239 }
240240 // Use registered-file index (faster than passing raw fds).
241241 io_uring_prep_read (sqe, fd_idx, dst, (unsigned ) size, (off_t ) offset);
242242 sqe->flags |= IOSQE_FIXED_FILE ;
243243 sqe->user_data = req_id;
244+ pending_submit_.push_back (req_id);
244245 ++pending_;
245246 return true ;
246247 }
247248
248249 void flush () override {
249- if (!ring_ok_ || pending_ == 0 ) return ;
250- io_uring_submit (&ring_);
250+ flush_submissions_ ();
251251 }
252252
253253 // Reap one completion from the ring. The demux/routing (buffering foreign
@@ -265,6 +265,12 @@ class IoUringAsyncFileIO : public FileIOLayer {
265265 if (pending_ == 0 ) {
266266 return false ; // nothing in flight
267267 }
268+ if (!pending_submit_.empty ()) {
269+ flush_submissions_ ();
270+ }
271+ if (pending_ == 0 ) {
272+ return false ;
273+ }
268274
269275 struct io_uring_cqe * cqe = nullptr ;
270276 int ret = 0 ;
@@ -347,28 +353,113 @@ class IoUringAsyncFileIO : public FileIOLayer {
347353 struct io_uring_sqe * sqe = io_uring_get_sqe (&ring_);
348354 if (sqe == nullptr ) {
349355 // SQ ring full mid-batch — flush what we've prepped, then retry once.
350- io_uring_submit (&ring_ );
356+ flush_submissions_ ( );
351357 sqe = io_uring_get_sqe (&ring_);
352358 if (sqe == nullptr ) break ;
353359 }
354360 io_uring_prep_read (sqe, r.fd_idx , r.dst , (unsigned ) r.size , (off_t ) r.offset );
355361 sqe->flags |= IOSQE_FIXED_FILE ;
356362 sqe->user_data = r.req_id ;
363+ pending_submit_.push_back (r.req_id );
357364 ++pending_;
358365 ++n_queued;
359366 }
360367 // Pass 2: single io_uring_submit for the batch. This is the MAD-235
361368 // win — one syscall covers N expert-prefetches for the MoE layer
362369 // instead of N separate submits.
363370 if (n_queued > 0 ) {
364- io_uring_submit (&ring_ );
371+ flush_submissions_ ( );
365372 }
366373 return n_queued;
367374 }
368375
369376private:
370377 explicit IoUringAsyncFileIO (std::vector<int > fds) : fds_(std::move(fds)) {}
371378
379+ void synthesize_submit_failures_ (int err) {
380+ while (!pending_submit_.empty ()) {
381+ const uint64_t req_id = pending_submit_.front ();
382+ pending_submit_.pop_front ();
383+
384+ IoResult r;
385+ r.req_id = req_id;
386+ r.status = IoStatus::ErrorNoSubmit;
387+ r.bytes_read = err;
388+ ready_[req_id] = r;
389+ --pending_;
390+ }
391+ }
392+
393+ bool flush_submissions_ () {
394+ if (!ring_ok_ || pending_submit_.empty ()) {
395+ return true ;
396+ }
397+
398+ while (!pending_submit_.empty ()) {
399+ int ret = 0 ;
400+ do {
401+ ret = io_uring_submit (&ring_);
402+ } while (ret == -EINTR );
403+
404+ if (ret == -EBUSY || ret == -EAGAIN || ret == 0 ) {
405+ bool drained = false ;
406+ while (reap_ready_cqe_ ()) {
407+ drained = true ;
408+ }
409+ if (drained) {
410+ continue ;
411+ }
412+ }
413+ if (ret < 0 ) {
414+ LLAMA_LOG_WARN (" wp::IoUringAsyncFileIO: io_uring_submit failed: %s\n " ,
415+ strerror (-ret));
416+ synthesize_submit_failures_ (ret);
417+ return false ;
418+ }
419+ if (ret == 0 ) {
420+ LLAMA_LOG_WARN (" wp::IoUringAsyncFileIO: io_uring_submit made no progress with %zu SQEs pending\n " ,
421+ pending_submit_.size ());
422+ synthesize_submit_failures_ (-EAGAIN );
423+ return false ;
424+ }
425+
426+ for (int i = 0 ; i < ret && !pending_submit_.empty (); ++i) {
427+ pending_submit_.pop_front ();
428+ }
429+ }
430+ return true ;
431+ }
432+
433+ bool reap_ready_cqe_ () {
434+ if (pending_ == 0 ) {
435+ return false ;
436+ }
437+
438+ struct io_uring_cqe * cqe = nullptr ;
439+ int ret = io_uring_peek_cqe (&ring_, &cqe);
440+ if (ret == -EAGAIN || ret == -EINTR || cqe == nullptr ) {
441+ return false ;
442+ }
443+ if (ret < 0 ) {
444+ return false ;
445+ }
446+
447+ IoResult r;
448+ r.req_id = cqe->user_data ;
449+ const int res = cqe->res ;
450+ if (res < 0 ) {
451+ r.status = IoStatus::ErrorIo;
452+ r.bytes_read = res;
453+ } else {
454+ r.status = IoStatus::Ok;
455+ r.bytes_read = res;
456+ }
457+ io_uring_cqe_seen (&ring_, cqe);
458+ --pending_;
459+ ready_[r.req_id ] = r;
460+ return true ;
461+ }
462+
372463 bool init_ (int queue_depth) {
373464 int ret = io_uring_queue_init (queue_depth, &ring_, 0 );
374465 if (ret < 0 ) {
@@ -397,6 +488,7 @@ class IoUringAsyncFileIO : public FileIOLayer {
397488 }
398489
399490 std::vector<int > fds_;
491+ std::deque<uint64_t > pending_submit_;
400492 bool ring_ok_ = false ;
401493 bool files_registered_ = false ;
402494 int pending_ = 0 ;
0 commit comments