Alioth Code Coverage

queue.rs100.00%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod packed;
16pub mod split;
17
18use std::collections::HashMap;
19use std::fmt::Debug;
20use std::io::{ErrorKind, IoSlice, IoSliceMut, Read, Write};
21use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU64, Ordering, fence};
22
23use crate::bitflags;
24use crate::mem::mapped::Ram;
25use crate::virtio::{IrqSender, Result, error};
26
27pub const QUEUE_SIZE_MAX: u16 = 256;
28
29bitflags! {
30 pub struct DescFlag(u16) {
31 NEXT = 1 << 0;
32 WRITE = 1 << 1;
33 INDIRECT = 1 << 2;
34 AVAIL = 1 << 7;
35 USED = 1 << 15;
36 }
37}
38
39#[derive(Debug, Default)]
40pub struct QueueReg {
41 pub size: AtomicU16,
42 pub desc: AtomicU64,
43 pub driver: AtomicU64,
44 pub device: AtomicU64,
45 pub enabled: AtomicBool,
46}
47
48#[derive(Debug)]
49pub struct DescChain<'m> {
50 id: u16,
51 delta: u16,
52 pub readable: Vec<IoSlice<'m>>,
53 pub writable: Vec<IoSliceMut<'m>>,
54}
55
56impl DescChain<'_> {
57 pub fn id(&self) -> u16 {6x
58 self.id6x
59 }6x
60}
61
62pub trait VirtQueue<'m> {
63 type Index: Clone + Copy;
64 const INIT_INDEX: Self::Index;
65 fn desc_avail(&self, index: Self::Index) -> bool;
66 fn get_avail(&self, index: Self::Index, ram: &'m Ram) -> Result<Option<DescChain<'m>>>;
67 fn set_used(&self, index: Self::Index, id: u16, len: u32);
68 fn enable_notification(&self, enabled: bool);
69 fn interrupt_enabled(&self, index: Self::Index, delta: u16) -> bool;
70 fn index_add(&self, index: Self::Index, delta: u16) -> Self::Index;
71}
72
73#[derive(Debug)]
74pub enum Status {
75 Done { len: u32 },
76 Deferred,
77 Break,
78}
79
80pub struct Queue<'r, 'm, Q>
81where
82 Q: VirtQueue<'m>,
83{
84 q: Q,
85 avail: Q::Index,
86 used: Q::Index,
87 reg: &'r QueueReg,
88 ram: &'m Ram,
89 deferred: HashMap<u16, DescChain<'m>>,
90}
91
92impl<'r, 'm, Q> Queue<'r, 'm, Q>
93where
94 Q: VirtQueue<'m>,
95{
96 pub fn new(q: Q, reg: &'r QueueReg, ram: &'m Ram) -> Self {21x
97 Self {21x
98 q,21x
99 avail: Q::INIT_INDEX,21x
100 used: Q::INIT_INDEX,21x
101 reg,21x
102 ram,21x
103 deferred: HashMap::new(),21x
104 }21x
105 }21x
106
107 pub fn reg(&self) -> &QueueReg {3x
108 self.reg3x
109 }3x
110
111 fn push_used(&mut self, chain: DescChain, len: u32) {57x
112 self.q.set_used(self.used, chain.id, len);57x
113 self.used = self.q.index_add(self.used, chain.delta);57x
114 }57x
115
116 pub fn handle_deferred(9x
117 &mut self,9x
118 id: u16,9x
119 q_index: u16,9x
120 irq_sender: &impl IrqSender,9x
121 mut op: impl FnMut(&mut DescChain) -> Result<u32>,9x
122 ) -> Result<()> {9x
123 let Some(mut chain) = self.deferred.remove(&id) else {9x
124 return error::InvalidDescriptor { id }.fail();3x
125 };
126 let len = op(&mut chain)?;6x
127 let delta = chain.delta;6x
128 self.push_used(chain, len);6x
129 if self.q.interrupt_enabled(self.used, delta) {6x
130 irq_sender.queue_irq(q_index);6x
131 }6x
132 Ok(())6x
133 }9x
134
135 pub fn handle_desc(93x
136 &mut self,93x
137 q_index: u16,93x
138 irq_sender: &impl IrqSender,93x
139 mut op: impl FnMut(&mut DescChain) -> Result<Status>,93x
140 ) -> Result<()> {93x
141 let mut send_irq = false;93x
142 let mut ret = Ok(());93x
143 'out: loop {
144 if !self.q.desc_avail(self.avail) {147x
145 break;75x
146 }72x
147 self.q.enable_notification(false);72x
148 while let Some(mut chain) = self.q.get_avail(self.avail, self.ram)? {129x
149 let delta = chain.delta;75x
150 match op(&mut chain) {75x
151 Err(e) => {6x
152 ret = Err(e);6x
153 self.q.enable_notification(true);6x
154 break 'out;6x
155 }
156 Ok(Status::Break) => break 'out,12x
157 Ok(Status::Done { len }) => {51x
158 self.push_used(chain, len);51x
159 send_irq = send_irq || self.q.interrupt_enabled(self.used, delta);51x
160 }
161 Ok(Status::Deferred) => {6x
162 self.deferred.insert(chain.id, chain);6x
163 }6x
164 }
165 self.avail = self.q.index_add(self.avail, delta);57x
166 }
167 self.q.enable_notification(true);54x
168 fence(Ordering::SeqCst);54x
169 }
170 if send_irq {93x
171 fence(Ordering::SeqCst);51x
172 irq_sender.queue_irq(q_index);51x
173 }51x
174 ret93x
175 }93x
176}
177
178pub fn copy_from_reader(mut reader: impl Read) -> impl FnMut(&mut DescChain) -> Result<Status> {36x
179 move |chain| {27x
180 let ret = reader.read_vectored(&mut chain.writable);27x
181 match ret {6x
182 Ok(0) => {
183 let size: usize = chain.writable.iter().map(|s| s.len()).sum();6x
184 if size == 0 {6x
185 Ok(Status::Done { len: 0 })3x
186 } else {
187 Ok(Status::Break)3x
188 }
189 }
190 Ok(len) => Ok(Status::Done { len: len as u32 }),15x
191 Err(e) if e.kind() == ErrorKind::WouldBlock => Ok(Status::Break),6x
192 Err(e) => Err(e.into()),3x
193 }
194 }27x
195}36x
196
197pub fn copy_to_writer(mut writer: impl Write) -> impl FnMut(&mut DescChain) -> Result<Status> {27x
198 move |chain| {21x
199 let ret = writer.write_vectored(&chain.readable);21x
200 match ret {6x
201 Ok(0) => {
202 let size: usize = chain.readable.iter().map(|s| s.len()).sum();6x
203 if size == 0 {6x
204 Ok(Status::Done { len: 0 })3x
205 } else {
206 Ok(Status::Break)3x
207 }
208 }
209 Ok(_) => Ok(Status::Done { len: 0 }),9x
210 Err(e) if e.kind() == ErrorKind::WouldBlock => Ok(Status::Break),6x
211 Err(e) => Err(e.into()),3x
212 }
213 }21x
214}27x
215
216#[cfg(test)]
217#[path = "queue_test.rs"]
218pub(in crate::virtio) mod tests;
219