hermit/drivers/virtio/virtqueue/
split.rs1use alloc::boxed::Box;
5use alloc::vec::Vec;
6use core::cell::UnsafeCell;
7use core::mem::{self, MaybeUninit};
8
9#[cfg(not(feature = "pci"))]
10use virtio::mmio::NotificationData;
11#[cfg(feature = "pci")]
12use virtio::pci::NotificationData;
13use virtio::{le16, virtq};
14
15#[cfg(not(feature = "pci"))]
16use super::super::transport::mmio::{ComCfg, NotifCfg, NotifCtrl};
17#[cfg(feature = "pci")]
18use super::super::transport::pci::{ComCfg, NotifCfg, NotifCtrl};
19use super::error::VirtqError;
20use super::{
21 AvailBufferToken, BufferType, MemPool, TransferToken, UsedBufferToken, Virtq, VirtqPrivate,
22 VqIndex, VqSize,
23};
24use crate::arch::memory_barrier;
25use crate::mm::device_alloc::DeviceAlloc;
26
27struct DescrRing {
28 read_idx: u16,
29 token_ring: Box<[Option<Box<TransferToken<virtq::Desc>>>]>,
30 mem_pool: MemPool,
31
32 descr_table_cell: Box<UnsafeCell<[MaybeUninit<virtq::Desc>]>, DeviceAlloc>,
33 avail_ring_cell: Box<UnsafeCell<virtq::Avail>, DeviceAlloc>,
34 used_ring_cell: Box<UnsafeCell<virtq::Used>, DeviceAlloc>,
35}
36
37impl DescrRing {
38 fn descr_table_mut(&mut self) -> &mut [MaybeUninit<virtq::Desc>] {
39 unsafe { &mut *self.descr_table_cell.get() }
40 }
41 fn avail_ring(&self) -> &virtq::Avail {
42 unsafe { &*self.avail_ring_cell.get() }
43 }
44 fn avail_ring_mut(&mut self) -> &mut virtq::Avail {
45 unsafe { &mut *self.avail_ring_cell.get() }
46 }
47 fn used_ring(&self) -> &virtq::Used {
48 unsafe { &*self.used_ring_cell.get() }
49 }
50
51 fn push(&mut self, tkn: TransferToken<virtq::Desc>) -> Result<u16, VirtqError> {
52 let mut index;
53 if let Some(ctrl_desc) = tkn.ctrl_desc.as_ref() {
54 let descriptor = SplitVq::indirect_desc(ctrl_desc.as_ref());
55
56 index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?.0;
57 self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
58 } else {
59 let mut rev_all_desc_iter = SplitVq::descriptor_iter(&tkn.buff_tkn)?.rev();
60
61 {
63 let descriptor = rev_all_desc_iter.next().unwrap();
65
66 index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?.0;
67 self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
68 }
69 for mut descriptor in rev_all_desc_iter {
70 descriptor.next = le16::from(index);
72
73 index = self.mem_pool.pool.pop().ok_or(VirtqError::NoDescrAvail)?.0;
74 self.descr_table_mut()[usize::from(index)] = MaybeUninit::new(descriptor);
75 }
76 }
79
80 self.token_ring[usize::from(index)] = Some(Box::new(tkn));
81
82 let len = self.token_ring.len();
83 let idx = self.avail_ring_mut().idx.to_ne();
84 self.avail_ring_mut().ring_mut(true)[idx as usize % len] = index.into();
85
86 memory_barrier();
87 let next_idx = idx.wrapping_add(1);
88 self.avail_ring_mut().idx = next_idx.into();
89
90 Ok(next_idx)
91 }
92
93 fn try_recv(&mut self) -> Result<UsedBufferToken, VirtqError> {
94 if self.read_idx == self.used_ring().idx.to_ne() {
95 return Err(VirtqError::NoNewUsed);
96 }
97 let cur_ring_index = self.read_idx as usize % self.token_ring.len();
98 let used_elem = self.used_ring().ring()[cur_ring_index];
99
100 let tkn = self.token_ring[used_elem.id.to_ne() as usize]
101 .take()
102 .expect(
103 "The buff_id is incorrect or the reference to the TransferToken was misplaced.",
104 );
105
106 let mut id_ret_idx = u16::try_from(used_elem.id.to_ne()).unwrap();
108 loop {
109 self.mem_pool.ret_id(super::MemDescrId(id_ret_idx));
110 let cur_chain_elem =
111 unsafe { self.descr_table_mut()[usize::from(id_ret_idx)].assume_init() };
112 if cur_chain_elem.flags.contains(virtq::DescF::NEXT) {
113 id_ret_idx = cur_chain_elem.next.to_ne();
114 } else {
115 break;
116 }
117 }
118
119 memory_barrier();
120 self.read_idx = self.read_idx.wrapping_add(1);
121 Ok(UsedBufferToken::from_avail_buffer_token(
122 tkn.buff_tkn,
123 used_elem.len.to_ne(),
124 ))
125 }
126
127 fn drv_enable_notif(&mut self) {
128 self.avail_ring_mut()
129 .flags
130 .remove(virtq::AvailF::NO_INTERRUPT);
131 }
132
133 fn drv_disable_notif(&mut self) {
134 self.avail_ring_mut()
135 .flags
136 .insert(virtq::AvailF::NO_INTERRUPT);
137 }
138
139 fn dev_is_notif(&self) -> bool {
140 !self.used_ring().flags.contains(virtq::UsedF::NO_NOTIFY)
141 }
142}
143
144pub struct SplitVq {
146 ring: DescrRing,
147 size: VqSize,
148 index: VqIndex,
149
150 notif_ctrl: NotifCtrl,
151}
152
153impl Virtq for SplitVq {
154 fn enable_notifs(&mut self) {
155 self.ring.drv_enable_notif();
156 }
157
158 fn disable_notifs(&mut self) {
159 self.ring.drv_disable_notif();
160 }
161
162 fn try_recv(&mut self) -> Result<UsedBufferToken, VirtqError> {
163 self.ring.try_recv()
164 }
165
166 fn dispatch_batch(
167 &mut self,
168 _tkns: Vec<(AvailBufferToken, BufferType)>,
169 _notif: bool,
170 ) -> Result<(), VirtqError> {
171 unimplemented!();
172 }
173
174 fn dispatch_batch_await(
175 &mut self,
176 _tkns: Vec<(AvailBufferToken, BufferType)>,
177 _notif: bool,
178 ) -> Result<(), VirtqError> {
179 unimplemented!()
180 }
181
182 fn dispatch(
183 &mut self,
184 buffer_tkn: AvailBufferToken,
185 notif: bool,
186 buffer_type: BufferType,
187 ) -> Result<(), VirtqError> {
188 let transfer_tkn = Self::transfer_token_from_buffer_token(buffer_tkn, buffer_type);
189 let next_idx = self.ring.push(transfer_tkn)?;
190
191 if notif {
192 unimplemented!();
195 }
196
197 if self.ring.dev_is_notif() {
198 let notification_data = NotificationData::new()
199 .with_vqn(self.index.0)
200 .with_next_idx(next_idx);
201 self.notif_ctrl.notify_dev(notification_data);
202 }
203 Ok(())
204 }
205
206 fn index(&self) -> VqIndex {
207 self.index
208 }
209
210 fn size(&self) -> VqSize {
211 self.size
212 }
213
214 fn has_used_buffers(&self) -> bool {
215 self.ring.read_idx != self.ring.used_ring().idx.to_ne()
216 }
217}
218
219impl VirtqPrivate for SplitVq {
220 type Descriptor = virtq::Desc;
221 fn create_indirect_ctrl(
222 buffer_tkn: &AvailBufferToken,
223 ) -> Result<Box<[Self::Descriptor]>, VirtqError> {
224 Ok(Self::descriptor_iter(buffer_tkn)?
225 .zip(1..)
226 .map(|(descriptor, next_id)| Self::Descriptor {
227 next: next_id.into(),
228 ..descriptor
229 })
230 .collect::<Vec<_>>()
231 .into_boxed_slice())
232 }
233}
234
235impl SplitVq {
236 pub(crate) fn new(
237 com_cfg: &mut ComCfg,
238 notif_cfg: &NotifCfg,
239 size: VqSize,
240 index: VqIndex,
241 features: virtio::F,
242 ) -> Result<Self, VirtqError> {
243 let Some(mut vq_handler) = com_cfg.select_vq(index.into()) else {
245 return Err(VirtqError::QueueNotExisting(index.into()));
246 };
247
248 let size = vq_handler.set_vq_size(size.0);
249
250 let mut descr_table_cell = unsafe {
251 core::mem::transmute::<
252 Box<[MaybeUninit<virtq::Desc>], DeviceAlloc>,
253 Box<UnsafeCell<[MaybeUninit<virtq::Desc>]>, DeviceAlloc>,
254 >(Box::new_uninit_slice_in(size.into(), DeviceAlloc))
255 };
256
257 let mut avail_ring_cell = {
258 let avail = virtq::Avail::try_new_in(size, true, DeviceAlloc)
259 .map_err(|_| VirtqError::AllocationError)?;
260
261 unsafe {
262 mem::transmute::<
263 Box<virtq::Avail, DeviceAlloc>,
264 Box<UnsafeCell<virtq::Avail>, DeviceAlloc>,
265 >(avail)
266 }
267 };
268
269 let mut used_ring_cell = {
270 let used = virtq::Used::try_new_in(size, true, DeviceAlloc)
271 .map_err(|_| VirtqError::AllocationError)?;
272
273 unsafe {
274 mem::transmute::<
275 Box<virtq::Used, DeviceAlloc>,
276 Box<UnsafeCell<virtq::Used>, DeviceAlloc>,
277 >(used)
278 }
279 };
280
281 vq_handler.set_ring_addr(DeviceAlloc.phys_addr_from(descr_table_cell.as_mut()));
283 vq_handler.set_drv_ctrl_addr(DeviceAlloc.phys_addr_from(avail_ring_cell.as_mut()));
285 vq_handler.set_dev_ctrl_addr(DeviceAlloc.phys_addr_from(used_ring_cell.as_mut()));
286
287 let descr_ring = DescrRing {
288 read_idx: 0,
289 token_ring: core::iter::repeat_with(|| None)
290 .take(size.into())
291 .collect::<Vec<_>>()
292 .into_boxed_slice(),
293 mem_pool: MemPool::new(size),
294
295 descr_table_cell,
296 avail_ring_cell,
297 used_ring_cell,
298 };
299
300 let mut notif_ctrl = NotifCtrl::new(notif_cfg.notification_location(&mut vq_handler));
301
302 if features.contains(virtio::F::NOTIFICATION_DATA) {
303 notif_ctrl.enable_notif_data();
304 }
305
306 vq_handler.enable_queue();
307
308 info!("Created SplitVq: idx={}, size={}", index.0, size);
309
310 Ok(SplitVq {
311 ring: descr_ring,
312 notif_ctrl,
313 size: VqSize(size),
314 index,
315 })
316 }
317}