strat9_kernel/ipc/
reply.rs1use super::message::IpcMessage;
11use crate::{
12 async_io::{complete::push_completion_for_ring, ring::find_ring},
13 memory::UserSliceWrite,
14 process::TaskId,
15 sync::{SpinLock, WaitQueue},
16};
17use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
18
19enum ReplyTarget {
25 Sync {
26 msg: Option<IpcMessage>,
27 waitq: Arc<WaitQueue>,
28 },
29
30 AsyncRing {
31 ring_id: u64,
32 user_data: u64,
33 reply_buf: u64,
34 },
35}
36
37struct ReplySlot {
38 target: ReplyTarget,
39
40 waiting_on: Option<TaskId>,
41}
42
43struct ReplyRegistry {
44 slots: BTreeMap<TaskId, ReplySlot>,
45}
46
47impl ReplyRegistry {
48 const fn new() -> Self {
49 ReplyRegistry {
50 slots: BTreeMap::new(),
51 }
52 }
53}
54
55static REPLIES: SpinLock<ReplyRegistry> = SpinLock::new(ReplyRegistry::new());
56
57fn epipe_reply() -> IpcMessage {
62 let mut err = IpcMessage::new(0x80);
63 let epipe: u32 = 32;
64 err.payload[0..4].copy_from_slice(&epipe.to_le_bytes());
65 err
66}
67
68pub fn wait_for_reply(task_id: TaskId, waiting_on: TaskId) -> IpcMessage {
78 let waitq = {
79 let mut registry = REPLIES.lock();
80 let slot = registry.slots.entry(task_id).or_insert_with(|| ReplySlot {
81 target: ReplyTarget::Sync {
82 msg: None,
83 waitq: Arc::new(WaitQueue::new()),
84 },
85 waiting_on: Some(waiting_on),
86 });
87 slot.waiting_on = Some(waiting_on); match &slot.target {
90 ReplyTarget::Sync { waitq, .. } => waitq.clone(),
91 ReplyTarget::AsyncRing { .. } => {
92 return epipe_reply();
93 }
94 }
95 };
96
97 let msg = waitq.wait_until(|| {
98 let mut registry = REPLIES.lock();
99 match registry.slots.get_mut(&task_id) {
100 Some(ReplySlot {
101 target: ReplyTarget::Sync { msg, .. },
102 ..
103 }) => msg.take(),
104 _ => Some(epipe_reply()),
105 }
106 });
107
108 let mut registry = REPLIES.lock();
109 registry.slots.remove(&task_id);
110 msg
111}
112
113pub fn register_ring_call(
121 caller: TaskId,
122 waiting_on: TaskId,
123 ring_id: u64,
124 user_data: u64,
125 reply_buf: u64,
126) {
127 let mut registry = REPLIES.lock();
128 registry.slots.insert(
129 caller,
130 ReplySlot {
131 target: ReplyTarget::AsyncRing {
132 ring_id,
133 user_data,
134 reply_buf,
135 },
136 waiting_on: Some(waiting_on),
137 },
138 );
139}
140
141pub fn cancel_replies_waiting_on(dead_task: TaskId) {
142 let mut actions = alloc::vec::Vec::new();
143
144 {
145 let mut registry = REPLIES.lock();
146 let to_cancel: Vec<TaskId> = registry
147 .slots
148 .iter()
149 .filter(|(_, slot)| slot.waiting_on == Some(dead_task))
150 .map(|(id, _)| *id)
151 .collect();
152
153 for id in to_cancel {
154 let slot = registry.slots.remove(&id);
155 if let Some(slot) = slot {
156 actions.push((id, slot.target));
157 }
158 }
159 }
160
161 for (_id, target) in actions {
162 match target {
163 ReplyTarget::Sync { waitq, .. } => {
164 waitq.wake_all();
168 }
169 ReplyTarget::AsyncRing {
170 ring_id, user_data, ..
171 } => {
172 push_completion_for_ring_by_id(ring_id, user_data, -32, 0);
173 }
174 }
175 }
176}
177
178pub fn deliver_reply(target: TaskId, msg: IpcMessage) -> Result<(), ()> {
181 let mut registry = REPLIES.lock();
182
183 let slot = registry.slots.get_mut(&target).ok_or(())?;
185
186 match &mut slot.target {
187 ReplyTarget::Sync {
188 msg: slot_msg,
189 waitq,
190 } => {
191 slot_msg.replace(msg);
192 let wq = waitq.clone();
193 drop(registry);
194 wq.wake_one();
195 Ok(())
196 }
197 ReplyTarget::AsyncRing {
198 ring_id,
199 user_data,
200 reply_buf,
201 } => {
202 let ring_id = *ring_id;
203 let user_data = *user_data;
204 let reply_buf = *reply_buf;
205
206 registry.slots.remove(&target);
207
208 let msg_size = core::mem::size_of::<IpcMessage>();
209 let mut raw = [0u8; core::mem::size_of::<IpcMessage>()];
210 crate::ipc::message::ipc_message_to_raw(&msg, &mut raw);
211 if let Ok(user) = UserSliceWrite::new(reply_buf, msg_size) {
212 let _ = user.copy_from(&raw);
213 }
214
215 drop(registry);
216
217 push_completion_for_ring_by_id(ring_id, user_data, 0, 0);
219 Ok(())
220 }
221 }
222}
223
224fn push_completion_for_ring_by_id(ring_id: u64, user_data: u64, result: i32, flags: u32) {
226 if let Some(ring) = find_ring(ring_id) {
227 push_completion_for_ring(&ring, user_data, result, flags);
228 }
229}