vyre_runtime/megakernel/io/
queue.rs1use std::sync::atomic::{fence, Ordering};
4
5use crate::PipelineError;
6
7use super::super::protocol::slot;
8use super::helpers::try_queue_word_index;
9use super::{io_op, io_status, io_word, IoCompletion, IO_SLOT_COUNT, IO_SLOT_WORDS};
10
11#[derive(Debug, Clone)]
14pub struct MegakernelIoQueue {
15 words: Vec<u32>,
16 slot_count: u32,
17}
18
19impl MegakernelIoQueue {
20 pub fn new(slot_count: u32) -> Result<Self, PipelineError> {
27 if slot_count == 0 {
28 return Err(PipelineError::QueueFull {
29 queue: "submission",
30 fix: "MegakernelIoQueue requires at least one slot",
31 });
32 }
33 if slot_count > IO_SLOT_COUNT {
34 return Err(PipelineError::QueueFull {
35 queue: "submission",
36 fix: "MegakernelIoQueue exceeds the compiled IO poll window of 64 slots; enlarge IO_SLOT_COUNT and rebuild the megakernel before publishing more than 64 completions",
37 });
38 }
39 let word_count = slot_count
40 .checked_mul(IO_SLOT_WORDS)
41 .ok_or(PipelineError::QueueFull {
42 queue: "submission",
43 fix: "io_queue word count overflows u32; shard the queue before allocating",
44 })?;
45 let word_count = usize::try_from(word_count).map_err(|error| {
46 PipelineError::Backend(format!(
47 "io_queue word count cannot fit host usize: {error}. Fix: shard the queue before allocating."
48 ))
49 })?;
50 Ok(Self {
51 words: vec![0; word_count],
52 slot_count,
53 })
54 }
55
56 #[must_use]
58 pub fn as_bytes(&self) -> &[u8] {
59 bytemuck::cast_slice(&self.words)
60 }
61
62 #[must_use]
64 pub fn as_mut_bytes(&mut self) -> &mut [u8] {
65 bytemuck::cast_slice_mut(&mut self.words)
66 }
67
68 #[must_use]
70 pub fn slot_count(&self) -> u32 {
71 self.slot_count
72 }
73
74 pub fn publish_slot(
84 &mut self,
85 queue_slot: u32,
86 mapped_slot: u32,
87 byte_count: u32,
88 tag: u32,
89 ) -> Result<(), PipelineError> {
90 self.publish_dma_read(
91 queue_slot,
92 0,
93 mapped_slot,
94 byte_count,
95 tag,
96 "io_queue slot exceeds MegakernelIoQueue::slot_count; enlarge the queue or publish into a valid slot id",
97 "io_queue slot still in flight; wait for the GPU to recycle it before publishing again",
98 )
99 }
100
101 pub fn submit_dma_read(
112 &mut self,
113 queue_slot: u32,
114 src_handle: u32,
115 dst_handle: u32,
116 byte_count: u32,
117 tag: u32,
118 ) -> Result<(), PipelineError> {
119 self.publish_dma_read(
120 queue_slot,
121 src_handle,
122 dst_handle,
123 byte_count,
124 tag,
125 "io_queue slot exceeds MegakernelIoQueue::slot_count; enlarge the queue or submit into a valid slot id",
126 "io_queue slot still in flight; wait for completion before submitting a new request",
127 )
128 }
129
130 #[allow(clippy::too_many_arguments)]
131 fn publish_dma_read(
132 &mut self,
133 queue_slot: u32,
134 src_handle: u32,
135 dst_handle: u32,
136 byte_count: u32,
137 tag: u32,
138 out_of_bounds_fix: &'static str,
139 in_flight_fix: &'static str,
140 ) -> Result<(), PipelineError> {
141 if queue_slot >= self.slot_count {
142 return Err(PipelineError::QueueFull {
143 queue: "submission",
144 fix: out_of_bounds_fix,
145 });
146 }
147 if self.read_word(queue_slot, io_word::STATUS)? != slot::EMPTY {
148 return Err(PipelineError::QueueFull {
149 queue: "submission",
150 fix: in_flight_fix,
151 });
152 }
153 self.write_word_unfenced(queue_slot, io_word::OP_TYPE, io_op::READ)?;
154 self.write_word_unfenced(queue_slot, io_word::SRC_HANDLE, src_handle)?;
155 self.write_word_unfenced(queue_slot, io_word::DST_HANDLE, dst_handle)?;
156 self.write_word_unfenced(queue_slot, io_word::OFFSET_LO, 0)?;
157 self.write_word_unfenced(queue_slot, io_word::OFFSET_HI, 0)?;
158 self.write_word_unfenced(queue_slot, io_word::BYTE_COUNT, byte_count)?;
159 self.write_word_unfenced(queue_slot, io_word::TAG, tag)?;
160 fence(Ordering::Release);
161 self.write_word_unfenced(queue_slot, io_word::STATUS, slot::PUBLISHED)?;
162 fence(Ordering::Release);
163 Ok(())
164 }
165
166 #[must_use]
172 pub fn completion(&self, queue_slot: u32) -> Option<IoCompletion> {
173 if queue_slot >= self.slot_count {
174 return None;
175 }
176 let status = self
181 .read_word(queue_slot, io_word::STATUS)
182 .expect("IO queue word index overflow is impossible after slot bounds check; Fix: ensure slot_count <= IO_SLOT_COUNT before calling completion");
183 if status == slot::EMPTY {
184 return None;
185 }
186 Some(IoCompletion {
187 slot_idx: queue_slot,
188 mapped_slot: self
189 .read_word_unfenced(queue_slot, io_word::DST_HANDLE)
190 .expect("IO queue word index overflow is impossible after slot bounds check; Fix: ensure slot_count <= IO_SLOT_COUNT before calling completion"),
191 byte_count: self
192 .read_word_unfenced(queue_slot, io_word::BYTE_COUNT)
193 .expect("IO queue word index overflow is impossible after slot bounds check; Fix: ensure slot_count <= IO_SLOT_COUNT before calling completion"),
194 tag: self
195 .read_word_unfenced(queue_slot, io_word::TAG)
196 .expect("IO queue word index overflow is impossible after slot bounds check; Fix: ensure slot_count <= IO_SLOT_COUNT before calling completion"),
197 })
198 }
199
200 #[must_use]
206 pub fn is_recycled(&self, queue_slot: u32) -> bool {
207 if queue_slot >= self.slot_count {
208 return false;
209 }
210 let status = self
213 .read_word(queue_slot, io_word::STATUS)
214 .expect("IO queue word index overflow is impossible after slot bounds check; Fix: ensure slot_count <= IO_SLOT_COUNT before calling is_recycled");
215 match status {
216 slot::EMPTY => true,
217 slot::PUBLISHED | slot::CLAIMED | io_status::OK | io_status::ERROR | slot::DONE => {
218 false
219 }
220 _ => false,
221 }
222 }
223
224 fn read_word(&self, slot_idx: u32, word: u32) -> Result<u32, PipelineError> {
225 let idx = try_queue_word_index(slot_idx, word)?;
226 fence(Ordering::Acquire);
227 Ok(self.words[idx])
228 }
229
230 fn read_word_unfenced(&self, slot_idx: u32, word: u32) -> Result<u32, PipelineError> {
231 let idx = try_queue_word_index(slot_idx, word)?;
232 Ok(self.words[idx])
233 }
234
235 fn write_word_unfenced(
236 &mut self,
237 slot_idx: u32,
238 word: u32,
239 value: u32,
240 ) -> Result<(), PipelineError> {
241 let idx = try_queue_word_index(slot_idx, word)?;
242 self.words[idx] = value;
243 Ok(())
244 }
245}