1mod packed;
8mod split;
9use crate::VirtioQueueCallbackWork;
10use crate::spec::VirtioDeviceFeatures;
11use crate::spec::queue as spec;
12use guestmem::GuestMemory;
13use guestmem::GuestMemoryError;
14use inspect::Inspect;
15use packed::PackedQueueCompleteWork;
16pub use packed::PackedQueueCompletionContext;
17use packed::PackedQueueGetWork;
18use spec::DescriptorFlags;
19use spec::PackedDescriptor;
20use spec::SplitDescriptor;
21use split::SplitQueueCompleteWork;
22use split::SplitQueueGetWork;
23use thiserror::Error;
24use zerocopy::FromBytes;
25use zerocopy::Immutable;
26use zerocopy::IntoBytes;
27use zerocopy::KnownLayout;
28
29pub(crate) fn descriptor_offset(index: u16) -> u64 {
30 index as u64 * size_of::<SplitDescriptor>() as u64
31}
32
33pub(crate) fn read_descriptor<T: IntoBytes + FromBytes + Immutable + KnownLayout>(
34 queue_desc: &GuestMemory,
35 index: u16,
36) -> Result<T, QueueError> {
37 queue_desc
38 .read_plain::<T>(descriptor_offset(index))
39 .map_err(QueueError::Memory)
40}
41
42fn split_in_flight(avail: u16, used: u16, queue_size: u16) -> Result<u16, QueueError> {
48 let in_flight = avail.wrapping_sub(used);
49 if in_flight > queue_size {
50 return Err(QueueError::InvalidSavedState {
51 avail_index: avail,
52 used_index: used,
53 queue_size,
54 });
55 }
56 Ok(in_flight)
57}
58
59fn packed_in_flight(avail: u16, used: u16, queue_size: u16) -> Result<u16, QueueError> {
68 if avail & 0x7fff >= queue_size || used & 0x7fff >= queue_size {
69 return Err(QueueError::InvalidSavedState {
70 avail_index: avail,
71 used_index: used,
72 queue_size,
73 });
74 }
75 let ring = queue_size as i32;
76 let position = |cursor: u16| (cursor & 0x7FFF) as i32 + i32::from(cursor & 0x8000 != 0) * ring;
77 let in_flight = (position(avail) - position(used)).rem_euclid(2 * ring);
78 if in_flight > ring {
79 return Err(QueueError::InvalidSavedState {
80 avail_index: avail,
81 used_index: used,
82 queue_size,
83 });
84 }
85 Ok(in_flight as u16)
86}
87
88#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, mesh::payload::Protobuf)]
94#[mesh(package = "virtio.queue")]
95pub struct QueueState {
96 #[mesh(1)]
97 pub avail_index: u16,
98 #[mesh(2)]
99 pub used_index: u16,
100}
101
102#[derive(Debug, Error)]
103pub enum QueueError {
104 #[error("error accessing queue memory")]
105 Memory(#[source] GuestMemoryError),
106 #[error("an indirect descriptor had the indirect flag set")]
107 DoubleIndirect,
108 #[error("a descriptor chain is too long or has a cycle")]
109 TooLong,
110 #[error("Invalid queue size {0}. Must be a power of 2.")]
111 InvalidQueueSize(u16),
112 #[error(
113 "guest oversubscribed queue size {queue_size}: {in_flight} already in flight, \
114 {requested} more requested"
115 )]
116 TooManyInFlightDescriptors {
117 in_flight: u16,
118 requested: u16,
119 queue_size: u16,
120 },
121 #[error(
122 "corrupt queue saved state: avail index {avail_index:#x}, used index {used_index:#x}, \
123 queue size {queue_size}"
124 )]
125 InvalidSavedState {
126 avail_index: u16,
127 used_index: u16,
128 queue_size: u16,
129 },
130}
131
132struct QueueDescriptor {
133 address: u64,
134 length: u32,
135 flags: DescriptorFlags,
136 buffer_id: Option<u16>,
137 next: Option<u16>,
138}
139
140enum QueueCompletionContext {
141 Split,
142 Packed(PackedQueueCompletionContext),
143}
144
145pub struct QueueCompletion {
153 context: QueueCompletionContext,
154 descriptor_index: u16,
155}
156
157impl QueueCompletion {
158 pub fn descriptor_index(&self) -> u16 {
159 self.descriptor_index
160 }
161
162 fn in_flight_cost(&self) -> u16 {
163 match &self.context {
164 QueueCompletionContext::Split => 1,
165 QueueCompletionContext::Packed(context) => context.descriptor_count(),
166 }
167 }
168}
169
170#[derive(Debug, Inspect)]
171#[inspect(tag = "type")]
172enum QueueGetWorkInner {
173 Split(#[inspect(flatten)] SplitQueueGetWork),
174 Packed(#[inspect(flatten)] PackedQueueGetWork),
175}
176
177#[derive(Debug, Inspect)]
178#[inspect(tag = "type")]
179enum QueueCompleteWorkInner {
180 Split(#[inspect(flatten)] SplitQueueCompleteWork),
181 Packed(#[inspect(flatten)] PackedQueueCompleteWork),
182}
183
184#[derive(Debug, Copy, Clone, Default, inspect::Inspect)]
185pub struct QueueParams {
186 pub size: u16,
187 pub enable: bool,
188 #[inspect(hex)]
189 pub desc_addr: u64,
190 #[inspect(hex)]
191 pub avail_addr: u64,
192 #[inspect(hex)]
193 pub used_addr: u64,
194}
195
196#[derive(Debug, Inspect)]
197pub(crate) struct QueueCoreGetWork {
198 queue_desc: GuestMemory,
199 queue_size: u16,
200 features: VirtioDeviceFeatures,
201 mem: GuestMemory,
202 #[inspect(flatten)]
203 inner: QueueGetWorkInner,
204 armed: bool,
206 failed: bool,
219 in_flight: u16,
225}
226
227impl QueueCoreGetWork {
228 pub fn avail_index(&self) -> u16 {
229 match &self.inner {
230 QueueGetWorkInner::Split(split) => split.last_avail_index(),
231 QueueGetWorkInner::Packed(packed) => packed.avail_state(),
232 }
233 }
234
235 pub fn new(
236 features: VirtioDeviceFeatures,
237 mem: GuestMemory,
238 params: QueueParams,
239 initial_state: Option<QueueState>,
240 ) -> Result<Self, QueueError> {
241 if params.size == 0 || params.size > 1 << 15 {
243 return Err(QueueError::InvalidQueueSize(params.size));
244 }
245 let initial_avail = initial_state.map(|s| s.avail_index);
246 if !features.ring_packed() && !params.size.is_power_of_two() {
249 return Err(QueueError::InvalidQueueSize(params.size));
250 }
251 let queue_desc = mem
252 .subrange(params.desc_addr, descriptor_offset(params.size), true)
253 .map_err(QueueError::Memory)?;
254 let inner = if features.ring_packed() {
255 let (index, wrap) = match initial_avail {
256 Some(v) => (v & 0x7FFF, (v >> 15) != 0),
257 None => (0, true),
258 };
259 QueueGetWorkInner::Packed(PackedQueueGetWork::new(
260 features,
261 mem.clone(),
262 params,
263 index,
264 wrap,
265 )?)
266 } else {
267 let index = initial_avail.unwrap_or(0);
268 QueueGetWorkInner::Split(SplitQueueGetWork::new(
269 features,
270 mem.clone(),
271 params,
272 index,
273 )?)
274 };
275 let in_flight = match initial_state {
280 None => 0,
281 Some(state) if features.ring_packed() => {
282 packed_in_flight(state.avail_index, state.used_index, params.size)?
283 }
284 Some(state) => split_in_flight(state.avail_index, state.used_index, params.size)?,
285 };
286 Ok(Self {
287 queue_desc,
288 queue_size: params.size,
289 features,
290 mem,
291 inner,
292 armed: false,
293 failed: false,
294 in_flight,
295 })
296 }
297
298 pub fn failed(&self) -> bool {
301 self.failed
302 }
303
304 pub fn try_next_work(&mut self) -> Result<Option<VirtioQueueCallbackWork>, QueueError> {
305 match self.try_peek_work() {
306 Ok(Some(work)) => {
307 self.advance(work.completion());
308 Ok(Some(work))
309 }
310 r => r,
311 }
312 }
313
314 pub fn try_peek_work(&mut self) -> Result<Option<VirtioQueueCallbackWork>, QueueError> {
323 if self.failed {
324 return Ok(None);
325 }
326 let r = self.try_peek_work_inner();
327 if r.is_err() {
328 self.failed = true;
329 }
330 r
331 }
332
333 fn try_peek_work_inner(&mut self) -> Result<Option<VirtioQueueCallbackWork>, QueueError> {
334 let index = match &mut self.inner {
335 QueueGetWorkInner::Split(split) => split.is_available()?,
336 QueueGetWorkInner::Packed(packed) => packed.is_available()?,
337 };
338 let Some(index) = index else { return Ok(None) };
339 self.suppress_if_armed();
340 let work = self.work_from_index(index)?;
341
342 let requested = work.completion().in_flight_cost();
349 if requested > self.queue_size - self.in_flight {
350 return Err(QueueError::TooManyInFlightDescriptors {
351 in_flight: self.in_flight,
352 requested,
353 queue_size: self.queue_size,
354 });
355 }
356
357 Ok(Some(work))
358 }
359
360 pub fn arm_for_kick(&mut self) -> bool {
367 if self.armed {
368 return true;
369 }
370 let r = match &mut self.inner {
371 QueueGetWorkInner::Split(split) => split.arm_kick(),
372 QueueGetWorkInner::Packed(packed) => packed.arm_kick(),
373 };
374 match r {
375 Ok(true) => {
376 self.armed = true;
377 true
378 }
379 Ok(false) => false,
380 Err(err) => {
381 tracelimit::error_ratelimited!(
382 error = &err as &dyn std::error::Error,
383 "failed to arm kick"
384 );
385 self.armed = true;
388 true
389 }
390 }
391 }
392
393 fn suppress_if_armed(&mut self) {
396 if self.armed {
397 self.armed = false;
398 let r = match &self.inner {
399 QueueGetWorkInner::Split(split) => split.suppress_kicks(),
400 QueueGetWorkInner::Packed(packed) => packed.suppress_kicks(),
401 };
402
403 if let Err(err) = r {
404 tracelimit::error_ratelimited!(
405 error = &err as &dyn std::error::Error,
406 "failed to suppress kicks"
407 );
408 }
409 }
410 }
411
412 pub fn advance(&mut self, completion: &QueueCompletion) {
415 let cost = completion.in_flight_cost();
416 match &mut self.inner {
417 QueueGetWorkInner::Split(split) => split.advance(),
418 QueueGetWorkInner::Packed(packed) => {
419 let QueueCompletionContext::Packed(ctx) = &completion.context else {
420 unreachable!();
421 };
422 packed.advance(ctx.descriptor_count());
423 }
424 }
425 self.in_flight = self
428 .in_flight
429 .checked_add(cost)
430 .expect("in-flight count overflowed");
431 }
432
433 pub fn work_completed(&mut self, completion: &QueueCompletion) {
435 self.in_flight = self
436 .in_flight
437 .checked_sub(completion.in_flight_cost())
438 .expect("completed more than was consumed");
439 }
440
441 fn work_from_index(&mut self, index: u16) -> Result<VirtioQueueCallbackWork, QueueError> {
442 if let QueueGetWorkInner::Split(split) = &mut self.inner {
443 let descriptor_index = split.get_available_descriptor_index(index)?;
444 let payload = self
445 .reader(descriptor_index)
446 .collect::<Result<Vec<_>, _>>()?;
447 Ok(VirtioQueueCallbackWork::from_parts(
448 QueueCompletion {
449 descriptor_index,
450 context: QueueCompletionContext::Split,
451 },
452 payload,
453 ))
454 } else {
455 let (payload, last_primary_desc_index) = {
456 let mut reader = self.reader(index);
457 (
458 (&mut reader).collect::<Result<Vec<_>, _>>()?,
459 reader.last_primary_desc_index(),
460 )
461 };
462 let last = self.descriptor(&self.queue_desc, last_primary_desc_index, None)?;
463 let count = if last_primary_desc_index >= index {
464 last_primary_desc_index - index + 1
465 } else {
466 self.queue_size - index + last_primary_desc_index + 1
468 };
469 let completion_context = PackedQueueCompletionContext::new(&last, count);
470 Ok(VirtioQueueCallbackWork::from_parts(
471 QueueCompletion {
472 context: QueueCompletionContext::Packed(completion_context),
473 descriptor_index: index,
474 },
475 payload,
476 ))
477 }
478 }
479
480 fn reader(&mut self, descriptor_index: u16) -> DescriptorReader<'_> {
481 DescriptorReader {
482 chain: DescriptorChain::new(self, self.features.ring_indirect_desc(), descriptor_index),
483 }
484 }
485
486 fn descriptor(
487 &self,
488 desc_queue: &GuestMemory,
489 index: u16,
490 active_indirect_len: Option<u16>,
491 ) -> Result<QueueDescriptor, QueueError> {
492 let descriptor = match self.inner {
493 QueueGetWorkInner::Split(_) => {
494 let descriptor: SplitDescriptor = read_descriptor(desc_queue, index)?;
495 QueueDescriptor {
496 address: descriptor.address.get(),
497 length: descriptor.length.get(),
498 flags: descriptor.flags(),
499 buffer_id: None,
500 next: if descriptor.flags().next() {
501 Some(descriptor.next.get())
502 } else {
503 None
504 },
505 }
506 }
507 QueueGetWorkInner::Packed(_) => {
508 let descriptor: PackedDescriptor = read_descriptor(desc_queue, index)?;
509 QueueDescriptor {
510 address: descriptor.address.get(),
511 length: descriptor.length.get(),
512 flags: descriptor.flags(),
513 buffer_id: Some(descriptor.buffer_id.get()),
514 next: if let Some(active_indirect_len) = active_indirect_len {
515 let next = index.wrapping_add(1);
519 if next < active_indirect_len {
520 Some(next)
521 } else {
522 None
523 }
524 } else if descriptor.flags().next() {
525 let next = index.wrapping_add(1);
528 if next >= self.queue_size {
529 Some(0)
530 } else {
531 Some(next)
532 }
533 } else {
534 None
535 },
536 }
537 }
538 };
539 Ok(descriptor)
540 }
541
542 fn size(&self) -> u16 {
543 self.queue_size
544 }
545}
546
547#[derive(Debug, Inspect)]
548pub(crate) struct QueueCoreCompleteWork {
549 #[inspect(flatten)]
550 inner: QueueCompleteWorkInner,
551}
552
553impl QueueCoreCompleteWork {
554 pub fn new(
555 features: VirtioDeviceFeatures,
556 mem: GuestMemory,
557 params: QueueParams,
558 initial_state: Option<QueueState>,
559 ) -> Result<Self, QueueError> {
560 let initial_used = initial_state.map(|s| s.used_index);
561 let inner = if features.ring_packed() {
562 let (index, wrap) = match initial_used {
563 Some(v) => (v & 0x7FFF, (v >> 15) != 0),
564 None => (0, true),
565 };
566 QueueCompleteWorkInner::Packed(PackedQueueCompleteWork::new(
567 features,
568 mem.clone(),
569 params,
570 index,
571 wrap,
572 )?)
573 } else {
574 let index = initial_used.unwrap_or(0);
575 QueueCompleteWorkInner::Split(SplitQueueCompleteWork::new(
576 features,
577 mem.clone(),
578 params,
579 index,
580 )?)
581 };
582 Ok(Self { inner })
583 }
584
585 pub fn used_index(&self) -> u16 {
586 match &self.inner {
587 QueueCompleteWorkInner::Split(split) => split.last_used_index(),
588 QueueCompleteWorkInner::Packed(packed) => packed.used_state(),
589 }
590 }
591
592 pub fn complete_descriptor(
593 &mut self,
594 completion: &QueueCompletion,
595 bytes_written: u32,
596 ) -> Result<bool, QueueError> {
597 match &mut self.inner {
598 QueueCompleteWorkInner::Split(split) => {
599 split.complete_descriptor(completion.descriptor_index, bytes_written)
600 }
601 QueueCompleteWorkInner::Packed(packed) => {
602 let QueueCompletionContext::Packed(context) = &completion.context else {
603 panic!("mismatched queue completion context for packed queue");
604 };
605 packed.complete_descriptor(context, bytes_written)
606 }
607 }
608 }
609}
610
611pub(crate) fn new_queue(
612 features: VirtioDeviceFeatures,
613 mem: GuestMemory,
614 params: QueueParams,
615 initial_state: Option<QueueState>,
616) -> Result<(QueueCoreGetWork, QueueCoreCompleteWork), QueueError> {
617 let get_work = QueueCoreGetWork::new(features, mem.clone(), params, initial_state)?;
618 let complete_work = QueueCoreCompleteWork::new(features, mem.clone(), params, initial_state)?;
619 Ok((get_work, complete_work))
620}
621
622struct DescriptorReader<'a> {
623 chain: DescriptorChain<'a>,
624}
625
626impl DescriptorReader<'_> {
627 pub fn last_primary_desc_index(&self) -> u16 {
628 self.chain.last_primary_desc_index()
629 }
630}
631
632pub struct VirtioQueuePayload {
633 pub writeable: bool,
634 pub address: u64,
635 pub length: u32,
636}
637
638impl Iterator for DescriptorReader<'_> {
639 type Item = Result<VirtioQueuePayload, QueueError>;
640
641 fn next(&mut self) -> Option<Self::Item> {
642 self.chain.next().map(|descriptor| {
643 descriptor.map(|descriptor| VirtioQueuePayload {
644 writeable: descriptor.flags.write(),
645 address: descriptor.address,
646 length: descriptor.length,
647 })
648 })
649 }
650}
651
652struct DescriptorChain<'a> {
653 queue: &'a QueueCoreGetWork,
654 queue_size: u16,
656 indirect_support: bool,
657 indirect_queue: Option<GuestMemory>,
658 indirect_table_len: Option<u16>,
660 descriptor_index: Option<u16>,
661 last_primary_desc_index: u16,
662 num_read: u16,
663}
664
665impl<'a> DescriptorChain<'a> {
666 fn new(queue: &'a QueueCoreGetWork, indirect_support: bool, descriptor_index: u16) -> Self {
667 Self {
668 queue,
669 queue_size: queue.size(),
670 indirect_support,
671 indirect_queue: None,
672 indirect_table_len: None,
673 descriptor_index: Some(descriptor_index),
674 last_primary_desc_index: descriptor_index,
675 num_read: 0,
676 }
677 }
678
679 fn next_descriptor(&mut self) -> Result<Option<QueueDescriptor>, QueueError> {
680 let Some(descriptor_index) = self.descriptor_index else {
681 return Ok(None);
682 };
683 let descriptor = self.queue.descriptor(
684 self.indirect_queue
685 .as_ref()
686 .unwrap_or(&self.queue.queue_desc),
687 descriptor_index,
688 self.indirect_table_len,
689 )?;
690 let descriptor = if !self.indirect_support || !descriptor.flags.indirect() {
691 if self.indirect_queue.is_none() {
692 self.last_primary_desc_index = descriptor_index;
693 }
694 descriptor
695 } else {
696 if self.indirect_queue.is_some() {
697 return Err(QueueError::DoubleIndirect);
698 }
699 let indirect_queue = self.indirect_queue.insert(
700 self.queue
701 .mem
702 .subrange(descriptor.address, descriptor.length as u64, true)
703 .map_err(QueueError::Memory)?,
704 );
705 self.descriptor_index = Some(0);
706 let indirect_len = (descriptor.length / size_of::<SplitDescriptor>() as u32) as u16;
707 self.indirect_table_len = Some(indirect_len);
708 self.queue
709 .descriptor(indirect_queue, 0, Some(indirect_len))?
710 };
711
712 self.num_read += 1;
713 self.descriptor_index = descriptor.next;
714 if self.descriptor_index.is_some() && self.num_read == self.queue_size {
718 return Err(QueueError::TooLong);
719 }
720 Ok(Some(descriptor))
721 }
722
723 pub fn last_primary_desc_index(&self) -> u16 {
724 self.last_primary_desc_index
725 }
726}
727
728impl Iterator for DescriptorChain<'_> {
729 type Item = Result<QueueDescriptor, QueueError>;
730
731 fn next(&mut self) -> Option<Self::Item> {
732 self.next_descriptor().transpose()
733 }
734}
735
736#[cfg(test)]
737mod tests {
738 use super::QueueError;
739 use super::packed_in_flight;
740
741 fn cursor(index: u16, wrap: bool) -> u16 {
744 index | (u16::from(wrap) << 15)
745 }
746
747 #[test]
748 fn packed_in_flight_decode() {
749 let qs = 8;
750
751 assert_eq!(
753 packed_in_flight(cursor(2, false), cursor(2, false), qs).unwrap(),
754 0
755 );
756 assert_eq!(
757 packed_in_flight(cursor(5, true), cursor(5, true), qs).unwrap(),
758 0
759 );
760
761 assert_eq!(
763 packed_in_flight(cursor(5, false), cursor(2, false), qs).unwrap(),
764 3
765 );
766
767 assert_eq!(
769 packed_in_flight(cursor(3, true), cursor(3, false), qs).unwrap(),
770 8
771 );
772 assert_eq!(
773 packed_in_flight(cursor(0, false), cursor(0, true), qs).unwrap(),
774 8
775 );
776
777 assert_eq!(
780 packed_in_flight(cursor(1, true), cursor(6, false), qs).unwrap(),
781 3
782 );
783 }
784
785 #[test]
786 fn packed_in_flight_rejects_corrupt_state() {
787 assert!(matches!(
792 packed_in_flight(cursor(1, false), cursor(6, false), 8),
793 Err(QueueError::InvalidSavedState { queue_size: 8, .. })
794 ));
795 }
796}