1use crate::state::Ctx;
11use crate::sys::*;
12use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
13use std::sync::{Arc, Condvar, Mutex};
14use std::time::{Duration, Instant};
15
16pub struct Pool {
18 listener: i32,
19 token: Mutex<()>,
20 park: Mutex<()>,
21 cv: Condvar,
22 parked: AtomicUsize,
23 closed: AtomicBool,
24 budget: AtomicU64,
26}
27
28const FOLLOWER_SPIN_US: u64 = 20;
30
31fn recv(listener: i32) -> Option<SeccompNotif> {
39 loop {
40 let mut req = SeccompNotif::default();
41 if unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV as _, &mut req as *mut _) } == 0 {
42 return Some(req);
43 }
44 match errno() {
45 libc::EINTR => continue,
46 libc::ENOENT => {
47 let mut pfd = libc::pollfd {
48 fd: listener,
49 events: libc::POLLIN,
50 revents: 0,
51 };
52 if unsafe { libc::poll(&mut pfd, 1, 0) } > 0 && pfd.revents & libc::POLLHUP != 0 {
53 return None;
54 }
55 continue;
56 }
57 _ => return None,
58 }
59 }
60}
61
62fn wait_ready(listener: i32, budget: u64) -> bool {
67 let mut pfd = libc::pollfd {
68 fd: listener,
69 events: libc::POLLIN,
70 revents: 0,
71 };
72 if budget > 0 {
73 let start = Instant::now();
74 loop {
75 pfd.revents = 0;
76 if unsafe { libc::poll(&mut pfd, 1, 0) } > 0 {
77 return true;
78 }
79 if start.elapsed().as_micros() >= budget as u128 {
80 break;
81 }
82 std::hint::spin_loop();
83 }
84 }
85 unsafe { libc::poll(&mut pfd, 1, -1) };
86 false
87}
88
89fn thread_main(listener: i32, pool: Arc<Pool>, ctx: Arc<Ctx>, spin_us: u64) {
90 loop {
91 if pool.closed.load(Ordering::Acquire) {
92 return;
93 }
94 let guard = 'tok: loop {
95 let start = Instant::now();
96 let follower_spin = FOLLOWER_SPIN_US.min(pool.budget.load(Ordering::Relaxed));
97 loop {
98 if let Ok(g) = pool.token.try_lock() {
99 break 'tok g;
100 }
101 if start.elapsed().as_micros() >= follower_spin as u128 {
102 break;
103 }
104 std::hint::spin_loop();
105 }
106 let pg = pool.park.lock().unwrap();
109 if let Ok(g) = pool.token.try_lock() {
110 break 'tok g;
111 }
112 pool.parked.fetch_add(1, Ordering::AcqRel);
113 let _ = pool.cv.wait_timeout(pg, Duration::from_secs(1)).unwrap();
114 pool.parked.fetch_sub(1, Ordering::AcqRel);
115 if pool.closed.load(Ordering::Acquire) {
116 return;
117 }
118 };
119 let budget = pool.budget.load(Ordering::Relaxed);
120 let hit = wait_ready(listener, budget);
121 pool.budget
122 .store(if hit { spin_us } else { budget / 2 }, Ordering::Relaxed);
123 let req = recv(listener);
124 drop(guard);
125 if pool.parked.load(Ordering::Acquire) > 0 {
126 let _pg = pool.park.lock().unwrap();
127 pool.cv.notify_one();
128 }
129 match req {
130 Some(r) => crate::syscall::notify::handle(&ctx, listener, &r),
131 None => {
132 pool.closed.store(true, Ordering::Release);
133 pool.cv.notify_all();
134 return;
135 }
136 }
137 }
138}
139
140impl Pool {
141 pub fn wait_closed(&self) {
148 while !self.closed.load(Ordering::Acquire) {
149 let mut pfd = libc::pollfd {
150 fd: self.listener,
151 events: 0,
152 revents: 0,
153 };
154 if unsafe { libc::poll(&mut pfd, 1, 200) } > 0 && pfd.revents & libc::POLLHUP != 0 {
155 self.closed.store(true, Ordering::Release);
156 self.cv.notify_all();
157 break;
158 }
159 }
160 }
161}
162
163pub fn start(listener: i32, ctx: Arc<Ctx>) -> Arc<Pool> {
172 let pool = Arc::new(Pool {
173 listener,
174 token: Mutex::new(()),
175 park: Mutex::new(()),
176 cv: Condvar::new(),
177 parked: AtomicUsize::new(0),
178 closed: AtomicBool::new(false),
179 budget: AtomicU64::new(ctx.config.spin_us),
180 });
181 let threads = ctx.config.threads.max(1);
182 let spin_us = ctx.config.spin_us;
183 for i in 0..threads {
184 let (pool, ctx) = (pool.clone(), ctx.clone());
185 std::thread::Builder::new()
186 .name(format!("notify-{i}"))
187 .spawn(move || thread_main(listener, pool, ctx, spin_us))
188 .expect("spawn supervisor thread");
189 }
190 pool
191}
192
193pub fn reply(listener: i32, id: u64, result: Result<i64>) {
200 let resp = match result {
201 Ok(v) => SeccompNotifResp {
202 id,
203 val: v,
204 error: 0,
205 flags: 0,
206 },
207 Err(e) => SeccompNotifResp {
208 id,
209 val: 0,
210 error: -e,
211 flags: 0,
212 },
213 };
214 unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND as _, &resp as *const _) };
215}
216
217pub fn reply_continue(listener: i32, id: u64) {
219 let resp = SeccompNotifResp {
220 id,
221 val: 0,
222 error: 0,
223 flags: SECCOMP_USER_NOTIF_FLAG_CONTINUE,
224 };
225 unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND as _, &resp as *const _) };
226}
227
228pub fn reply_fd(listener: i32, id: u64, fd: i32, cloexec: bool) {
238 let add = SeccompNotifAddfd {
239 id,
240 flags: SECCOMP_ADDFD_FLAG_SEND,
241 srcfd: fd as u32,
242 newfd: 0,
243 newfd_flags: if cloexec { libc::O_CLOEXEC as u32 } else { 0 },
244 };
245 let rc = unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD as _, &add as *const _) };
246 if rc < 0 {
247 let e = errno();
248 if e == libc::EINVAL {
249 let add2 = SeccompNotifAddfd { flags: 0, ..add };
251 let newfd = unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD as _, &add2 as *const _) };
252 reply(
253 listener,
254 id,
255 if newfd < 0 { Err(errno()) } else { Ok(newfd as i64) },
256 );
257 } else if e != libc::ENOENT {
258 reply(
261 listener,
262 id,
263 Err(if e == libc::EBADF { libc::EOPNOTSUPP } else { e }),
264 );
265 }
266 }
267 unsafe { libc::close(fd) };
268}
269
270pub fn addfd_at(listener: i32, id: u64, fd: i32, newfd: i32, cloexec: bool) -> Result<()> {
287 let add = SeccompNotifAddfd {
288 id,
289 flags: SECCOMP_ADDFD_FLAG_SETFD,
290 srcfd: fd as u32,
291 newfd: newfd as u32,
292 newfd_flags: if cloexec { libc::O_CLOEXEC as u32 } else { 0 },
293 };
294 let rc = unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD as _, &add as *const _) };
295 let e = errno();
296 unsafe { libc::close(fd) };
297 if rc < 0 { Err(e) } else { Ok(()) }
298}
299
300pub fn addfd(listener: i32, id: u64, fd: i32, cloexec: bool) -> Result<i32> {
315 let add = SeccompNotifAddfd {
316 id,
317 flags: 0,
318 srcfd: fd as u32,
319 newfd: 0,
320 newfd_flags: if cloexec { libc::O_CLOEXEC as u32 } else { 0 },
321 };
322 let rc = unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD as _, &add as *const _) };
323 let e = errno();
324 unsafe { libc::close(fd) };
325 if rc < 0 { Err(e) } else { Ok(rc) }
326}
327
328pub fn still_valid(listener: i32, id: u64) -> bool {
332 unsafe { libc::ioctl(listener, SECCOMP_IOCTL_NOTIF_ID_VALID as _, &id as *const _) == 0 }
333}