Skip to main content

hyperlight_common/virtq/
consumer.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 The Hyperlight Authors.
3
4use alloc::vec;
5
6use bytes::Bytes;
7use fixedbitset::FixedBitSet;
8use smallvec::SmallVec;
9
10use super::*;
11
12type WritableElems = SmallVec<[BufferElement; 2]>;
13
14/// Payload received from the producer, safely copied out of shared memory.
15///
16/// Created by [`VirtqConsumer::poll`]. Device-readable segments are eagerly
17/// copied during poll using [`MemOps::read`] (volatile on the host side), so
18/// accessing data requires no unsafe code and no references into shared
19/// memory. Segment boundaries are preserved in [`Segments`].
20#[derive(Debug, Clone)]
21pub struct RecvChain {
22    token: Token,
23    segments: Segments,
24}
25
26impl RecvChain {
27    /// The token identifying this chain.
28    pub fn token(&self) -> Token {
29        self.token
30    }
31
32    /// The chain payload as ordered byte segments.
33    pub fn segments(&self) -> &Segments {
34        &self.segments
35    }
36
37    /// Consume the chain, taking ownership of the segments.
38    pub fn into_segments(self) -> Segments {
39        self.segments
40    }
41
42    /// Return the chain payload as contiguous bytes.
43    ///
44    /// Returns empty [`Bytes`] when the chain has no readable buffers.
45    pub fn to_bytes(&self) -> Bytes {
46        self.segments.to_bytes()
47    }
48
49    /// Consume the chain and return the payload as contiguous bytes.
50    pub fn into_bytes(self) -> Bytes {
51        self.segments.into_bytes()
52    }
53}
54
55/// Consumer-side chain reply, either writable or ack-only.
56///
57/// Created by [`VirtqConsumer::poll`]. Must be submitted back via
58/// [`VirtqConsumer::complete`] to release the descriptor.
59#[must_use = "dropping without completing leaks the descriptor"]
60pub enum ReplyChain<M: MemOps> {
61    /// Reply with writable buffer capacity.
62    /// Use the `write*` methods on [`WritableChain`] to fill the
63    /// response buffer.
64    Writable(WritableChain<M>),
65    /// Ack-only reply (for chains with only readable buffers). No response buffer.
66    /// Just pass back to [`VirtqConsumer::complete`] to acknowledge.
67    Ack(AckChain),
68}
69
70impl<M: MemOps> ReplyChain<M> {
71    /// The token identifying this reply.
72    pub fn token(&self) -> Token {
73        match self {
74            ReplyChain::Writable(wc) => wc.token(),
75            ReplyChain::Ack(ack) => ack.token(),
76        }
77    }
78
79    /// Number of bytes written (0 for Ack).
80    pub fn written(&self) -> usize {
81        match self {
82            ReplyChain::Writable(wc) => wc.written,
83            ReplyChain::Ack(_) => 0,
84        }
85    }
86
87    /// Convert into the writable form.
88    ///
89    /// Returns the [`AckChain`] unchanged as `Err` for ack-only replies, so the
90    /// completion capability is never silently dropped.
91    pub fn into_writable(self) -> Result<WritableChain<M>, AckChain> {
92        match self {
93            ReplyChain::Writable(wc) => Ok(wc),
94            ReplyChain::Ack(ack) => Err(ack),
95        }
96    }
97}
98
99/// A reply chain with writable buffer capacity.
100///
101/// # Example
102///
103/// ```ignore
104/// if let ReplyChain::Writable(mut wc) = reply {
105///     wc.write_all(b"response data")?;
106///     consumer.complete(wc)?;
107/// }
108/// ```
109#[must_use = "dropping without completing leaks the descriptor"]
110pub struct WritableChain<M: MemOps> {
111    mem: M,
112    token: Token,
113    elems: WritableElems,
114    capacity: usize,
115    written: usize,
116}
117
118impl<M: MemOps> WritableChain<M> {
119    fn new(mem: M, token: Token, elems: WritableElems) -> Self {
120        let capacity = elems.iter().map(|elem| elem.len as usize).sum();
121        Self {
122            mem,
123            token,
124            elems,
125            capacity,
126            written: 0,
127        }
128    }
129
130    /// The token identifying this writable reply.
131    pub fn token(&self) -> Token {
132        self.token
133    }
134
135    /// Total reply capacity in bytes.
136    pub fn capacity(&self) -> usize {
137        self.capacity
138    }
139
140    /// Number of bytes written so far.
141    pub fn written(&self) -> usize {
142        self.written
143    }
144
145    /// Remaining reply capacity.
146    pub fn remaining(&self) -> usize {
147        self.capacity() - self.written()
148    }
149
150    /// Write bytes into writable buffers, returning how many were written.
151    ///
152    /// Appends at the current write position. If `buf` is larger than the
153    /// remaining capacity, writes as many bytes as will fit (partial write).
154    /// Segmentation is intentionally hidden; host-side writes must go through
155    /// [`MemOps::write`].
156    ///
157    /// # Errors
158    ///
159    /// - [`VirtqError::MemoryWriteError`] - underlying MemOps write failed
160    pub fn write(&mut self, buf: &[u8]) -> Result<usize, VirtqError> {
161        let written = write_elements(&self.mem, &self.elems, self.written, buf)
162            .map_err(|_| VirtqError::MemoryWriteError)?;
163        self.written += written;
164        Ok(written)
165    }
166
167    /// Write the entire buffer or return an error.
168    ///
169    /// # Errors
170    ///
171    /// - [`VirtqError::ReplyTooLarge`] - buf exceeds remaining capacity
172    /// - [`VirtqError::MemoryWriteError`] - underlying MemOps write failed
173    pub fn write_all(&mut self, buf: &[u8]) -> Result<&mut Self, VirtqError> {
174        if buf.len() > self.remaining() {
175            return Err(VirtqError::ReplyTooLarge);
176        }
177
178        let written = self.write(buf)?;
179        debug_assert_eq!(written, buf.len());
180        Ok(self)
181    }
182
183    /// Rewind the write cursor to the beginning.
184    ///
185    /// Previously written bytes in shared memory are not zeroed; the
186    /// `written` count is simply reset to 0.
187    pub fn rewind(&mut self) {
188        self.written = 0;
189    }
190}
191
192/// An ack-only reply for chains with no writable buffers.
193///
194/// No response buffer - just pass back to [`VirtqConsumer::complete`]
195/// to acknowledge processing and release the descriptor.
196/// This wrapper keeps ack replies as a must-use completion capability instead
197/// of exposing a bare token that could be accidentally ignored.
198#[must_use = "dropping without completing leaks the descriptor"]
199pub struct AckChain {
200    token: Token,
201}
202
203impl AckChain {
204    fn new(token: Token) -> Self {
205        Self { token }
206    }
207
208    pub fn token(&self) -> Token {
209        self.token
210    }
211}
212
213/// A high-level virtqueue consumer (device side).
214///
215/// The consumer receives chains from the producer (driver), processes them,
216/// and sends back replies. This is typically used on the device/host side.
217///
218/// # Example
219///
220/// ```ignore
221/// let mut consumer = VirtqConsumer::new(layout, mem, notifier);
222///
223/// // Poll and process
224/// while let Some((chain, reply)) = consumer.poll(MAX_RECV_LEN)? {
225///     let data = chain.to_bytes();
226///     match reply {
227///         ReplyChain::Writable(mut wc) => {
228///             let response = handle_request(data);
229///             wc.write_all(&response)?;
230///             consumer.complete(wc)?;
231///         }
232///         ReplyChain::Ack(ack) => {
233///             consumer.complete(ack)?;
234///         }
235///     }
236/// }
237///
238/// // Or defer completions
239/// let mut pending = Vec::new();
240/// while let Some((chain, reply)) = consumer.poll(MAX_RECV_LEN)? {
241///     pending.push((process(chain), reply));
242/// }
243///
244/// for (result, reply) in pending {
245///     // ... complete later ...
246///     consumer.complete(reply)?;
247/// }
248/// ```
249pub struct VirtqConsumer<M, N> {
250    inner: RingConsumer<M>,
251    notifier: N,
252    inflight: FixedBitSet,
253    next_token: u32,
254}
255
256impl<M: MemOps + Clone, N: Notifier> VirtqConsumer<M, N> {
257    /// Create a new virtqueue consumer.
258    ///
259    /// # Arguments
260    ///
261    /// * `layout` - Ring memory layout
262    /// * `mem` - Memory ops implementation for reading/writing to shared memory
263    /// * `notifier` - Callback for notifying the driver about replies
264    pub fn new(layout: Layout, mem: M, notifier: N) -> Self {
265        let inner = RingConsumer::new(layout, mem);
266        let inflight = FixedBitSet::with_capacity(inner.len());
267
268        Self {
269            inner,
270            notifier,
271            inflight,
272            next_token: 0,
273        }
274    }
275
276    /// Poll for a single incoming chain from the driver.
277    ///
278    /// Returns a [`RecvChain`] (copied data) and a [`ReplyChain`] (writable reply
279    /// capacity or ack token). Both are independent owned values with no borrow
280    /// on the consumer.
281    ///
282    /// On [`VirtqError::BadChain`], [`VirtqError::PayloadTooLarge`], and
283    /// [`VirtqError::MemoryReadError`] the descriptor is returned to the driver
284    /// (completed with zero length) before the error is propagated, so a
285    /// rejected chain does not leak.
286    ///
287    /// # Arguments
288    ///
289    /// * `max_recv_len` - Maximum receive payload size to copy. Payloads larger
290    ///   than this return [`VirtqError::PayloadTooLarge`].
291    ///
292    /// # Errors
293    ///
294    /// - [`VirtqError::BadChain`] - Descriptor chain format not recognized
295    /// - [`VirtqError::InvalidState`] - Descriptor ID collision (driver bug)
296    /// - [`VirtqError::MemoryReadError`] - Failed to read chain payload from shared memory
297    pub fn poll(
298        &mut self,
299        max_recv_len: usize,
300    ) -> Result<Option<(RecvChain, ReplyChain<M>)>, VirtqError> {
301        let (id, chain) = match self.inner.poll_available() {
302            Ok(x) => x,
303            Err(RingError::WouldBlock) => return Ok(None),
304            Err(e) => return Err(e.into()),
305        };
306
307        let readables = chain.readables();
308        let writables = chain.writables();
309        if readables.is_empty() && writables.is_empty() {
310            return Err(self.abort_chain(id, VirtqError::BadChain));
311        }
312
313        let recv_len = readables
314            .iter()
315            .fold(0usize, |acc, elem| acc.saturating_add(elem.len as usize));
316
317        // Reserve the inflight slot
318        let id_idx = id as usize;
319        if id_idx >= self.inflight.len() {
320            return Err(VirtqError::InvalidState);
321        }
322
323        if self.inflight.contains(id_idx) {
324            return Err(VirtqError::InvalidState);
325        }
326
327        self.inflight.insert(id_idx);
328        let token = Token {
329            seq: self.next_token,
330            id,
331        };
332        self.next_token = self.next_token.wrapping_add(1);
333
334        if recv_len > max_recv_len {
335            return Err(self.abort_chain(
336                id,
337                VirtqError::PayloadTooLarge {
338                    recv: recv_len,
339                    limit: max_recv_len,
340                },
341            ));
342        }
343
344        // Copy chain payload from shared memory
345        let data = match self.read_elements(readables) {
346            Ok(d) => d,
347            Err(e) => return Err(self.abort_chain(id, e)),
348        };
349
350        let chain = RecvChain {
351            token,
352            segments: data,
353        };
354
355        let reply = if !writables.is_empty() {
356            let mem = self.inner.mem().clone();
357            let writable = WritableChain::new(mem, token, writables.iter().copied().collect());
358            ReplyChain::Writable(writable)
359        } else {
360            let ack = AckChain::new(token);
361            ReplyChain::Ack(ack)
362        };
363
364        Ok(Some((chain, reply)))
365    }
366
367    /// Submit a reply/ack for a received chain back to the ring.
368    ///
369    /// Accepts both [`WritableChain`] (with written byte count) and
370    /// [`AckChain`] (zero-length) via the [`ReplyChain`] enum.
371    /// Clears the inflight slot and notifies the producer if event
372    /// suppression allows.
373    pub fn complete(&mut self, reply: impl Into<ReplyChain<M>>) -> Result<(), VirtqError> {
374        let reply = reply.into();
375        let id = reply.token().id;
376        let written = u32::try_from(reply.written()).map_err(|_| VirtqError::ReplyTooLarge)?;
377
378        let id_idx = id as usize;
379        let slot_set = id_idx < self.inflight.len() && self.inflight.contains(id_idx);
380        if !slot_set {
381            return Err(VirtqError::InvalidState);
382        }
383
384        self.inflight.set(id_idx, false);
385
386        if self.inner.submit_used_with_notify(id, written)? {
387            self.notifier.notify(QueueStats {
388                num_free: self.inner.num_free(),
389                num_inflight: self.inner.num_inflight(),
390            });
391        }
392
393        Ok(())
394    }
395
396    /// Return a consumed descriptor to the driver with zero written length.
397    ///
398    /// The ring's `poll_available` removes the descriptor from the available
399    /// ring before [`poll`](Self::poll) validates the chain.
400    fn abort_chain(&mut self, id: u16, err: VirtqError) -> VirtqError {
401        let id_idx = id as usize;
402        if id_idx < self.inflight.len() {
403            self.inflight.set(id_idx, false);
404        }
405
406        // Best effort: failing to return the descriptor means the ring is
407        // already in an unrecoverable state, so surface the original error.
408        if let Ok(true) = self.inner.submit_used_with_notify(id, 0) {
409            self.notifier.notify(QueueStats {
410                num_free: self.inner.num_free(),
411                num_inflight: self.inner.num_inflight(),
412            });
413        }
414
415        err
416    }
417
418    /// Get the current available cursor position.
419    ///
420    /// Returns the position where the next available descriptor will be
421    /// consumed. Useful for setting up descriptor-based event suppression.
422    #[inline]
423    pub fn avail_cursor(&self) -> RingCursor {
424        self.inner.avail_cursor()
425    }
426
427    /// Get the current used cursor position.
428    ///
429    /// Returns the position where the next used descriptor will be written.
430    /// Useful for setting up descriptor-based event suppression.
431    #[inline]
432    pub fn used_cursor(&self) -> RingCursor {
433        self.inner.used_cursor()
434    }
435
436    /// Configure event suppression for available buffer notifications.
437    ///
438    /// This controls when the driver (producer) signals us about new buffers:
439    ///
440    /// - [`SuppressionKind::Enable`] - Always signal (default) - good for latency
441    /// - [`SuppressionKind::Disable`] - Never signal - caller must poll
442    /// - [`SuppressionKind::Descriptor`] - Signal only at specific cursor position
443    ///
444    /// # Example: Polling Mode
445    /// ```ignore
446    /// consumer.set_avail_suppression(SuppressionKind::Disable)?;
447    /// loop {
448    ///     while let Some((chain, reply)) = consumer.poll(1024)? {
449    ///         process(chain, reply);
450    ///     }
451    ///     // ... do other work ...
452    /// }
453    /// ```
454    pub fn set_avail_suppression(&mut self, kind: SuppressionKind) -> Result<(), VirtqError> {
455        match kind {
456            SuppressionKind::Enable => self.inner.enable_avail_notifications()?,
457            SuppressionKind::Disable => self.inner.disable_avail_notifications()?,
458            SuppressionKind::Descriptor(cursor) => self
459                .inner
460                .enable_avail_notifications_desc(cursor.head(), cursor.wrap())?,
461        }
462        Ok(())
463    }
464
465    /// Read readable buffer elements from shared memory into `Bytes`.
466    fn read_elements(&self, elems: &[BufferElement]) -> Result<Segments, VirtqError> {
467        let mut segments = SmallVec::<[Bytes; 4]>::new();
468
469        for elem in elems {
470            let mut buf = vec![0u8; elem.len as usize];
471            self.inner
472                .mem()
473                .read(elem.addr, &mut buf)
474                .map_err(|_| VirtqError::MemoryReadError)?;
475            segments.push(Bytes::from(buf));
476        }
477
478        Ok(Segments::from_smallvec(segments))
479    }
480
481    /// Reset ring and inflight state to initial values.
482    pub fn reset(&mut self) {
483        self.inner.reset();
484        self.inflight.clear();
485    }
486}
487
488fn write_elements<M: MemOps>(
489    mem: &M,
490    elems: &[BufferElement],
491    offset: usize,
492    buf: &[u8],
493) -> Result<usize, M::Error> {
494    let capacity: usize = elems.iter().map(|elem| elem.len as usize).sum();
495    let mut src = &buf[..buf.len().min(capacity.saturating_sub(offset))];
496    let mut written = 0;
497    let mut skip = offset;
498
499    for elem in elems {
500        if src.is_empty() {
501            break;
502        }
503
504        let elem_len = elem.len as usize;
505        if skip >= elem_len {
506            skip -= elem_len;
507            continue;
508        }
509
510        let elem_offset = skip;
511        skip = 0;
512        let n = (elem_len - elem_offset).min(src.len());
513        let addr = elem.addr + elem_offset as u64;
514
515        mem.write(addr, &src[..n])?;
516
517        written += n;
518        src = &src[n..];
519    }
520
521    Ok(written)
522}
523
524impl<M: MemOps> From<WritableChain<M>> for ReplyChain<M> {
525    fn from(wc: WritableChain<M>) -> Self {
526        ReplyChain::Writable(wc)
527    }
528}
529
530impl<M: MemOps> From<AckChain> for ReplyChain<M> {
531    fn from(ack: AckChain) -> Self {
532        ReplyChain::Ack(ack)
533    }
534}
535
536#[cfg(test)]
537mod tests {
538    use super::*;
539    use crate::virtq::ring::tests::{make_producer, make_ring};
540    use crate::virtq::test_utils::*;
541
542    fn poll_data(
543        consumer: &mut VirtqConsumer<crate::virtq::ring::tests::TestMem, TestNotifier>,
544    ) -> (RecvChain, ReplyChain<crate::virtq::ring::tests::TestMem>) {
545        consumer.poll(1024).unwrap().unwrap()
546    }
547
548    #[test]
549    fn test_write_only_recv_is_empty() {
550        let ring = make_ring(16);
551        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
552
553        let se = producer.chain().writable(16).build().unwrap();
554        producer.submit(se).unwrap();
555
556        let (recv, reply) = poll_data(&mut consumer);
557        assert!(recv.to_bytes().is_empty());
558        assert!(matches!(reply, ReplyChain::Writable(_)));
559
560        if let ReplyChain::Writable(mut wc) = reply {
561            wc.write_all(b"response").unwrap();
562            consumer.complete(wc).unwrap();
563        }
564    }
565
566    #[test]
567    fn test_read_only_ack_reply() {
568        let ring = make_ring(16);
569        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
570
571        let mut se = producer.chain().readable(16).build().unwrap();
572        se.write_all(b"hello").unwrap();
573        producer.submit(se).unwrap();
574
575        let (recv, reply) = poll_data(&mut consumer);
576        assert_eq!(recv.to_bytes().as_ref(), b"hello");
577        assert!(matches!(reply, ReplyChain::Ack(_)));
578
579        consumer.complete(reply).unwrap();
580    }
581
582    #[test]
583    fn test_readwrite_round_trip() {
584        let ring = make_ring(16);
585        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
586
587        let mut se = producer.chain().readable(32).writable(64).build().unwrap();
588        se.write_all(b"hello world").unwrap();
589        producer.submit(se).unwrap();
590
591        let (recv, reply) = poll_data(&mut consumer);
592        assert_eq!(recv.to_bytes().as_ref(), b"hello world");
593
594        if let ReplyChain::Writable(mut wc) = reply {
595            assert_eq!(wc.capacity(), 64);
596            assert_eq!(wc.written(), 0);
597            assert_eq!(wc.remaining(), 64);
598            wc.write_all(b"response").unwrap();
599            assert_eq!(wc.written(), 8);
600            assert_eq!(wc.remaining(), 56);
601            consumer.complete(wc).unwrap();
602        } else {
603            panic!("expected Writable reply for recv+reply chain");
604        }
605    }
606
607    #[test]
608    fn test_writable_partial_write() {
609        let ring = make_ring(16);
610        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
611
612        let se = producer.chain().writable(8).build().unwrap();
613        producer.submit(se).unwrap();
614
615        let (_recv, reply) = poll_data(&mut consumer);
616
617        if let ReplyChain::Writable(mut wc) = reply {
618            let n = wc.write(b"hello world!").unwrap();
619            assert_eq!(n, 8);
620            assert_eq!(wc.remaining(), 0);
621            consumer.complete(wc).unwrap();
622        } else {
623            panic!("expected Writable");
624        }
625    }
626
627    #[test]
628    fn test_writable_write_all_too_large() {
629        let ring = make_ring(16);
630        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
631
632        let se = producer.chain().writable(4).build().unwrap();
633        producer.submit(se).unwrap();
634        let (_recv, reply) = poll_data(&mut consumer);
635
636        if let ReplyChain::Writable(mut wc) = reply {
637            let err = wc.write_all(b"too long").err().unwrap();
638            assert!(matches!(err, VirtqError::ReplyTooLarge));
639        } else {
640            panic!("expected Writable");
641        }
642    }
643
644    #[test]
645    fn test_poll_too_large_returns_payload_error() {
646        let ring = make_ring(16);
647        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
648
649        let mut se = producer.chain().readable(8).writable(16).build().unwrap();
650        se.write_all(b"too much").unwrap();
651        producer.submit(se).unwrap();
652
653        assert!(matches!(
654            consumer.poll(4),
655            Err(VirtqError::PayloadTooLarge { recv: 8, limit: 4 })
656        ));
657    }
658
659    #[test]
660    fn test_poll_too_large_returns_descriptor() {
661        let ring = make_ring(16);
662        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
663
664        let mut se = producer.chain().readable(8).writable(16).build().unwrap();
665        se.write_all(b"too much").unwrap();
666        let token = producer.submit(se).unwrap();
667
668        // Oversized payload is rejected, but the descriptor must be returned to
669        // the driver so the ring slot is not leaked.
670        assert!(matches!(
671            consumer.poll(4),
672            Err(VirtqError::PayloadTooLarge { recv: 8, limit: 4 })
673        ));
674
675        // The producer can reclaim the rejected chain; the queue is not wedged.
676        let used = producer.poll().unwrap().unwrap();
677        assert_eq!(used.token(), token);
678
679        // A subsequent normal exchange still round-trips end to end.
680        let se2 = producer.chain().writable(16).build().unwrap();
681        producer.submit(se2).unwrap();
682        let (_recv, reply) = poll_data(&mut consumer);
683        consumer.complete(reply).unwrap();
684        assert!(producer.poll().unwrap().is_some());
685    }
686
687    #[test]
688    fn test_villain_indirect_descriptor_does_not_mark_high_level_inflight() {
689        let ring = make_ring(16);
690        let mem = ring.mem();
691        let mut consumer = VirtqConsumer::new(ring.layout(), mem, TestNotifier::new());
692
693        let mut desc = Descriptor::new(0x1000, 16, 0, DescFlags::INDIRECT);
694        desc.mark_avail(true);
695        ring.write_desc(0, desc);
696
697        assert!(matches!(
698            consumer.poll(1024),
699            Err(VirtqError::RingError(RingError::BadChain))
700        ));
701        assert_eq!(consumer.inflight.count_ones(..), 0);
702        assert_eq!(consumer.inner.num_inflight(), 0);
703    }
704
705    #[test]
706    fn test_villain_bad_chain_does_not_mark_high_level_inflight() {
707        let ring = make_ring(16);
708        let mem = ring.mem();
709        let mut consumer = VirtqConsumer::new(ring.layout(), mem, TestNotifier::new());
710
711        let mut first = Descriptor::new(0x1000, 16, 0, DescFlags::NEXT | DescFlags::WRITE);
712        first.mark_avail(true);
713        ring.write_desc(0, first);
714
715        let mut second = Descriptor::new(0x2000, 16, 0, DescFlags::empty());
716        second.mark_avail(true);
717        ring.write_desc(1, second);
718
719        assert!(matches!(
720            consumer.poll(1024),
721            Err(VirtqError::RingError(RingError::BadChain))
722        ));
723        assert_eq!(consumer.inflight.count_ones(..), 0);
724        assert_eq!(consumer.inner.num_inflight(), 0);
725    }
726
727    #[test]
728    fn test_writable_chain_writes_single_segment() {
729        let ring = make_ring(16);
730        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
731
732        let se = producer.chain().writable(16).build().unwrap();
733        producer.submit(se).unwrap();
734        let (_recv, reply) = poll_data(&mut consumer);
735
736        let ReplyChain::Writable(mut wc) = reply else {
737            panic!("expected Writable");
738        };
739        wc.write_all(b"hello").unwrap();
740        consumer.complete(wc).unwrap();
741
742        let used = producer.poll().unwrap().unwrap();
743        assert_eq!(used.to_bytes().unwrap().as_ref(), b"hello");
744    }
745
746    #[test]
747    fn test_writable_rewind() {
748        let ring = make_ring(16);
749        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
750
751        let se = producer.chain().writable(16).build().unwrap();
752        producer.submit(se).unwrap();
753
754        let (_recv, reply) = poll_data(&mut consumer);
755
756        if let ReplyChain::Writable(mut wc) = reply {
757            wc.write_all(b"first").unwrap();
758            assert_eq!(wc.written(), 5);
759            wc.rewind();
760            assert_eq!(wc.written(), 0);
761            assert_eq!(wc.remaining(), 16);
762            wc.write_all(b"second").unwrap();
763            assert_eq!(wc.written(), 6);
764            consumer.complete(wc).unwrap();
765        } else {
766            panic!("expected Writable");
767        }
768    }
769
770    #[test]
771    fn test_writable_reply_scatters_across_segments() {
772        let ring = make_ring(16);
773        let mem = ring.mem();
774        let mut ring_producer = make_producer(&ring);
775        let mut consumer = VirtqConsumer::new(ring.layout(), mem.clone(), TestNotifier::new());
776
777        let base = mem.base_addr() + Layout::query_size(ring.len()) as u64 + 0x100;
778        let chain = BufferChainBuilder::new()
779            .writable(base, 4)
780            .writable(base + 4, 4)
781            .build()
782            .unwrap();
783        let id = ring_producer.submit_available(&chain).unwrap();
784
785        let (recv, reply) = poll_data(&mut consumer);
786        assert!(recv.to_bytes().is_empty());
787
788        let ReplyChain::Writable(mut wc) = reply else {
789            panic!("expected Writable");
790        };
791        assert_eq!(wc.capacity(), 8);
792        wc.write_all(b"abcdefgh").unwrap();
793        assert_eq!(wc.written(), 8);
794        consumer.complete(wc).unwrap();
795
796        let mut first = [0u8; 4];
797        let mut second = [0u8; 4];
798        mem.read(base, &mut first).unwrap();
799        mem.read(base + 4, &mut second).unwrap();
800        assert_eq!(&first, b"abcd");
801        assert_eq!(&second, b"efgh");
802
803        let used = ring_producer.poll_used().unwrap();
804        assert_eq!(used.id, id);
805        assert_eq!(used.len, 8);
806    }
807
808    #[test]
809    fn test_multiple_pending_replies() {
810        let ring = make_ring(16);
811        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
812
813        let se1 = producer.chain().writable(16).build().unwrap();
814        producer.submit(se1).unwrap();
815        let se2 = producer.chain().writable(16).build().unwrap();
816        producer.submit(se2).unwrap();
817
818        let (_e1, c1) = poll_data(&mut consumer);
819        let (_e2, c2) = poll_data(&mut consumer);
820
821        // Complete in reverse order
822        consumer.complete(c2).unwrap();
823        consumer.complete(c1).unwrap();
824    }
825
826    #[test]
827    fn test_recv_into_bytes() {
828        let ring = make_ring(16);
829        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
830
831        let mut se = producer.chain().readable(16).build().unwrap();
832        se.write_all(b"abc").unwrap();
833        producer.submit(se).unwrap();
834
835        let (recv, reply) = poll_data(&mut consumer);
836        let data = recv.into_bytes();
837        assert_eq!(data.as_ref(), b"abc");
838        consumer.complete(reply).unwrap();
839    }
840
841    #[test]
842    fn test_virtq_consumer_reset() {
843        let ring = make_ring(16);
844        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
845
846        // Submit and poll (but do not complete)
847        let se = producer.chain().writable(16).build().unwrap();
848        producer.submit(se).unwrap();
849
850        let (_recv, reply) = poll_data(&mut consumer);
851        assert!(consumer.inflight.count_ones(..) > 0);
852
853        // Complete first so we do not leak
854        consumer.complete(reply).unwrap();
855
856        consumer.reset();
857
858        assert_eq!(consumer.inflight.count_ones(..), 0);
859        assert_eq!(consumer.inner.num_inflight(), 0);
860    }
861
862    #[test]
863    fn test_virtq_consumer_reset_clears_inflight() {
864        let ring = make_ring(16);
865        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
866
867        // Submit two entries and poll both
868        let se1 = producer.chain().writable(16).build().unwrap();
869        producer.submit(se1).unwrap();
870        let se2 = producer.chain().writable(16).build().unwrap();
871        producer.submit(se2).unwrap();
872
873        let (_e1, c1) = poll_data(&mut consumer);
874        let (_e2, c2) = poll_data(&mut consumer);
875        // Complete both before reset
876        consumer.complete(c1).unwrap();
877        consumer.complete(c2).unwrap();
878
879        consumer.reset();
880
881        assert_eq!(consumer.inflight.count_ones(..), 0);
882        assert_eq!(consumer.inner.num_inflight(), 0);
883    }
884}