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