mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Ask an armed region's page state under the freeze lock when a fault finds its range gone, so that a late write is not taken for a frozen page's and a read is not passed on past an arm.
The review found two windows in memory.c lazy_fault, where a fault holding frz_lock and lz_lock finds its address neither armed nor in flight. It returned 0 there, and access_fault then asked for the page's state without a lock. A write whose range a flight gave back while the write waited for frz_lock went on to thaw_fault, whose thaw released every armed range of the 2 MiB region (lazy_drop_region_locked), each fetched under the lock. And after a release that sets no hold (mem_lazy_skip's give-back of another buffer's range), the completion thread could arm the page again between that 0 and access_fault's query, so a read saw an inaccessible page and went to the crash handler; that one predates the ranges in flight. Now a fault that finds a flight there waits for it as before. Otherwise it releases lz_lock, asks for the page's state while it still holds frz_lock, under which no arm runs, and runs the access again when the page allows it: a read on a readable page, a write on a read-write one. A write to a frozen page still goes on to thaw_fault, and an address that is not ours to the crash handler. The new late_write scenario of tests/lazy_fetch_check.py has a thread write a range whose release starts and ends while the write waits for frz_lock: the write runs again and the region's other range stays armed. With the old return of 0 it fails, the other range released. The read's window has no check: the arm would have to land between two instructions. Not reproduced in the game before the change; lazy_fetch_check passed in all its scenarios. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -268,6 +268,30 @@ int main(int argc, char **argv) {
|
||||
CHECK(armed(s2, K64), "the region's other range was released");
|
||||
CHECK(lz_other == 0, "%llu other releases", (unsigned long long)lz_other);
|
||||
CHECK(b.took < 250 && b.got == 0x77, "the write waited %.1f ms and read back %#x", b.took, b.got);
|
||||
} else if (!strcmp(what, "late_write")) {
|
||||
// B writes a range whose flight starts and ends while B waits for frz_lock in lazy_fault (this thread holds the
|
||||
// lock and runs a release's flight under it, as a thaw does): B finds the page given back and writes again, and
|
||||
// the region's other range stays armed (B went on to thaw_fault before, whose thaw released it)
|
||||
uint64_t R = heap + 128 * M, s1 = R, s2 = R + M;
|
||||
CHECK(arm_buf(s1, K64, 1) == K64 && arm_buf(s2, K64, 1) == K64, "arms");
|
||||
stale(s1, K64, 1), stale(s2, K64, 1);
|
||||
fetch_ns = 20000000ull;
|
||||
plat_mutex_lock(&frz_lock);
|
||||
Touch b;
|
||||
void *tb = touch(&b, s1 + 0x20, 1);
|
||||
plat_wait_ns(50000000); // B waits for frz_lock in lazy_fault, past its look for a range in flight
|
||||
int k[LZ_BATCH];
|
||||
LzSeg sg[LZ_BATCH];
|
||||
uint64_t rg[1][3] = {{s1, s1 + K64, 0}};
|
||||
plat_mutex_lock(&lz_lock);
|
||||
int nt = lazy_take_batch_locked((const uint64_t (*)[3])rg, 1, k, sg);
|
||||
plat_mutex_unlock(&lz_lock);
|
||||
if (nt > 0) lazy_fly_run(k, sg, nt);
|
||||
plat_mutex_unlock(&frz_lock);
|
||||
plat_thread_join(tb);
|
||||
CHECK(nt == 1 && b.got == 0x77, "%d taken out, the write read back %#x", nt, b.got);
|
||||
CHECK(armed(s2, K64) && lz_other == 0, "the region's other range was released (%llu other releases)",
|
||||
(unsigned long long)lz_other);
|
||||
} else if (!strcmp(what, "concurrent")) {
|
||||
// two threads touch two ranges at once: their fetches run at the same time
|
||||
uint64_t s1 = heap + 40 * M, s2 = heap + 48 * M;
|
||||
|
||||
@@ -27,6 +27,7 @@ SCENARIOS = {
|
||||
'no_stall': ('no_stall', {}),
|
||||
'join': ('join', {}),
|
||||
'loser': ('loser', {}),
|
||||
'late_write': ('late_write', {}),
|
||||
'concurrent': ('concurrent', {}),
|
||||
'thaw': ('thaw', {}),
|
||||
'thaw_unlocked': ('thaw_unlocked', {}),
|
||||
|
||||
Reference in New Issue
Block a user