Skip to main content

hyperlight_common/virtq/
producer.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::collections::VecDeque;
18use alloc::vec::Vec;
19
20use bytes::Bytes;
21use smallvec::SmallVec;
22
23use super::*;
24
25/// A used chain observed by the driver (producer) side.
26///
27/// Read-only chains are returned as [`Ack`](Self::Ack). Chains with a writable
28/// buffer complete as [`Data`](Self::Data), even when the device wrote zero
29/// bytes. Non-empty segments in [`Data`](Self::Data) are backed by
30/// shared-memory pool allocations that are returned when the last clone is
31/// dropped.
32#[derive(Debug)]
33pub enum UsedChain {
34    /// Acknowledgement for a read-only/fire-and-forget chain.
35    Ack(Token),
36    /// Data written by the consumer into the chain's writable buffers.
37    ///
38    /// The payload may contain zero bytes when the consumer writes zero bytes;
39    /// that is still a data used chain because the submitted chain had
40    /// writable capacity.
41    Data(Token, Segments),
42}
43
44impl UsedChain {
45    /// Token identifying which submitted chain this used chain corresponds to.
46    pub fn token(&self) -> Token {
47        match self {
48            Self::Ack(token) | Self::Data(token, _) => *token,
49        }
50    }
51
52    /// Data written by the consumer as contiguous bytes, if this chain has data.
53    pub fn to_bytes(&self) -> Option<Bytes> {
54        match self {
55            Self::Ack(_) => None,
56            Self::Data(_, segments) => Some(segments.to_bytes()),
57        }
58    }
59
60    /// Segments written by the consumer, if this chain has data.
61    pub fn segments(&self) -> Option<&Segments> {
62        match self {
63            Self::Ack(_) => None,
64            Self::Data(_, segments) => Some(segments),
65        }
66    }
67
68    /// Consume the used chain and return written data as contiguous bytes, if present.
69    pub fn into_bytes(self) -> Option<Bytes> {
70        match self {
71            Self::Ack(_) => None,
72            Self::Data(_, segments) => Some(segments.into_bytes()),
73        }
74    }
75
76    /// Consume the used chain and return written segments, if present.
77    pub fn into_segments(self) -> Option<Segments> {
78        match self {
79            Self::Ack(_) => None,
80            Self::Data(_, segments) => Some(segments),
81        }
82    }
83}
84
85/// Allocation tracking for an in-flight descriptor chain.
86///
87/// Descriptor lengths have already been published to the ring, so in-flight
88/// state only needs the completion token and allocation ownership for later
89/// reclaim.
90#[derive(Debug)]
91pub(crate) struct Inflight {
92    token: Token,
93    chain: BufferChain,
94}
95
96/// A high-level virtqueue producer (driver side).
97///
98/// The producer sends chains to the consumer (device), and receives used chains.
99/// This is used on the driver/guest side.
100///
101/// # Threading
102///
103/// The producer is intended for single-threaded, guest-side use. Reply payloads
104/// are exposed as zero-copy [`Bytes`] via [`Bytes::from_owner`](bytes::Bytes::from_owner),
105/// which requires the owning pool to be `Send`. Do not move the producer
106/// or its replies across threads, and do not instantiate it on the multi-threaded
107/// host with those pools.
108///
109/// # Example
110///
111/// ```ignore
112/// let mut producer = VirtqProducer::new(layout, mem, notifier, pool);
113///
114/// // Build and submit a chain
115/// let mut chain = producer.chain().readable(64).writable(64).build()?;
116/// chain.write_all(b"hello")?;
117/// let token = producer.submit(chain)?;
118///
119/// // Later, poll for the used chain
120/// if let Some(used) = producer.poll()? {
121///     assert_eq!(used.token(), token);
122///     match used {
123///         UsedChain::Data(_, segments) => println!("Got used chain: {:?}", segments),
124///         UsedChain::Ack(_) => println!("Got ack"),
125///     }
126/// }
127/// ```
128pub struct VirtqProducer<M, N, P> {
129    inner: RingProducer<M>,
130    notifier: N,
131    pool: P,
132    next_token: u32,
133    inflight: Vec<Option<Inflight>>,
134    pending: VecDeque<UsedChain>,
135}
136
137impl<M, N, P> VirtqProducer<M, N, P>
138where
139    M: MemOps + Clone,
140    N: Notifier,
141    P: BufferProvider + Clone,
142{
143    /// Create a new virtqueue producer.
144    ///
145    /// # Arguments
146    ///
147    /// * `layout` - Ring memory layout (descriptor table and event suppression addresses)
148    /// * `mem` - Memory operations implementation for reading/writing to shared memory
149    /// * `notifier` - Callback for notifying the device (consumer) about new chains
150    /// * `pool` - Buffer allocator for chain payload and reply data
151    pub fn new(layout: Layout, mem: M, notifier: N, pool: P) -> Self {
152        let inner = RingProducer::new(layout, mem);
153        let ring_len = inner.len();
154
155        Self {
156            inner,
157            pool,
158            notifier,
159            next_token: 0,
160            inflight: (0..ring_len).map(|_| None).collect(),
161            pending: VecDeque::with_capacity(ring_len),
162        }
163    }
164
165    fn dealloc_elems(
166        &self,
167        elems: impl IntoIterator<Item = BufferElement>,
168    ) -> Result<(), VirtqError> {
169        let mut first_err = None;
170        for elem in elems {
171            if let Err(err) = self.pool.dealloc(elem.addr)
172                && first_err.is_none()
173            {
174                first_err = Some(VirtqError::Alloc(err));
175            }
176        }
177
178        if let Some(err) = first_err {
179            return Err(err);
180        }
181
182        Ok(())
183    }
184
185    /// Begin building a descriptor chain for submission.
186    ///
187    /// Returns a [`ChainBuilder`] that allocates buffers from the pool.
188    pub fn chain(&self) -> ChainBuilder<M, P> {
189        ChainBuilder::new(self.inner.mem().clone(), self.pool.clone())
190    }
191
192    /// Begin a batch of submissions.
193    ///
194    /// Chains submitted through the returned [`SubmitBatch`] are published to
195    /// the ring immediately, but the consumer is notified at most once when
196    /// [`SubmitBatch::finish`] is called. This mirrors the virtio pattern of
197    /// adding multiple buffers and then kicking the queue once.
198    pub fn batch(&mut self) -> SubmitBatch<'_, M, N, P> {
199        SubmitBatch::new(self)
200    }
201
202    /// Submit a [`SendChain`] to the ring.
203    ///
204    /// Publishes the descriptor chain, stores the in-flight tracking state,
205    /// and notifies the consumer if event suppression allows. Notifications
206    /// are layout-neutral; use [`batch`](Self::batch) when a higher-level
207    /// protocol wants to publish multiple chains and kick once.
208    ///
209    /// # Errors
210    ///
211    /// - [`VirtqError::PayloadTooLarge`] - written exceeds readable buffer capacity
212    /// - [`VirtqError::RingError`] - ring is full
213    /// - [`VirtqError::InvalidState`] - descriptor ID collision
214    pub fn submit(&mut self, chain: SendChain<M, P>) -> Result<Token, VirtqError> {
215        let cursor_before = self.inner.avail_cursor();
216        let token = self.publish(chain)?;
217        self.notify_since(cursor_before)?;
218        Ok(token)
219    }
220
221    fn publish(&mut self, send: SendChain<M, P>) -> Result<Token, VirtqError> {
222        let token_id = self.next_token;
223        let id = self.inner.submit_available(send.chain())?;
224        let token = Token { seq: token_id, id };
225
226        // A free descriptor id must never already be tracked as inflight.
227        if self.inflight[id as usize].is_some() {
228            return Err(VirtqError::InvalidState);
229        }
230
231        let inf = send.into_inflight(token);
232        self.inflight[id as usize] = Some(inf);
233        self.next_token = self.next_token.wrapping_add(1);
234
235        Ok(token)
236    }
237
238    fn notify_since(&mut self, cursor: RingCursor) -> Result<bool, VirtqError> {
239        let should_notify = self.inner.should_notify_since(cursor)?;
240        if should_notify {
241            self.notify_now();
242        }
243        Ok(should_notify)
244    }
245
246    fn notify_now(&self) {
247        self.notifier.notify(QueueStats {
248            num_free: self.inner.num_free(),
249            num_inflight: self.inner.num_inflight(),
250        });
251    }
252
253    /// Signal backpressure to the consumer.
254    ///
255    /// Bypasses event suppression. Call this when submit fails with a
256    /// backpressure error and the consumer needs to drain.
257    #[inline]
258    pub fn notify_backpressure(&self) {
259        self.notify_now();
260    }
261
262    /// Get the current used cursor position.
263    ///
264    /// Useful for setting up descriptor-based event suppression.
265    #[inline]
266    pub fn used_cursor(&self) -> RingCursor {
267        self.inner.used_cursor()
268    }
269
270    /// Number of free (unsubmitted) descriptors in the ring.
271    #[inline]
272    pub fn num_free(&self) -> usize {
273        self.inner.num_free()
274    }
275
276    /// Configure event suppression for used buffer notifications.
277    ///
278    /// This controls when the device (consumer) signals us about completed buffers:
279    ///
280    /// - [`SuppressionKind::Enable`]: Always signal (default) - good for latency
281    /// - [`SuppressionKind::Disable`]: Never signal - caller must poll
282    /// - [`SuppressionKind::Descriptor`]: Signal only at specific cursor position
283    ///
284    /// # Example: Used-chain batching
285    ///
286    /// ```ignore
287    /// // Submit chains, then suppress notifications until all are used
288    /// let mut se = producer.chain().readable(64).writable(128).build()?;
289    /// se.write_all(b"entry1")?;
290    /// producer.submit(se)?;
291    /// let cursor = producer.used_cursor();
292    /// producer.set_used_suppression(SuppressionKind::Descriptor(cursor))?;
293    /// // Device will notify only after reaching that cursor position
294    /// ```
295    pub fn set_used_suppression(&mut self, kind: SuppressionKind) -> Result<(), VirtqError> {
296        match kind {
297            SuppressionKind::Enable => self.inner.enable_used_notifications()?,
298            SuppressionKind::Disable => self.inner.disable_used_notifications()?,
299            SuppressionKind::Descriptor(cursor) => self
300                .inner
301                .enable_used_notifications_desc(cursor.head(), cursor.wrap())?,
302        }
303        Ok(())
304    }
305
306    /// Reset ring, inflight, and pool state to initial values.
307    ///
308    /// # Safety
309    ///
310    /// No outstanding [`UsedChain::Data`] buffers, borrowed segment views, or
311    /// peer accesses to previously submitted descriptors may exist. Resetting
312    /// recycles the same backing addresses, so outstanding zero-copy buffers or
313    /// stale descriptor users could alias memory that is handed out again.
314    ///
315    /// TODO(virtq): find a way to allow guest to keep used chains across resets.
316    pub unsafe fn reset(&mut self) {
317        self.inflight.iter_mut().for_each(|slot| *slot = None);
318        self.pending.clear();
319        self.inner.reset();
320        self.pool.reset();
321    }
322
323    /// Replace the pool and reset ring, inflight, and pending state.
324    ///
325    /// # Safety
326    ///
327    /// No outstanding [`UsedChain::Data`] buffers, borrowed segment views, or
328    /// peer accesses to previously submitted descriptors may exist. The new pool
329    /// may manage the same shared-memory addresses as the old pool, so old
330    /// zero-copy buffers must not outlive this transition.
331    pub unsafe fn reset_with_pool(&mut self, pool: P) {
332        self.pending.clear();
333        self.inflight.iter_mut().for_each(|slot| *slot = None);
334        self.inner.reset();
335        self.pool = pool;
336        self.pool.reset();
337    }
338}
339
340impl<M, N, P> VirtqProducer<M, N, P>
341where
342    M: MemOps + Clone + Send + 'static,
343    N: Notifier,
344    P: BufferProvider + Clone + Send + 'static,
345{
346    /// Poll for a single used chain from the device.
347    ///
348    /// Returns buffered used chains from prior [`reclaim`](Self::reclaim)
349    /// calls first, then checks the ring for newly used chains.
350    ///
351    /// Returns `Ok(Some(used))` if a used chain is available, `Ok(None)` if no
352    /// used chains are ready (would block), or an error if the device misbehaved.
353    ///
354    /// Data used chains contain zero-copy [`Bytes`] backed by the shared-memory
355    /// allocation via [`BufferOwner`]. The pool allocation is held alive as long
356    /// as any `Bytes` clone exists, and is returned to the pool when the last
357    /// clone is dropped.
358    ///
359    /// # Errors
360    ///
361    /// - [`VirtqError::InvalidState`] - Device returned invalid descriptor ID or
362    ///   wrote more data than the writable buffer capacity
363    pub fn poll(&mut self) -> Result<Option<UsedChain>, VirtqError> {
364        if let Some(chain) = self.pending.pop_front() {
365            return Ok(Some(chain));
366        }
367        self.poll_ring()
368    }
369
370    /// Reclaim ring slots and pool allocations from used descriptors.
371    ///
372    /// Processes all available used chains from the ring: frees readable
373    /// buffer allocations immediately, and buffers writable data for
374    /// later retrieval via [`poll`](Self::poll).
375    ///
376    /// Read-only ack used chains are discarded immediately.
377    ///
378    /// Use this to free resources under backpressure without losing
379    /// writable data. Returns the number of chains reclaimed.
380    pub fn reclaim(&mut self) -> Result<usize, VirtqError> {
381        let mut count = 0;
382        while let Some(chain) = self.poll_ring()? {
383            if matches!(chain, UsedChain::Data(_, _)) {
384                debug_assert!(self.pending.len() < self.inner.len());
385                self.pending.push_back(chain);
386            }
387            count += 1;
388        }
389        Ok(count)
390    }
391
392    /// Poll one used chain directly from the ring (bypassing pending buffer).
393    fn poll_ring(&mut self) -> Result<Option<UsedChain>, VirtqError> {
394        let used = match self.inner.poll_used() {
395            Ok(u) => u,
396            Err(RingError::WouldBlock) => return Ok(None),
397            Err(e) => return Err(e.into()),
398        };
399
400        let inf = self
401            .inflight
402            .get_mut(used.id as usize)
403            .and_then(Option::take)
404            .ok_or(VirtqError::InvalidState)?;
405
406        let written = used.len as usize;
407        let Inflight { token, chain } = inf;
408
409        self.dealloc_elems(chain.readables().iter().copied())?;
410
411        let used = if chain.writables().is_empty() {
412            UsedChain::Ack(token)
413        } else {
414            UsedChain::Data(token, self.recv_segments(chain.writables(), written)?)
415        };
416
417        Ok(Some(used))
418    }
419
420    fn recv_segments(
421        &self,
422        writables: &[BufferElement],
423        written: usize,
424    ) -> Result<Segments, VirtqError> {
425        let mut owned = SmallVec::<[(BufferElement, usize); 4]>::new();
426        let mut free = SmallVec::<[BufferElement; 4]>::new();
427        let mut remaining = written;
428
429        for &alloc in writables {
430            if remaining == 0 {
431                free.push(alloc);
432                continue;
433            }
434
435            let len = remaining.min(alloc.len as usize);
436            owned.push((alloc, len));
437            remaining -= len;
438        }
439
440        if remaining != 0 {
441            let elems = owned.iter().map(|(elem, _)| *elem).chain(free);
442            self.dealloc_elems(elems)?;
443            return Err(VirtqError::InvalidState);
444        }
445
446        for (elem, len) in &owned {
447            if unsafe { self.inner.mem().as_slice(elem.addr, *len) }.is_err() {
448                let elems = owned.iter().map(|(elem, _)| *elem).chain(free);
449                let _ = self.dealloc_elems(elems);
450                return Err(VirtqError::MemoryReadError);
451            }
452        }
453
454        let mut sgs = SmallVec::<[Bytes; 4]>::new();
455        for (elem, written) in owned {
456            let alloc = OwnedAlloc::new(
457                self.pool.clone(),
458                Allocation {
459                    addr: elem.addr,
460                    len: elem.len as usize,
461                },
462            );
463            let mem = self.inner.mem().clone();
464            let owner = BufferOwner {
465                alloc,
466                mem,
467                written,
468            };
469            sgs.push(Bytes::from_owner(owner));
470        }
471
472        self.dealloc_elems(free)?;
473
474        Ok(Segments::from_smallvec(sgs))
475    }
476
477    /// Drain all available used chains, calling the provided closure for each.
478    ///
479    /// This is a convenience method that repeatedly calls [`poll`](Self::poll)
480    /// until no more used chains are available.
481    ///
482    /// # Arguments
483    ///
484    /// * `f` - Closure called for each used chain
485    ///
486    /// # Example
487    ///
488    /// ```ignore
489    /// producer.drain(|used| {
490    ///     println!("Got used chain for {:?}", used.token());
491    /// })?;
492    /// ```
493    pub fn drain(&mut self, mut f: impl FnMut(UsedChain)) -> Result<(), VirtqError> {
494        while let Some(chain) = self.poll()? {
495            f(chain);
496        }
497
498        Ok(())
499    }
500}
501
502/// A scoped batch of producer submissions.
503///
504/// Submissions are published immediately, while notification is delayed until
505/// [`finish`](Self::finish). `finish` is explicit because the event-suppression
506/// check can fail; dropping a batch does not notify.
507#[must_use = "call finish to notify the consumer about batched submissions"]
508pub struct SubmitBatch<'a, M, N, P> {
509    producer: &'a mut VirtqProducer<M, N, P>,
510    notify_from: Option<RingCursor>,
511}
512
513impl<'a, M, N, P> SubmitBatch<'a, M, N, P>
514where
515    M: MemOps + Clone,
516    N: Notifier,
517    P: BufferProvider + Clone,
518{
519    fn new(producer: &'a mut VirtqProducer<M, N, P>) -> Self {
520        Self {
521            producer,
522            notify_from: None,
523        }
524    }
525
526    /// Begin building a descriptor chain for this batch.
527    pub fn chain(&self) -> ChainBuilder<M, P> {
528        self.producer.chain()
529    }
530
531    /// Publish a chain as part of this batch without notifying yet.
532    pub fn submit(&mut self, chain: SendChain<M, P>) -> Result<Token, VirtqError> {
533        let cursor_before = self.producer.inner.avail_cursor();
534        let token = self.producer.publish(chain)?;
535
536        if self.notify_from.is_none() {
537            self.notify_from = Some(cursor_before);
538        }
539        Ok(token)
540    }
541
542    /// Finish the batch and notify the consumer once if event suppression
543    /// requires it for the whole published range.
544    ///
545    /// Returns `true` if a notification was sent.
546    pub fn finish(mut self) -> Result<bool, VirtqError> {
547        let Some(notify_from) = self.notify_from.take() else {
548            return Ok(false);
549        };
550
551        self.producer.notify_since(notify_from)
552    }
553}
554
555/// Builder for configuring a descriptor chain's buffer layout.
556///
557/// If dropped without building, no resources are leaked (allocations are
558/// deferred to [`build`](Self::build)).
559#[must_use = "call .build() to create a SendChain"]
560pub struct ChainBuilder<M: MemOps, P: BufferProvider + Clone> {
561    mem: M,
562    pool: P,
563    rd_caps: SmallVec<[usize; 4]>,
564    wr_caps: SmallVec<[usize; 4]>,
565}
566
567impl<M: MemOps, P: BufferProvider + Clone> ChainBuilder<M, P> {
568    fn new(mem: M, pool: P) -> Self {
569        Self {
570            mem,
571            pool,
572            rd_caps: SmallVec::new(),
573            wr_caps: SmallVec::new(),
574        }
575    }
576
577    /// Request a device-readable buffer of `cap` bytes.
578    ///
579    /// The producer writes data into readable buffers before submission; the
580    /// consumer reads that data after polling the chain.
581    /// The actual allocation is deferred to [`build`](Self::build).
582    pub fn readable(mut self, cap: usize) -> Self {
583        self.rd_caps.push(cap);
584        self
585    }
586
587    /// Request a device-writable buffer of `cap` bytes.
588    ///
589    /// The writable buffer is filled by the consumer and returned via
590    /// [`VirtqProducer::poll`] as [`UsedChain`].
591    ///
592    /// Multiple writable buffers are completed as ordered [`Segments`]. The
593    /// consumer writes them sequentially, because the virtio used ring reports
594    /// one aggregate written length rather than per-descriptor lengths.
595    pub fn writable(mut self, cap: usize) -> Self {
596        self.wr_caps.push(cap);
597        self
598    }
599
600    /// Allocate buffers and return a [`SendChain`] for writing.
601    ///
602    /// # Errors
603    ///
604    /// - [`VirtqError::InvalidState`] - No buffers requested
605    /// - [`VirtqError::Alloc`] - Pool exhausted
606    pub fn build(self) -> Result<SendChain<M, P>, VirtqError> {
607        if self.rd_caps.is_empty() && self.wr_caps.is_empty() {
608            return Err(VirtqError::InvalidState);
609        }
610
611        let mut rollback = Rollback::new(&self.pool);
612        let mut rd_caps = SmallVec::<[usize; 4]>::new();
613        let mut rd_elems = SmallVec::<[BufferElement; 4]>::new();
614        let mut wr_elems = SmallVec::<[BufferElement; 4]>::new();
615
616        // Allocate readable buffers, splitting into multiple descriptors if needed.
617        // The buffer element lengths are initialized to zero and updated as the
618        // `SendChain` writes.
619        for &cap in &self.rd_caps {
620            let sgs = self.pool.alloc_sg(cap)?;
621            let mut remaining = cap;
622
623            for alloc in sgs {
624                let _ = checked_descriptor_len(alloc.len)?;
625                let seg_cap = remaining.min(alloc.len);
626
627                rd_caps.push(seg_cap);
628                rd_elems.push(BufferElement {
629                    addr: alloc.addr,
630                    len: 0,
631                    writable: false,
632                });
633                remaining -= seg_cap;
634                rollback.allocs.push(alloc);
635            }
636
637            if remaining != 0 {
638                return Err(VirtqError::InvalidState);
639            }
640        }
641
642        // Allocate writable buffers, with the same caveat about splitting as readable buffers.
643        // Writable buffer elements are initialized with their full capacity for the device to
644        // write into.
645        for &cap in &self.wr_caps {
646            let sgs = self.pool.alloc_sg(cap)?;
647            for alloc in sgs {
648                let len = checked_descriptor_len(alloc.len)?;
649                wr_elems.push(BufferElement {
650                    addr: alloc.addr,
651                    len,
652                    writable: true,
653                });
654                rollback.allocs.push(alloc);
655            }
656        }
657
658        let chain = BufferChainBuilder::new()
659            .readables(rd_elems)
660            .writables(wr_elems)
661            .build()?;
662
663        rollback.release();
664
665        Ok(SendChain {
666            mem: self.mem,
667            pool: self.pool,
668            chain: Some(chain),
669            rd_caps,
670            rd_capacity: self.rd_caps.iter().sum(),
671            rd_written: 0,
672            write_mode: WriteMode::Unset,
673        })
674    }
675}
676
677struct Rollback<'a, P: BufferProvider> {
678    pool: &'a P,
679    allocs: SmallVec<[Allocation; 8]>,
680}
681
682impl<'a, P: BufferProvider> Rollback<'a, P> {
683    fn new(pool: &'a P) -> Self {
684        Self {
685            pool,
686            allocs: SmallVec::new(),
687        }
688    }
689
690    fn release(mut self) {
691        self.allocs.clear();
692    }
693}
694
695impl<P: BufferProvider> Drop for Rollback<'_, P> {
696    fn drop(&mut self) {
697        for alloc in self.allocs.drain(..) {
698            let result = self.pool.dealloc(alloc.addr);
699            debug_assert!(result.is_ok(), "rollback dealloc failed: {result:?}");
700        }
701    }
702}
703
704/// Tracks which write API a [`SendChain`] payload uses, so the two paths are
705/// not mixed.
706///
707/// Copy writes ([`SendChain::write`]/[`SendChain::write_all`]) append at an
708/// aggregate cursor, while direct writes
709/// ([`SendChain::write_seg`]/[`SendChain::with_seg`]) set per-segment lengths
710/// absolutely. Mixing them would corrupt the written-length accounting.
711#[derive(Debug, Clone, Copy, PartialEq, Eq)]
712enum WriteMode {
713    Unset,
714    Append,
715    Direct,
716}
717
718/// A configured send chain ready for writing and submission.
719///
720/// Created by [`ChainBuilder::build`]. Write readable payload bytes directly on
721/// the chain, then submit via [`VirtqProducer::submit`].
722///
723/// # Examples
724///
725/// ```ignore
726/// let mut sc = producer.chain().readable(64).writable(128).build()?;
727/// sc.write_all(b"header")?.write_all(b" body")?;
728/// let tok = producer.submit(sc)?;
729///
730/// let mut sc = producer.chain().readable(128).build()?;
731/// sc.with_seg(0, |buf| serialize_into(buf))?;
732/// let tok = producer.submit(sc)?;
733/// ```
734///
735/// Copy writes (`write`/`write_all`) and direct writes (`write_seg`/`with_seg`)
736/// must not be mixed on the same chain; doing so panics in debug builds.
737///
738/// If dropped without submitting, allocated buffers are returned to the pool.
739#[must_use = "dropping without submitting deallocates the buffers"]
740pub struct SendChain<M: MemOps, P: BufferProvider> {
741    mem: M,
742    pool: P,
743    chain: Option<BufferChain>,
744    rd_caps: SmallVec<[usize; 4]>,
745    rd_capacity: usize,
746    rd_written: usize,
747    write_mode: WriteMode,
748}
749
750// `chain` is wrapped in `Option` only so `into_inflight` can `take()` it
751// without moving out of this `Drop` type; it stays `Some` for a chain's whole
752// public lifetime, so these `expect`s cannot fail.
753#[allow(clippy::expect_used)]
754impl<M: MemOps, P: BufferProvider> SendChain<M, P> {
755    fn chain(&self) -> &BufferChain {
756        self.chain.as_ref().expect("SendChain missing BufferChain")
757    }
758
759    fn chain_mut(&mut self) -> &mut BufferChain {
760        self.chain.as_mut().expect("SendChain missing BufferChain")
761    }
762
763    /// Record that this chain uses `mode`, asserting it is not mixed with the
764    /// other write path.
765    fn note_write_mode(&mut self, mode: WriteMode) {
766        debug_assert!(
767            self.write_mode == WriteMode::Unset || self.write_mode == mode,
768            "SendChain mixes copy writes (write/write_all) with direct writes (write_seg/with_seg)"
769        );
770        self.write_mode = mode;
771    }
772
773    fn into_inflight(mut self, token: Token) -> Inflight {
774        let chain = self.chain.take().expect("SendChain missing BufferChain");
775        Inflight { token, chain }
776    }
777
778    /// Number of producer-written readable segments in this chain.
779    pub fn segment_count(&self) -> usize {
780        self.chain().readables().len()
781    }
782
783    /// Total producer-written readable capacity in bytes.
784    pub fn capacity(&self) -> usize {
785        self.rd_capacity
786    }
787
788    /// Number of producer-written readable bytes written so far.
789    pub fn written(&self) -> usize {
790        self.rd_written
791    }
792
793    /// Remaining producer-written readable capacity.
794    pub fn remaining(&self) -> usize {
795        self.capacity() - self.written()
796    }
797
798    /// Write bytes into payload segments, returning how many bytes were written.
799    ///
800    /// Appends at the current aggregate write position and scatters across
801    /// readable segments in chain order. Uses [`MemOps::write`] (volatile on
802    /// host side). If `buf` is larger than the remaining capacity, writes as
803    /// many bytes as will fit.
804    ///
805    /// # Errors
806    ///
807    /// - [`VirtqError::NoPayloadSegment`] - no readable buffer allocated
808    /// - [`VirtqError::MemoryWriteError`] - underlying write failed
809    pub fn write(&mut self, buf: &[u8]) -> Result<usize, VirtqError> {
810        if self.segment_count() == 0 {
811            return Err(VirtqError::NoPayloadSegment);
812        }
813
814        self.note_write_mode(WriteMode::Append);
815
816        let mut remaining = &buf[..buf.len().min(self.remaining())];
817        let mut written = 0;
818
819        let SendChain {
820            mem,
821            chain,
822            rd_caps,
823            ..
824        } = self;
825
826        let readables = chain
827            .as_mut()
828            .expect("SendChain missing BufferChain")
829            .readables_mut();
830
831        for (readable, &cap) in readables.iter_mut().zip(rd_caps.iter()) {
832            if remaining.is_empty() {
833                break;
834            }
835
836            let written_len = readable.len as usize;
837            let free = cap - written_len;
838            if free == 0 {
839                continue;
840            }
841
842            let n = free.min(remaining.len());
843            let addr = readable.addr + written_len as u64;
844            mem.write(addr, &remaining[..n])
845                .map_err(|_| VirtqError::MemoryWriteError)?;
846
847            readable.len += n as u32;
848            written += n;
849            remaining = &remaining[n..];
850        }
851
852        self.rd_written += written;
853        Ok(written)
854    }
855
856    /// Write the entire buffer into payload segments.
857    ///
858    /// Appends at the current aggregate write position and scatters across
859    /// readable segments in chain order. Uses [`MemOps::write`] (volatile on
860    /// host side).
861    ///
862    /// # Errors
863    ///
864    /// - [`VirtqError::PayloadTooLarge`] - buf exceeds remaining capacity
865    /// - [`VirtqError::NoPayloadSegment`] - no readable buffer allocated
866    /// - [`VirtqError::MemoryWriteError`] - underlying write failed
867    pub fn write_all(&mut self, buf: &[u8]) -> Result<&mut Self, VirtqError> {
868        if self.segment_count() == 0 {
869            return Err(VirtqError::NoPayloadSegment);
870        }
871
872        if buf.len() > self.remaining() {
873            return Err(VirtqError::PayloadTooLarge {
874                recv: buf.len(),
875                limit: self.remaining(),
876            });
877        }
878
879        let written = self.write(buf)?;
880        debug_assert_eq!(written, buf.len());
881        Ok(self)
882    }
883
884    /// Write bytes into one readable segment by index.
885    ///
886    /// Writes from the start of the selected segment and records `buf.len()` as
887    /// that segment's descriptor length.
888    ///
889    /// # Errors
890    ///
891    /// - [`VirtqError::NoPayloadSegment`] - `index` does not name a payload segment
892    /// - [`VirtqError::PayloadTooLarge`] - `buf` exceeds the segment capacity
893    /// - [`VirtqError::MemoryWriteError`] - underlying write failed
894    pub fn write_seg(&mut self, index: usize, buf: &[u8]) -> Result<&mut Self, VirtqError> {
895        self.note_write_mode(WriteMode::Direct);
896
897        let cap = *self
898            .rd_caps
899            .get(index)
900            .ok_or(VirtqError::NoPayloadSegment)?;
901
902        if buf.len() > cap {
903            return Err(VirtqError::PayloadTooLarge {
904                recv: buf.len(),
905                limit: cap,
906            });
907        }
908
909        let addr = self
910            .chain()
911            .readables()
912            .get(index)
913            .ok_or(VirtqError::NoPayloadSegment)?
914            .addr;
915
916        self.mem
917            .write(addr, buf)
918            .map_err(|_| VirtqError::MemoryWriteError)?;
919
920        let previous = self.chain().readables()[index].len as usize;
921        self.chain_mut().readables_mut()[index].len = checked_descriptor_len(buf.len())?;
922        self.rd_written = self.rd_written - previous + buf.len();
923        Ok(self)
924    }
925
926    /// Serialize directly into one readable segment by index.
927    ///
928    /// The closure returns the number of valid bytes it wrote. The written
929    /// length for that segment is recorded on success.
930    ///
931    /// # Errors
932    ///
933    /// - [`VirtqError::NoPayloadSegment`] - `index` does not name a payload segment
934    /// - [`VirtqError::PayloadTooLarge`] - closure reports more bytes than segment capacity
935    /// - [`VirtqError::MemoryWriteError`] - the memory backend cannot expose a mutable slice
936    pub fn with_seg<E>(
937        &mut self,
938        index: usize,
939        f: impl FnOnce(&mut [u8]) -> Result<usize, E>,
940    ) -> Result<&mut Self, E>
941    where
942        E: From<VirtqError>,
943    {
944        self.note_write_mode(WriteMode::Direct);
945
946        let cap = *self
947            .rd_caps
948            .get(index)
949            .ok_or_else(|| E::from(VirtqError::NoPayloadSegment))?;
950
951        let addr = self
952            .chain()
953            .readables()
954            .get(index)
955            .ok_or_else(|| E::from(VirtqError::NoPayloadSegment))?
956            .addr;
957
958        let buf = unsafe {
959            self.mem
960                .as_mut_slice(addr, cap)
961                .map_err(|_| E::from(VirtqError::MemoryWriteError))?
962        };
963
964        let written = f(buf)?;
965        if written > buf.len() {
966            return Err(E::from(VirtqError::PayloadTooLarge {
967                recv: written,
968                limit: buf.len(),
969            }));
970        }
971
972        let previous = self.chain().readables()[index].len as usize;
973        // SAFETY: index was validated by the earlier get() call, so the readable element exists.
974        self.chain_mut().readables_mut()[index].len =
975            checked_descriptor_len(written).map_err(E::from)?;
976        self.rd_written = self.rd_written - previous + written;
977
978        Ok(self)
979    }
980}
981
982impl<M: MemOps, P: BufferProvider> Drop for SendChain<M, P> {
983    fn drop(&mut self) {
984        if let Some(chain) = self.chain.take() {
985            for elem in chain.elems() {
986                let result = self.pool.dealloc(elem.addr);
987                debug_assert!(result.is_ok(), "SendChain drop dealloc failed: {result:?}");
988            }
989        }
990    }
991}
992
993fn checked_descriptor_len(len: usize) -> Result<u32, VirtqError> {
994    if len > u32::MAX as usize {
995        return Err(VirtqError::PayloadTooLarge {
996            recv: len,
997            limit: u32::MAX as usize,
998        });
999    }
1000    Ok(len as u32)
1001}
1002
1003#[cfg(test)]
1004mod tests {
1005    use super::*;
1006    use crate::virtq::ring::tests::{TestMem, make_ring};
1007    use crate::virtq::test_utils::*;
1008
1009    fn poll_received<M: MemOps + Clone, N: Notifier>(
1010        consumer: &mut VirtqConsumer<M, N>,
1011    ) -> (RecvChain, ReplyChain<M>) {
1012        consumer.poll(1024).unwrap().unwrap()
1013    }
1014
1015    #[derive(Clone)]
1016    struct NoDirectSliceMem(TestMem);
1017
1018    // SAFETY: Delegates all non-slice memory operations to TestMem. Direct
1019    // slices are intentionally unsupported to exercise producer error handling.
1020    unsafe impl MemOps for NoDirectSliceMem {
1021        type Error = ();
1022
1023        fn read(&self, addr: u64, dst: &mut [u8]) -> Result<(), Self::Error> {
1024            self.0.read(addr, dst).map_err(|e| match e {})
1025        }
1026
1027        fn write(&self, addr: u64, src: &[u8]) -> Result<(), Self::Error> {
1028            self.0.write(addr, src).map_err(|e| match e {})
1029        }
1030
1031        fn load_acquire(&self, addr: u64) -> Result<u16, Self::Error> {
1032            self.0.load_acquire(addr).map_err(|e| match e {})
1033        }
1034
1035        fn store_release(&self, addr: u64, val: u16) -> Result<(), Self::Error> {
1036            self.0.store_release(addr, val).map_err(|e| match e {})
1037        }
1038
1039        unsafe fn as_slice(&self, _addr: u64, _len: usize) -> Result<&[u8], Self::Error> {
1040            Err(())
1041        }
1042
1043        unsafe fn as_mut_slice(&self, _addr: u64, _len: usize) -> Result<&mut [u8], Self::Error> {
1044            Err(())
1045        }
1046    }
1047
1048    #[test]
1049    fn test_chain_readwrite_build() {
1050        let ring = make_ring(16);
1051        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1052
1053        let se = producer.chain().readable(64).writable(128).build().unwrap();
1054        assert_eq!(se.capacity(), 64);
1055        assert_eq!(se.written(), 0);
1056        assert_eq!(se.remaining(), 64);
1057    }
1058
1059    #[test]
1060    fn test_chain_readable_writable_names_build() {
1061        let ring = make_ring(16);
1062        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1063
1064        let se = producer.chain().readable(16).writable(32).build().unwrap();
1065        assert_eq!(se.segment_count(), 1);
1066        assert_eq!(se.capacity(), 16);
1067    }
1068
1069    #[test]
1070    fn test_chain_multi_readable_write_all_scatters() {
1071        let ring = make_ring(16);
1072        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1073
1074        let mut se = producer
1075            .chain()
1076            .readable(5)
1077            .readable(6)
1078            .writable(32)
1079            .build()
1080            .unwrap();
1081        se.write_all(b"hello world").unwrap();
1082
1083        assert_eq!(se.written(), 11);
1084
1085        let token = producer.submit(se).unwrap();
1086        let (recv, reply) = poll_received(&mut consumer);
1087        assert_eq!(recv.token(), token);
1088        assert_eq!(recv.to_bytes().as_ref(), b"hello world");
1089        assert_eq!(recv.segments().segment_count(), 2);
1090        assert_eq!(recv.segments().as_slice()[0].as_ref(), b"hello");
1091        assert_eq!(recv.segments().as_slice()[1].as_ref(), b" world");
1092        consumer.complete(reply).unwrap();
1093    }
1094
1095    #[test]
1096    fn test_chain_readable_splits_logical_capacity() {
1097        let ring = make_ring(16);
1098        let layout = ring.layout();
1099        let mem = ring.mem();
1100        let pool_base = mem.base_addr() + Layout::query_size(ring.len()) as u64 + 0x100;
1101        let pool = TestPool::new_with_max_alloc_len(pool_base, 0x8000, 4);
1102        let notifier = TestNotifier::new();
1103        let mut producer = VirtqProducer::new(layout, mem.clone(), notifier.clone(), pool);
1104        let mut consumer = VirtqConsumer::new(layout, mem, notifier);
1105
1106        let mut se = producer.chain().readable(10).writable(32).build().unwrap();
1107
1108        assert_eq!(se.segment_count(), 3);
1109        assert_eq!(se.capacity(), 10);
1110
1111        se.write_all(b"abcdefghij").unwrap();
1112        assert_eq!(se.written(), 10);
1113
1114        let token = producer.submit(se).unwrap();
1115        let (recv, reply) = poll_received(&mut consumer);
1116        assert_eq!(recv.token(), token);
1117        assert_eq!(recv.to_bytes().as_ref(), b"abcdefghij");
1118        assert_eq!(recv.segments().segment_count(), 3);
1119        assert_eq!(recv.segments().as_slice()[0].as_ref(), b"abcd");
1120        assert_eq!(recv.segments().as_slice()[1].as_ref(), b"efgh");
1121        assert_eq!(recv.segments().as_slice()[2].as_ref(), b"ij");
1122        consumer.complete(reply).unwrap();
1123    }
1124
1125    #[test]
1126    fn test_chain_readable_rejects_zero_capacity_on_build() {
1127        let ring = make_ring(16);
1128        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1129
1130        assert!(matches!(
1131            producer.chain().readable(0).build(),
1132            Err(VirtqError::Alloc(AllocError::InvalidArg))
1133        ));
1134    }
1135
1136    #[test]
1137    fn test_chain_writable_splits_logical_capacity() {
1138        let ring = make_ring(16);
1139        let layout = ring.layout();
1140        let mem = ring.mem();
1141        let pool_base = mem.base_addr() + Layout::query_size(ring.len()) as u64 + 0x100;
1142        let pool = TestPool::new_with_max_alloc_len(pool_base, 0x8000, 4);
1143        let notifier = TestNotifier::new();
1144        let mut producer = VirtqProducer::new(layout, mem.clone(), notifier.clone(), pool);
1145        let mut consumer = VirtqConsumer::new(layout, mem, notifier);
1146
1147        let se = producer.chain().writable(10).build().unwrap();
1148        let token = producer.submit(se).unwrap();
1149
1150        let (_recv, reply) = poll_received(&mut consumer);
1151        let ReplyChain::Writable(mut wc) = reply else {
1152            panic!("expected writable reply");
1153        };
1154        assert_eq!(wc.capacity(), 10);
1155
1156        wc.write_all(b"abcdefghij").unwrap();
1157        consumer.complete(wc).unwrap();
1158
1159        let used = producer.poll().unwrap().unwrap();
1160        assert_eq!(used.token(), token);
1161        let segments = used.segments().unwrap();
1162        assert_eq!(segments.segment_count(), 3);
1163        assert_eq!(segments.as_slice()[0].as_ref(), b"abcd");
1164        assert_eq!(segments.as_slice()[1].as_ref(), b"efgh");
1165        assert_eq!(segments.as_slice()[2].as_ref(), b"ij");
1166    }
1167
1168    #[test]
1169    fn test_chain_writable_rejects_zero_capacity_on_build() {
1170        let ring = make_ring(16);
1171        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1172
1173        assert!(matches!(
1174            producer.chain().writable(0).build(),
1175            Err(VirtqError::Alloc(AllocError::InvalidArg))
1176        ));
1177    }
1178
1179    #[test]
1180    fn test_chain_multi_readable_write_all_preserves_segments() {
1181        let ring = make_ring(16);
1182        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1183
1184        let mut se = producer.chain().readable(4).readable(4).build().unwrap();
1185        se.write_all(b"headbody").unwrap();
1186
1187        producer.submit(se).unwrap();
1188        let (recv, reply) = poll_received(&mut consumer);
1189        assert_eq!(recv.to_bytes().as_ref(), b"headbody");
1190        consumer.complete(reply).unwrap();
1191    }
1192
1193    #[test]
1194    fn test_chain_payload_segment_writer_serializes_directly() {
1195        let ring = make_ring(16);
1196        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1197
1198        let mut se = producer.chain().readable(4).readable(4).build().unwrap();
1199        se.with_seg(0, |segment| {
1200            segment.copy_from_slice(b"head");
1201            Ok::<usize, VirtqError>(4)
1202        })
1203        .unwrap();
1204        se.with_seg(1, |segment| {
1205            segment.copy_from_slice(b"body");
1206            Ok::<usize, VirtqError>(4)
1207        })
1208        .unwrap();
1209
1210        producer.submit(se).unwrap();
1211        let (recv, reply) = poll_received(&mut consumer);
1212        assert_eq!(recv.to_bytes().as_ref(), b"headbody");
1213        assert_eq!(recv.segments().segment_count(), 2);
1214        consumer.complete(reply).unwrap();
1215    }
1216
1217    #[test]
1218    fn test_chain_payload_segment_write_copies_directly() {
1219        let ring = make_ring(16);
1220        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1221
1222        let mut se = producer.chain().readable(4).readable(4).build().unwrap();
1223        se.write_seg(0, b"head").unwrap();
1224        se.write_seg(1, b"body").unwrap();
1225
1226        producer.submit(se).unwrap();
1227        let (recv, reply) = poll_received(&mut consumer);
1228        assert_eq!(recv.to_bytes().as_ref(), b"headbody");
1229        assert_eq!(recv.segments().segment_count(), 2);
1230        consumer.complete(reply).unwrap();
1231    }
1232
1233    #[test]
1234    fn test_chain_multi_writable_used_returns_segments() {
1235        let ring = make_ring(16);
1236        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1237
1238        let se = producer.chain().writable(5).writable(6).build().unwrap();
1239        let token = producer.submit(se).unwrap();
1240
1241        let (_recv, reply) = poll_received(&mut consumer);
1242        let ReplyChain::Writable(mut wc) = reply else {
1243            panic!("expected writable reply");
1244        };
1245        assert_eq!(wc.capacity(), 11);
1246
1247        wc.write_all(b"hello world").unwrap();
1248        consumer.complete(wc).unwrap();
1249
1250        let used = producer.poll().unwrap().unwrap();
1251        assert_eq!(used.token(), token);
1252        let segments = used.segments().unwrap();
1253        assert_eq!(segments.segment_count(), 2);
1254        assert_eq!(segments.as_slice()[0].as_ref(), b"hello");
1255        assert_eq!(segments.as_slice()[1].as_ref(), b" world");
1256        assert_eq!(segments.to_bytes().as_ref(), b"hello world");
1257    }
1258
1259    #[test]
1260    fn test_chain_multi_writable_short_used_truncates_last_segment() {
1261        let ring = make_ring(16);
1262        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1263
1264        let se = producer.chain().writable(5).writable(6).build().unwrap();
1265        producer.submit(se).unwrap();
1266
1267        let (_recv, reply) = poll_received(&mut consumer);
1268        let ReplyChain::Writable(mut wc) = reply else {
1269            panic!("expected writable reply");
1270        };
1271
1272        wc.write_all(b"hello wo").unwrap();
1273        consumer.complete(wc).unwrap();
1274
1275        let used = producer.poll().unwrap().unwrap();
1276        let segments = used.segments().unwrap();
1277        assert_eq!(segments.segment_count(), 2);
1278        assert_eq!(segments.as_slice()[0].as_ref(), b"hello");
1279        assert_eq!(segments.as_slice()[1].as_ref(), b" wo");
1280        assert_eq!(segments.to_bytes().as_ref(), b"hello wo");
1281    }
1282
1283    #[test]
1284    fn test_chain_multi_writable_zero_used_returns_empty_segments() {
1285        let ring = make_ring(16);
1286        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1287
1288        let se = producer.chain().writable(5).writable(6).build().unwrap();
1289        producer.submit(se).unwrap();
1290
1291        let (_recv, reply) = poll_received(&mut consumer);
1292        consumer.complete(reply).unwrap();
1293
1294        let used = producer.poll().unwrap().unwrap();
1295        let segments = used.segments().unwrap();
1296        assert_eq!(segments.segment_count(), 0);
1297        assert!(segments.is_empty());
1298        assert!(segments.to_bytes().is_empty());
1299    }
1300
1301    #[test]
1302    fn test_chain_readable_only_build() {
1303        let ring = make_ring(16);
1304        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1305
1306        let se = producer.chain().readable(32).build().unwrap();
1307        assert_eq!(se.capacity(), 32);
1308    }
1309
1310    #[test]
1311    fn test_chain_writable_only_build() {
1312        let ring = make_ring(16);
1313        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1314
1315        let se = producer.chain().writable(64).build().unwrap();
1316        assert_eq!(se.capacity(), 0);
1317    }
1318
1319    #[test]
1320    fn test_chain_empty_build_fails() {
1321        let ring = make_ring(16);
1322        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1323
1324        let result = producer.chain().build();
1325        assert!(matches!(result, Err(VirtqError::InvalidState)));
1326    }
1327
1328    #[test]
1329    fn test_send_chain_write_all_and_submit() {
1330        let ring = make_ring(16);
1331        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1332
1333        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1334
1335        se.write_all(b"hello")
1336            .unwrap()
1337            .write_all(b" world")
1338            .unwrap();
1339        assert_eq!(se.written(), 11);
1340        assert_eq!(se.remaining(), 53);
1341        let tok = producer.submit(se).unwrap();
1342
1343        let (recv, reply) = poll_received(&mut consumer);
1344        assert_eq!(recv.token(), tok);
1345        assert_eq!(recv.to_bytes().as_ref(), b"hello world");
1346        consumer.complete(reply).unwrap();
1347    }
1348
1349    #[test]
1350    fn test_send_payload_write_all_fluent() {
1351        let ring = make_ring(16);
1352        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1353
1354        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1355        se.write_all(b"hello")
1356            .unwrap()
1357            .write_all(b" world")
1358            .unwrap();
1359        assert_eq!(se.written(), 11);
1360        assert_eq!(se.remaining(), 53);
1361        let tok = producer.submit(se).unwrap();
1362
1363        let (recv, reply) = poll_received(&mut consumer);
1364        assert_eq!(recv.token(), tok);
1365        assert_eq!(recv.to_bytes().as_ref(), b"hello world");
1366        consumer.complete(reply).unwrap();
1367    }
1368
1369    #[test]
1370    fn test_send_payload_partial_write() {
1371        let ring = make_ring(16);
1372        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1373
1374        let mut se = producer.chain().readable(8).build().unwrap();
1375        let written = se.write(b"hello world").unwrap();
1376        assert_eq!(written, 8);
1377        assert_eq!(se.remaining(), 0);
1378
1379        producer.submit(se).unwrap();
1380        let (recv, reply) = poll_received(&mut consumer);
1381        assert_eq!(recv.to_bytes().as_ref(), b"hello wo");
1382        consumer.complete(reply).unwrap();
1383    }
1384
1385    #[test]
1386    fn test_send_payload_write_with_serializes_directly() {
1387        let ring = make_ring(16);
1388        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1389
1390        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1391        se.with_seg(0, |buf| {
1392            buf[..5].copy_from_slice(b"hello");
1393            Ok::<usize, VirtqError>(5)
1394        })
1395        .unwrap();
1396
1397        let _tok = producer.submit(se).unwrap();
1398
1399        let (recv, reply) = poll_received(&mut consumer);
1400        assert_eq!(recv.to_bytes().as_ref(), b"hello");
1401        consumer.complete(reply).unwrap();
1402    }
1403
1404    #[test]
1405    fn test_send_chain_single_segment_writer_serializes_directly() {
1406        let ring = make_ring(16);
1407        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1408
1409        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1410        se.with_seg(0, |segment| {
1411            assert_eq!(segment.len(), 64);
1412            segment[..5].copy_from_slice(b"hello");
1413            Ok::<usize, VirtqError>(5)
1414        })
1415        .unwrap();
1416
1417        let _tok = producer.submit(se).unwrap();
1418
1419        let (recv, reply) = poll_received(&mut consumer);
1420        assert_eq!(recv.to_bytes().as_ref(), b"hello");
1421        consumer.complete(reply).unwrap();
1422    }
1423
1424    #[test]
1425    fn test_send_chain_single_segment_writer_rejects_multi_segment() {
1426        let ring = make_ring(16);
1427        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1428
1429        let mut se = producer.chain().readable(4).readable(4).build().unwrap();
1430        assert!(matches!(
1431            se.with_seg(2, |_| Ok::<usize, VirtqError>(0)),
1432            Err(VirtqError::NoPayloadSegment)
1433        ));
1434    }
1435
1436    #[test]
1437    fn test_send_chain_single_segment_writer_rejects_auto_split_chain() {
1438        let ring = make_ring(16);
1439        let layout = ring.layout();
1440        let mem = ring.mem();
1441        let pool_base = mem.base_addr() + Layout::query_size(ring.len()) as u64 + 0x100;
1442        let pool = TestPool::new_with_max_alloc_len(pool_base, 0x8000, 4);
1443        let notifier = TestNotifier::new();
1444        let producer = VirtqProducer::new(layout, mem, notifier, pool);
1445
1446        let mut se = producer.chain().readable(8).build().unwrap();
1447        assert_eq!(se.segment_count(), 2);
1448        assert!(matches!(
1449            se.with_seg(2, |_| Ok::<usize, VirtqError>(0)),
1450            Err(VirtqError::NoPayloadSegment)
1451        ));
1452    }
1453
1454    #[test]
1455    fn test_send_payload_segment_set_written_too_large() {
1456        let ring = make_ring(16);
1457        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1458
1459        let mut se = producer.chain().readable(32).writable(64).build().unwrap();
1460        let err = se
1461            .with_seg(0, |_| Ok::<usize, VirtqError>(64))
1462            .err()
1463            .unwrap();
1464        assert!(matches!(
1465            err,
1466            VirtqError::PayloadTooLarge {
1467                recv: 64,
1468                limit: 32
1469            }
1470        ));
1471    }
1472
1473    #[test]
1474    fn test_send_chain_write_too_large() {
1475        let ring = make_ring(16);
1476        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1477
1478        let mut se = producer.chain().readable(4).build().unwrap();
1479        let err = se.write_all(b"too long").err().unwrap();
1480        assert!(matches!(
1481            err,
1482            VirtqError::PayloadTooLarge { recv: 8, limit: 4 }
1483        ));
1484    }
1485
1486    #[test]
1487    fn test_writeonly_has_no_readable_buffer() {
1488        let ring = make_ring(16);
1489        let (producer, _consumer, _notifier) = make_test_producer(&ring);
1490
1491        let mut se = producer.chain().writable(32).build().unwrap();
1492        let err = se.write_all(b"data").err().unwrap();
1493        assert!(matches!(err, VirtqError::NoPayloadSegment));
1494    }
1495
1496    #[test]
1497    fn test_drop_chain_builder_deallocs() {
1498        let ring = make_ring(16);
1499        let (mut producer, _consumer, _notifier) = make_test_producer(&ring);
1500
1501        {
1502            let _builder = producer.chain().readable(64).writable(128);
1503            // dropped without build
1504        }
1505
1506        // Ring should still be fully usable
1507        let se = producer.chain().readable(64).writable(128).build().unwrap();
1508        let tok = producer.submit(se).unwrap();
1509        assert!(tok.id < 16);
1510    }
1511
1512    #[test]
1513    fn test_drop_send_chain_deallocs() {
1514        let ring = make_ring(16);
1515        let (mut producer, _consumer, _notifier) = make_test_producer(&ring);
1516
1517        {
1518            let _se = producer.chain().readable(64).writable(128).build().unwrap();
1519            // dropped without submit
1520        }
1521
1522        // Ring should still be fully usable
1523        let se = producer.chain().readable(64).writable(128).build().unwrap();
1524        let tok = producer.submit(se).unwrap();
1525        assert!(tok.id < 16);
1526    }
1527
1528    #[test]
1529    fn test_submit_notifies() {
1530        let ring = make_ring(16);
1531        let (mut producer, _consumer, notifier) = make_test_producer(&ring);
1532
1533        let initial_count = notifier.notification_count();
1534
1535        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1536        se.write_all(b"hello").unwrap();
1537        producer.submit(se).unwrap();
1538
1539        assert!(notifier.notification_count() > initial_count);
1540    }
1541
1542    #[test]
1543    fn test_submit_read_only_notifies_by_default() {
1544        let ring = make_ring(16);
1545        let (mut producer, _consumer, notifier) = make_test_producer(&ring);
1546
1547        let initial_count = notifier.notification_count();
1548
1549        let mut se = producer.chain().readable(64).build().unwrap();
1550        se.write_all(b"fire-and-forget").unwrap();
1551        producer.submit(se).unwrap();
1552
1553        assert!(notifier.notification_count() > initial_count);
1554    }
1555
1556    #[test]
1557    fn test_submit_write_only_notifies_by_default() {
1558        let ring = make_ring(16);
1559        let (mut producer, _consumer, notifier) = make_test_producer(&ring);
1560
1561        let initial_count = notifier.notification_count();
1562
1563        let se = producer.chain().writable(128).build().unwrap();
1564        producer.submit(se).unwrap();
1565
1566        assert!(notifier.notification_count() > initial_count);
1567    }
1568
1569    #[test]
1570    fn test_batch_notifies_once_on_finish() {
1571        let ring = make_ring(16);
1572        let (mut producer, mut consumer, notifier) = make_test_producer(&ring);
1573
1574        let initial_count = notifier.notification_count();
1575
1576        let mut batch = producer.batch();
1577
1578        let mut first = batch.chain().readable(64).build().unwrap();
1579        first.write_all(b"first").unwrap();
1580        batch.submit(first).unwrap();
1581
1582        let mut second = batch.chain().readable(64).build().unwrap();
1583        second.write_all(b"second").unwrap();
1584        batch.submit(second).unwrap();
1585
1586        assert_eq!(notifier.notification_count(), initial_count);
1587        assert!(batch.finish().unwrap());
1588        assert_eq!(notifier.notification_count(), initial_count + 1);
1589
1590        let (recv, reply) = poll_received(&mut consumer);
1591        assert_eq!(recv.to_bytes().as_ref(), b"first");
1592        consumer.complete(reply).unwrap();
1593
1594        let (recv, reply) = poll_received(&mut consumer);
1595        assert_eq!(recv.to_bytes().as_ref(), b"second");
1596        consumer.complete(reply).unwrap();
1597    }
1598
1599    #[test]
1600    fn test_batch_finish_notifies_from_batch_start_cursor() {
1601        let ring = make_ring(16);
1602        let (mut producer, mut consumer, notifier) = make_test_producer(&ring);
1603
1604        let cursor = consumer.avail_cursor();
1605        consumer
1606            .set_avail_suppression(SuppressionKind::Descriptor(cursor))
1607            .unwrap();
1608
1609        let mut batch = producer.batch();
1610
1611        let mut first = batch.chain().readable(64).build().unwrap();
1612        first.write_all(b"first").unwrap();
1613        batch.submit(first).unwrap();
1614        assert_eq!(notifier.notification_count(), 0);
1615
1616        let mut second = batch.chain().readable(64).writable(64).build().unwrap();
1617        second.write_all(b"second").unwrap();
1618        batch.submit(second).unwrap();
1619
1620        assert!(batch.finish().unwrap());
1621
1622        assert_eq!(notifier.notification_count(), 1);
1623    }
1624
1625    #[test]
1626    fn test_empty_batch_finish_does_not_notify() {
1627        let ring = make_ring(16);
1628        let (mut producer, _consumer, notifier) = make_test_producer(&ring);
1629
1630        let batch = producer.batch();
1631        assert!(!batch.finish().unwrap());
1632        assert_eq!(notifier.notification_count(), 0);
1633    }
1634
1635    #[test]
1636    fn test_write_only_round_trip() {
1637        let ring = make_ring(16);
1638        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1639
1640        let se = producer.chain().writable(32).build().unwrap();
1641        let token = producer.submit(se).unwrap();
1642
1643        let (recv, reply) = poll_received(&mut consumer);
1644        assert_eq!(recv.token(), token);
1645        assert!(recv.to_bytes().is_empty());
1646
1647        if let ReplyChain::Writable(mut wc) = reply {
1648            wc.write_all(b"filled-by-consumer").unwrap();
1649            consumer.complete(wc).unwrap();
1650        } else {
1651            panic!("expected Writable");
1652        }
1653
1654        let used = producer.poll().unwrap().unwrap();
1655        assert_eq!(used.token(), token);
1656        assert_eq!(used.to_bytes().unwrap().len(), b"filled-by-consumer".len());
1657        assert_eq!(used.to_bytes().unwrap().as_ref(), b"filled-by-consumer");
1658    }
1659
1660    #[test]
1661    fn test_read_only_round_trip() {
1662        let ring = make_ring(16);
1663        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1664
1665        let mut se = producer.chain().readable(32).build().unwrap();
1666        se.write_all(b"fire-and-forget").unwrap();
1667        let token = producer.submit(se).unwrap();
1668
1669        let (recv, reply) = poll_received(&mut consumer);
1670        assert_eq!(recv.token(), token);
1671        assert_eq!(recv.to_bytes().as_ref(), b"fire-and-forget");
1672        assert!(matches!(reply, ReplyChain::Ack(_)));
1673        consumer.complete(reply).unwrap();
1674
1675        let used = producer.poll().unwrap().unwrap();
1676        assert!(matches!(used, UsedChain::Ack(t) if t == token));
1677    }
1678
1679    #[test]
1680    fn test_readwrite_round_trip() {
1681        let ring = make_ring(16);
1682        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1683
1684        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1685        se.write_all(b"request data").unwrap();
1686        let token = producer.submit(se).unwrap();
1687
1688        let (recv, reply) = poll_received(&mut consumer);
1689        assert_eq!(recv.to_bytes().as_ref(), b"request data");
1690        if let ReplyChain::Writable(mut wc) = reply {
1691            wc.write_all(b"response data").unwrap();
1692            consumer.complete(wc).unwrap();
1693        } else {
1694            panic!("expected Writable");
1695        }
1696
1697        let used = producer.poll().unwrap().unwrap();
1698        assert_eq!(used.token(), token);
1699        assert_eq!(used.to_bytes().unwrap().as_ref(), b"response data");
1700    }
1701
1702    #[test]
1703    fn test_poll_used_requires_direct_slice() {
1704        let ring = make_ring(16);
1705        let layout = ring.layout();
1706        let test_mem = ring.mem();
1707        let pool_base = test_mem.base_addr() + Layout::query_size(ring.len()) as u64 + 0x100;
1708        let pool = TestPool::new(pool_base, 0x8000);
1709        let notifier = TestNotifier::new();
1710        let mem = NoDirectSliceMem(test_mem);
1711        let mut producer = VirtqProducer::new(layout, mem.clone(), notifier.clone(), pool);
1712        let mut consumer = VirtqConsumer::new(layout, mem, notifier);
1713
1714        let mut se = producer.chain().readable(64).writable(128).build().unwrap();
1715        se.write_all(b"request data").unwrap();
1716        producer.submit(se).unwrap();
1717
1718        let (_recv, reply) = poll_received(&mut consumer);
1719        if let ReplyChain::Writable(mut wc) = reply {
1720            wc.write_all(b"response data").unwrap();
1721            consumer.complete(wc).unwrap();
1722        } else {
1723            panic!("expected Writable");
1724        }
1725
1726        assert!(matches!(producer.poll(), Err(VirtqError::MemoryReadError)));
1727    }
1728
1729    #[test]
1730    fn test_virtq_producer_reset() {
1731        let ring = make_ring(16);
1732        let (mut producer, mut consumer, _notifier) = make_test_producer(&ring);
1733
1734        // Submit and complete a round trip
1735        let mut se = producer.chain().readable(32).writable(64).build().unwrap();
1736        se.write_all(b"hello").unwrap();
1737        producer.submit(se).unwrap();
1738
1739        let (recv, reply) = poll_received(&mut consumer);
1740        assert_eq!(recv.to_bytes().as_ref(), b"hello");
1741        consumer.complete(reply).unwrap();
1742        let _ = producer.poll().unwrap().unwrap();
1743
1744        // Now reset
1745        // SAFETY: the used chain was dropped before reset and no peer can
1746        // access the reset test ring concurrently.
1747        unsafe {
1748            producer.reset();
1749        }
1750
1751        // All inflight slots should be cleared
1752        assert_eq!(producer.inner.num_inflight(), 0);
1753        // Ring state should be back to initial
1754        assert_eq!(producer.inner.num_free(), producer.inner.len());
1755    }
1756
1757    #[test]
1758    fn test_virtq_producer_reset_clears_inflight() {
1759        let ring = make_ring(16);
1760        let (mut producer, _consumer, _notifier) = make_test_producer(&ring);
1761
1762        // Submit without completing
1763        let se = producer.chain().writable(64).build().unwrap();
1764        producer.submit(se).unwrap();
1765
1766        assert_eq!(producer.inner.num_inflight(), 1);
1767
1768        // SAFETY: no peer can access the reset test ring concurrently.
1769        unsafe {
1770            producer.reset();
1771        }
1772
1773        assert_eq!(producer.inner.num_inflight(), 0);
1774        assert_eq!(producer.inner.num_free(), producer.inner.len());
1775    }
1776}