1use std::sync::Arc;
26use std::sync::atomic::{Ordering, fence};
27
28use virtio_bindings::virtio_ring;
29
30use crate::QueueConfig;
31use crate::guest_mem::GuestMemWriter;
32use crate::queue::flags;
33
34const VRING_AVAIL_F_NO_INTERRUPT: u16 = virtio_ring::VRING_AVAIL_F_NO_INTERRUPT as u16;
37
38#[derive(Debug, Clone, Copy)]
40pub struct VirtqDesc {
41 pub addr: u64,
43 pub len: u32,
45 pub flags: u16,
47 pub next: u16,
49}
50
51impl VirtqDesc {
52 #[must_use]
54 pub const fn is_write(&self) -> bool {
55 self.flags & flags::WRITE != 0
56 }
57
58 #[must_use]
60 pub const fn has_next(&self) -> bool {
61 self.flags & flags::NEXT != 0
62 }
63}
64
65#[derive(Debug)]
68pub struct DescChain {
69 pub head_idx: u16,
71 pub descriptors: Vec<VirtqDesc>,
73}
74
75pub struct ChainIter<'a> {
80 queue: &'a SplitQueue,
81 idx: u16,
82 ttl: u16,
84 done: bool,
85}
86
87impl Iterator for ChainIter<'_> {
88 type Item = VirtqDesc;
89
90 fn next(&mut self) -> Option<VirtqDesc> {
91 if self.done || self.ttl == 0 || self.idx >= self.queue.size {
92 return None;
93 }
94 self.ttl -= 1;
95 let desc = self.queue.read_descriptor(self.idx);
96 if desc.has_next() {
97 self.idx = desc.next;
98 } else {
99 self.done = true;
100 }
101 Some(desc)
102 }
103}
104
105pub struct SplitQueue {
107 mem: Arc<GuestMemWriter>,
108 queue_idx: u16,
109 size: u16,
110 desc_gpa: u64,
111 avail_gpa: u64,
112 used_gpa: u64,
113 last_avail_idx: u16,
115 used_idx: u16,
117 event_idx: bool,
119}
120
121impl SplitQueue {
122 #[must_use]
126 pub fn new(
127 mem: Arc<GuestMemWriter>,
128 queue_idx: u16,
129 cfg: &QueueConfig,
130 event_idx: bool,
131 ) -> Self {
132 let used_idx = mem.read_u16(cfg.used_addr as usize + 2);
136 Self {
137 mem,
138 queue_idx,
139 size: cfg.size,
140 desc_gpa: cfg.desc_addr,
141 avail_gpa: cfg.avail_addr,
142 used_gpa: cfg.used_addr,
143 last_avail_idx: 0,
144 used_idx,
145 event_idx,
146 }
147 }
148
149 #[must_use]
151 pub const fn last_avail_idx(&self) -> u16 {
152 self.last_avail_idx
153 }
154
155 pub const fn set_last_avail_idx(&mut self, idx: u16) {
158 self.last_avail_idx = idx;
159 }
160
161 #[must_use]
163 pub const fn queue_idx(&self) -> u16 {
164 self.queue_idx
165 }
166
167 #[must_use]
169 pub const fn size(&self) -> u16 {
170 self.size
171 }
172
173 #[must_use]
175 pub fn mem(&self) -> &GuestMemWriter {
176 &self.mem
177 }
178
179 pub fn set_event_idx(&mut self, enabled: bool) {
181 self.event_idx = enabled;
182 }
183
184 fn avail_idx(&self) -> u16 {
186 self.mem.read_u16(self.avail_gpa as usize + 2)
188 }
189
190 fn avail_ring_entry(&self, pos: u16) -> u16 {
192 let off = self.avail_gpa as usize + 4 + (pos % self.size) as usize * 2;
193 self.mem.read_u16(off)
194 }
195
196 fn read_descriptor(&self, idx: u16) -> VirtqDesc {
198 let base = self.desc_gpa as usize + idx as usize * 16;
200 VirtqDesc {
201 addr: self.mem.read_u64(base),
202 len: self.mem.read_u32(base + 8),
203 flags: self.mem.read_u16(base + 12),
204 next: self.mem.read_u16(base + 14),
205 }
206 }
207
208 #[must_use]
210 pub fn has_avail(&self) -> bool {
211 self.avail_idx() != self.last_avail_idx
212 }
213
214 pub fn pop_avail(&mut self) -> Option<DescChain> {
219 let head_idx = self.next_avail_head()?;
220 let descriptors = self.chain_iter(head_idx).collect();
221 Some(DescChain {
222 head_idx,
223 descriptors,
224 })
225 }
226
227 pub fn next_avail_head(&mut self) -> Option<u16> {
231 let avail_idx = self.avail_idx();
232 if avail_idx == self.last_avail_idx {
233 return None;
234 }
235 fence(Ordering::Acquire);
240 let head_idx = self.avail_ring_entry(self.last_avail_idx);
241 self.last_avail_idx = self.last_avail_idx.wrapping_add(1);
242 Some(head_idx)
243 }
244
245 #[must_use]
250 pub fn chain_iter(&self, head: u16) -> ChainIter<'_> {
251 ChainIter {
252 queue: self,
253 idx: head,
254 ttl: self.size,
255 done: false,
256 }
257 }
258
259 fn write_used_entry(&self, head_idx: u16, len: u32) {
261 let off = self.used_gpa as usize + 4 + (self.used_idx % self.size) as usize * 8;
263 self.mem.write_u32(off, u32::from(head_idx));
264 self.mem.write_u32(off + 4, len);
265 }
266
267 pub fn push_used(&mut self, head_idx: u16, len: u32) -> bool {
270 let old_used = self.used_idx;
271 self.write_used_entry(head_idx, len);
272 self.used_idx = self.used_idx.wrapping_add(1);
273 fence(Ordering::Release);
276 self.mem
277 .write_u16(self.used_gpa as usize + 2, self.used_idx);
278 self.should_notify(old_used, self.used_idx)
279 }
280
281 pub fn push_used_batch(&mut self, completions: &[(u16, u32)]) -> bool {
284 if completions.is_empty() {
285 return false;
286 }
287 let old_used = self.used_idx;
288 for &(head_idx, len) in completions {
289 self.write_used_entry(head_idx, len);
290 self.used_idx = self.used_idx.wrapping_add(1);
291 }
292 fence(Ordering::Release);
293 self.mem
294 .write_u16(self.used_gpa as usize + 2, self.used_idx);
295 self.should_notify(old_used, self.used_idx)
296 }
297
298 pub fn write_avail_event(&self) {
303 let off = self.used_gpa as usize + 4 + self.size as usize * 8;
304 self.mem.write_u16(off, self.last_avail_idx);
305 }
306
307 pub fn write_avail_event_current(&self) {
319 let avail_idx = self.avail_idx();
320 fence(Ordering::Release);
321 let off = self.used_gpa as usize + 4 + self.size as usize * 8;
322 self.mem.write_u16(off, avail_idx);
323 }
324
325 #[must_use]
336 pub fn enable_notification(&self) -> bool {
337 self.write_avail_event();
338 fence(Ordering::SeqCst);
339 self.avail_idx() != self.last_avail_idx
340 }
341
342 fn should_notify(&self, old_used: u16, new_used: u16) -> bool {
345 if old_used == new_used {
346 return false;
347 }
348 if self.event_idx {
349 fence(Ordering::SeqCst);
357 let used_event = self
358 .mem
359 .read_u16(self.avail_gpa as usize + 4 + self.size as usize * 2);
360 vring_need_event(used_event, new_used, old_used)
361 } else {
362 let avail_flags = self.mem.read_u16(self.avail_gpa as usize);
364 avail_flags & VRING_AVAIL_F_NO_INTERRUPT == 0
365 }
366 }
367}
368
369unsafe impl Send for SplitQueue {}
374unsafe impl Sync for SplitQueue {}
375
376fn vring_need_event(event_idx: u16, new_idx: u16, old_idx: u16) -> bool {
379 new_idx.wrapping_sub(event_idx).wrapping_sub(1) < new_idx.wrapping_sub(old_idx)
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385
386 const RAM: usize = 0x1_0000; const SIZE: u16 = 8;
388
389 const DESC_OFF: u64 = 0x1000;
391 const AVAIL_OFF: u64 = 0x2000;
392 const USED_OFF: u64 = 0x3000;
393 const DATA_OFF: u64 = 0x4000;
394
395 struct TestRam {
398 buf: Vec<u8>,
399 gpa_base: u64,
400 }
401
402 impl TestRam {
403 fn new(gpa_base: u64) -> Self {
404 Self {
405 buf: vec![0u8; RAM],
406 gpa_base,
407 }
408 }
409
410 fn mem(&mut self) -> Arc<GuestMemWriter> {
411 unsafe {
414 Arc::new(GuestMemWriter::new(
415 self.buf.as_mut_ptr(),
416 self.buf.len(),
417 self.gpa_base as usize,
418 ))
419 }
420 }
421
422 fn cfg(&self) -> QueueConfig {
423 QueueConfig {
424 desc_addr: self.gpa_base + DESC_OFF,
425 avail_addr: self.gpa_base + AVAIL_OFF,
426 used_addr: self.gpa_base + USED_OFF,
427 size: SIZE,
428 ready: true,
429 gpa_base: self.gpa_base,
430 }
431 }
432
433 fn off(&self, gpa: u64) -> usize {
434 (gpa - self.gpa_base) as usize
435 }
436
437 fn w16(&mut self, gpa: u64, v: u16) {
438 let o = self.off(gpa);
439 self.buf[o..o + 2].copy_from_slice(&v.to_le_bytes());
440 }
441
442 fn r16(&self, gpa: u64) -> u16 {
443 let o = (gpa - self.gpa_base) as usize;
444 u16::from_le_bytes([self.buf[o], self.buf[o + 1]])
445 }
446
447 fn r32(&self, gpa: u64) -> u32 {
448 let o = (gpa - self.gpa_base) as usize;
449 u32::from_le_bytes([
450 self.buf[o],
451 self.buf[o + 1],
452 self.buf[o + 2],
453 self.buf[o + 3],
454 ])
455 }
456
457 fn write_desc(&mut self, idx: u16, addr: u64, len: u32, flags: u16, next: u16) {
458 let base = self.gpa_base + DESC_OFF + u64::from(idx) * 16;
459 let o = self.off(base);
460 self.buf[o..o + 8].copy_from_slice(&addr.to_le_bytes());
461 self.buf[o + 8..o + 12].copy_from_slice(&len.to_le_bytes());
462 self.buf[o + 12..o + 14].copy_from_slice(&flags.to_le_bytes());
463 self.buf[o + 14..o + 16].copy_from_slice(&next.to_le_bytes());
464 }
465
466 fn set_avail(&mut self, pos: u16, head: u16) {
467 self.w16(self.gpa_base + AVAIL_OFF + 4 + u64::from(pos) * 2, head);
468 }
469
470 fn set_avail_idx(&mut self, idx: u16) {
471 self.w16(self.gpa_base + AVAIL_OFF + 2, idx);
472 }
473
474 fn set_used_event(&mut self, v: u16) {
475 self.w16(self.gpa_base + AVAIL_OFF + 4 + u64::from(SIZE) * 2, v);
476 }
477
478 fn set_avail_flags(&mut self, v: u16) {
479 self.w16(self.gpa_base + AVAIL_OFF, v);
480 }
481
482 fn used_idx(&self) -> u16 {
483 self.r16(self.gpa_base + USED_OFF + 2)
484 }
485
486 fn used_entry(&self, slot: u16) -> (u32, u32) {
487 let base = self.gpa_base + USED_OFF + 4 + u64::from(slot) * 8;
488 (self.r32(base), self.r32(base + 4))
489 }
490
491 fn avail_event(&self) -> u16 {
492 self.r16(self.gpa_base + USED_OFF + 4 + u64::from(SIZE) * 8)
493 }
494 }
495
496 fn queue(ram: &mut TestRam, event_idx: bool) -> SplitQueue {
497 let cfg = ram.cfg();
498 SplitQueue::new(ram.mem(), 0, &cfg, event_idx)
499 }
500
501 #[test]
502 fn pop_single_descriptor() {
503 let mut ram = TestRam::new(0);
504 ram.write_desc(0, ram.gpa_base + DATA_OFF, 256, 0, 0);
505 ram.set_avail(0, 0);
506 ram.set_avail_idx(1);
507
508 let mut q = queue(&mut ram, false);
509 assert!(q.has_avail());
510 let chain = q.pop_avail().unwrap();
511 assert_eq!(chain.head_idx, 0);
512 assert_eq!(chain.descriptors.len(), 1);
513 assert_eq!(chain.descriptors[0].addr, ram.gpa_base + DATA_OFF);
514 assert_eq!(chain.descriptors[0].len, 256);
515 assert!(!q.has_avail());
516 }
517
518 #[test]
519 fn pop_descriptor_chain() {
520 let mut ram = TestRam::new(0);
521 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, flags::NEXT, 1);
522 ram.write_desc(
523 1,
524 ram.gpa_base + DATA_OFF + 16,
525 16,
526 flags::NEXT | flags::WRITE,
527 2,
528 );
529 ram.write_desc(2, ram.gpa_base + DATA_OFF + 32, 16, flags::WRITE, 0);
530 ram.set_avail(0, 0);
531 ram.set_avail_idx(1);
532
533 let mut q = queue(&mut ram, false);
534 let chain = q.pop_avail().unwrap();
535 assert_eq!(chain.descriptors.len(), 3);
536 assert!(!chain.descriptors[0].is_write());
537 assert!(chain.descriptors[1].is_write());
538 assert!(!chain.descriptors[2].has_next());
539 }
540
541 #[test]
542 fn cyclic_chain_terminates() {
543 let mut ram = TestRam::new(0);
545 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, flags::NEXT, 1);
546 ram.write_desc(1, ram.gpa_base + DATA_OFF + 16, 16, flags::NEXT, 0);
547 ram.set_avail(0, 0);
548 ram.set_avail_idx(1);
549
550 let mut q = queue(&mut ram, false);
551 let chain = q.pop_avail().unwrap();
552 assert!(chain.descriptors.len() <= SIZE as usize);
553 }
554
555 #[test]
556 fn out_of_range_next_stops_walk() {
557 let mut ram = TestRam::new(0);
558 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, flags::NEXT, 99);
559 ram.set_avail(0, 0);
560 ram.set_avail_idx(1);
561
562 let mut q = queue(&mut ram, false);
563 let chain = q.pop_avail().unwrap();
564 assert_eq!(chain.descriptors.len(), 1);
565 }
566
567 #[test]
568 fn chain_iter_walks_allocation_free() {
569 let mut ram = TestRam::new(0);
570 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, flags::NEXT, 1);
571 ram.write_desc(
572 1,
573 ram.gpa_base + DATA_OFF + 16,
574 32,
575 flags::NEXT | flags::WRITE,
576 2,
577 );
578 ram.write_desc(2, ram.gpa_base + DATA_OFF + 48, 64, flags::WRITE, 0);
579 ram.set_avail(0, 0);
580 ram.set_avail_idx(1);
581
582 let mut q = queue(&mut ram, false);
583 let head = q.next_avail_head().unwrap();
584 assert_eq!(head, 0);
585 let descs: Vec<_> = q.chain_iter(head).collect();
586 assert_eq!(descs.len(), 3);
587 assert_eq!(descs[0].len, 16);
588 assert!(!descs[0].is_write());
589 assert!(descs[1].is_write());
590 assert_eq!(descs[2].len, 64);
591 assert!(!descs[2].has_next());
592 assert!(q.next_avail_head().is_none());
594 }
595
596 #[test]
597 fn chain_iter_cycle_terminates() {
598 let mut ram = TestRam::new(0);
599 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, flags::NEXT, 1);
600 ram.write_desc(1, ram.gpa_base + DATA_OFF + 16, 16, flags::NEXT, 0);
601 ram.set_avail(0, 0);
602 ram.set_avail_idx(1);
603
604 let mut q = queue(&mut ram, false);
605 let head = q.next_avail_head().unwrap();
606 assert!(q.chain_iter(head).count() <= SIZE as usize);
607 }
608
609 #[test]
610 fn push_used_writes_entry_and_idx() {
611 let mut ram = TestRam::new(0);
612 let mut q = queue(&mut ram, false);
613 let notify = q.push_used(5, 1024);
614 assert!(notify); assert_eq!(ram.used_idx(), 1);
616 assert_eq!(ram.used_entry(0), (5, 1024));
617 }
618
619 #[test]
620 fn push_used_batch_single_idx_update() {
621 let mut ram = TestRam::new(0);
622 let mut q = queue(&mut ram, false);
623 let notify = q.push_used_batch(&[(0, 100), (1, 200), (2, 300)]);
624 assert!(notify);
625 assert_eq!(ram.used_idx(), 3);
626 assert_eq!(ram.used_entry(0), (0, 100));
627 assert_eq!(ram.used_entry(2), (2, 300));
628 }
629
630 #[test]
631 fn push_used_batch_empty_is_noop() {
632 let mut ram = TestRam::new(0);
633 let mut q = queue(&mut ram, false);
634 assert!(!q.push_used_batch(&[]));
635 assert_eq!(ram.used_idx(), 0);
636 }
637
638 #[test]
639 fn no_interrupt_flag_suppresses_without_event_idx() {
640 let mut ram = TestRam::new(0);
641 ram.set_avail_flags(VRING_AVAIL_F_NO_INTERRUPT);
642 let mut q = queue(&mut ram, false);
643 assert!(!q.push_used(0, 16));
644 }
645
646 #[test]
647 fn event_idx_suppresses_until_event_reached() {
648 let mut ram = TestRam::new(0);
649 ram.set_used_event(2);
651 let mut q = queue(&mut ram, true);
652 assert!(!q.push_used(0, 16));
654 assert!(!q.push_used(1, 16));
656 assert!(q.push_used(2, 16));
658 }
659
660 #[test]
661 fn write_avail_event_publishes_last_avail() {
662 let mut ram = TestRam::new(0);
663 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, 0, 0);
664 ram.set_avail(0, 0);
665 ram.set_avail_idx(1);
666 let mut q = queue(&mut ram, true);
667 let _ = q.pop_avail();
668 q.write_avail_event();
669 assert_eq!(ram.avail_event(), 1); }
671
672 #[test]
673 fn write_avail_event_current_publishes_guest_avail_idx() {
674 let mut ram = TestRam::new(0);
675 ram.write_desc(0, ram.gpa_base + DATA_OFF, 16, 0, 0);
676 ram.set_avail(0, 0);
677 ram.set_avail_idx(5);
679 let mut q = queue(&mut ram, true);
680 let _ = q.pop_avail();
681 q.write_avail_event_current();
682 assert_eq!(ram.avail_event(), 5);
684 }
685
686 #[test]
690 fn nonzero_gpa_base_resolves_correctly() {
691 let mut ram = TestRam::new(0x4000_0000);
692 ram.write_desc(0, ram.gpa_base + DATA_OFF, 512, 0, 0);
693 ram.set_avail(0, 0);
694 ram.set_avail_idx(1);
695
696 let mut q = queue(&mut ram, false);
697 let chain = q.pop_avail().unwrap();
698 assert_eq!(chain.descriptors[0].addr, 0x4000_0000 + DATA_OFF);
699 assert_eq!(chain.descriptors[0].len, 512);
700
701 q.push_used(0, 512);
702 assert_eq!(ram.used_idx(), 1);
703 assert_eq!(ram.used_entry(0), (0, 512));
704 }
705
706 #[test]
707 fn vring_need_event_formula() {
708 assert!(vring_need_event(0, 1, 0));
710 assert!(!vring_need_event(3, 6, 5));
712 assert!(vring_need_event(65535, 0, 65534));
714 }
715}