mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
browser: a GL submit runs after the kernel's lock, not under it
GlDevice's submit ioctl ran inside gex_syscall's locked dispatch, and a synchronous submit waits for the page to run the batch: every other thread's syscall (and every parked Worker's wake-up `schedule`) queued on KLOCK meanwhile. In Muck's menu in the page (mp16, a 20 s CPU profile of every Worker) the main thread spent 39% of its time in KLock::take while the render thread spent 45% in gl_submit. The ioctl now records the submit and `gex_syscall` does it once the lock is released (an error becomes -EIO in rax then); a submit taken outside that path still runs in place. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_d2453757b8a84fdbb4ee7c9e093141df
This commit is contained in:
1 parent
3137f7b048
commit
8842f365ab
2 files changed
+44
-7
No files matched your search
+40
-7
@@ -67,6 +67,36 @@ thread_local! {
|
||||
/// The part of that spent waiting for the previous, unwaited batch
|
||||
/// (`GEX_GL_SUBMIT_ASYNC`) to finish: the page says, `gex_gl_note_wait_ns`.
|
||||
pub(crate) static GL_WAIT_NS: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
|
||||
/// A submit the ioctl took but left for after the kernel's lock
|
||||
/// (`submit_pending`): (ring address, unwaited).
|
||||
static PENDING: std::cell::Cell<Option<(u32, bool)>> = const { std::cell::Cell::new(None) };
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// Set by `gex_syscall` around its locked dispatch, which calls
|
||||
/// `submit_pending` after: only there is a submit left for later.
|
||||
pub(crate) static DEFER: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
|
||||
}
|
||||
|
||||
fn submit_now(ring: u32, unwaited: bool) -> i32 {
|
||||
let t0 = crate::now_ns();
|
||||
let r = if unwaited { unsafe { gl_submit_async(ring) } } else { unsafe { gl_submit(ring) } };
|
||||
GL_NS.with(|g| g.set(g.get() + crate::now_ns().saturating_sub(t0)));
|
||||
r
|
||||
}
|
||||
|
||||
/// The submit the GL device's ioctl took, done now that the kernel's lock
|
||||
/// is released (`gex_syscall`): a synchronous submit waits for the page to
|
||||
/// run the batch, and under the lock every other thread's syscall waited
|
||||
/// with it (Muck's main thread spent 39% of its time on KLOCK while the
|
||||
/// render thread submitted, mp16). On failure the call's result becomes
|
||||
/// -EIO in `ctx`'s rax.
|
||||
pub(crate) fn submit_pending(ctx_addr: u64) {
|
||||
let Some((ring, unwaited)) = PENDING.with(|p| p.take()) else { return };
|
||||
if submit_now(ring, unwaited) < 0 {
|
||||
let mut m = GuestMem::new(crate::guest_bytes());
|
||||
let _ = m.write_u64(ctx_addr + gex_abi::ctx::x(0) as u64, (-(Errno::EIO.0 as i64)) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
/// From the page's GL bridge: how long a submit waited for the batch before
|
||||
@@ -121,13 +151,16 @@ impl FileHandle for GlDevice {
|
||||
if m.read(arg, 32).is_err() {
|
||||
return Some(Err(Errno::EFAULT));
|
||||
}
|
||||
let t0 = crate::now_ns();
|
||||
let r = if request & 0xffff_ffff == GEX_GL_SUBMIT_ASYNC {
|
||||
unsafe { gl_submit_async(arg as u32) }
|
||||
} else {
|
||||
unsafe { gl_submit(arg as u32) }
|
||||
};
|
||||
GL_NS.with(|g| g.set(g.get() + crate::now_ns().saturating_sub(t0)));
|
||||
let unwaited = request & 0xffff_ffff == GEX_GL_SUBMIT_ASYNC;
|
||||
// One left over (a path that did not finish it): now, in order.
|
||||
if let Some((ring, u)) = PENDING.with(|p| p.take()) {
|
||||
submit_now(ring, u);
|
||||
}
|
||||
if DEFER.with(|d| d.get()) {
|
||||
PENDING.with(|p| p.set(Some((arg as u32, unwaited))));
|
||||
return Some(Ok(0));
|
||||
}
|
||||
let r = submit_now(arg as u32, unwaited);
|
||||
Some(if r < 0 { Err(Errno::EIO) } else { Ok(0) })
|
||||
}
|
||||
GEX_GL_LIMITS => Some(m.write_u32(arg, MAX_BATCH).map(|()| 0)),
|
||||
|
||||
@@ -6050,6 +6050,8 @@ pub extern "C" fn gex_syscall(ctx: i32, pc: i64) {
|
||||
// warm process. The per-number census above stays behind the heartbeat.
|
||||
let began = now_ns();
|
||||
SYS_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
// A GL submit is taken under the lock and done after it (gl.rs).
|
||||
gl::DEFER.with(|d| d.set(true));
|
||||
let stop = with_host(|h| {
|
||||
let mut gm = GuestMem::new(guest_bytes());
|
||||
let kstate = h.threads[cur()].kstate.clone();
|
||||
@@ -6590,6 +6592,8 @@ pub extern "C" fn gex_syscall(ctx: i32, pc: i64) {
|
||||
}
|
||||
}
|
||||
});
|
||||
gl::DEFER.with(|d| d.set(false));
|
||||
gl::submit_pending(ctx as u32 as u64);
|
||||
{
|
||||
let took = now_ns().saturating_sub(began);
|
||||
SYS_TOTAL_NS.fetch_add(took, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
Reference in new issue
Block a user