Skip to main content

hyperlight_common/virtq/
consumer.rs

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