Skip to content

Commit 06e67bb

Browse files
authored
vmop: add VMOP_KILL_THREAD and VMOP_KILL_CHILD (#65)
* vmop: fix killed-thread escape races and add VMOP_KILL_THREAD/KILL_CHILD Fixes the v68/opt pi-test hang (and the underlying correctness hole it exposed) and wires up a proper cross-thread kill API. ## Root cause A thread marked for death (KILL|VMWORK set by kill_thread_locked) could still be on its way into a blocking syscall. While it spun for the BKL it was still RUNNING, so the per-state kill action had already fired. Once it took the BKL the gate in futex_classic/futex_pi checked VMWORK&&IE — not VMWORK alone. Kill leaves IE alone, so with IE clear the thread sailed past, set STATUS_BLOCKED, and parked in a wait ring. Nobody reaped it; num_cpus never drained; the booter (no timer fallback) slept forever. A second bug: after h2_vmkill(VMOP_KILL_VM_SELF) some siblings died mid-cleanup holding pthread_exit_lock. The main thread's pthread_exit tried to acquire it and blocked forever (num_cpus stuck at 1) because the original kill loop skipped the caller with "if (target == me) continue". ## Changes vmop.ref.c / vmop.h / h2_vm.h - Add VMOP_KILL_THREAD and VMOP_KILL_CHILD operations. KILL_THREAD sets KILL+VMWORK on a target and forces it out of every state (VMWAIT, RUNNING, INTBLOCKED, BLOCKED, READY) ignoring guest IE. KILL_CHILD iterates a child VM's contexts[] under one BKL section. - Remove the "if (target == me) continue" skip in the kill_vm loop so the caller also receives KILL|VMWORK+IPI; the IPI fires before any mutex can be acquired after vmkill returns. futex_classic / futex_pi (ref + v4opt.S) - Change the VMWORK gate from VMWORK&&IE to VMWORK alone, matching what H2K_switch already does. Gate calls vm_do_work_withlock (BKL held), which handles KILL (never returns) or clears VMWORK and returns -1 so the caller retries. popup.ref.c / intpool.ref.c - Add the same VMWORK gate (these had no check at all). - popup.v4opt.S: add allocframe(#0) to make the function non-leaf, add vmstatus load + bitsset after k0lock, jump to fail label that calls vm_do_work_withlock; change jumpr r31 → dealloc_return at all early-return paths. - Note: h2_intwait (trap 11) routes to H2K_popup_wait; covered here. vmwork.ref.c - Remove the IE guard around the VMWORK clear. The interrupt controller retains the pending interrupt, so clearing VMWORK with IE disabled cannot lose work — it is re-discovered when the guest re-enables IE. pthread_thread.ref.c - Add h2_thread_stop_trap in the last==true branch of pthread_exit as defense-in-depth (now redundant given the kill-loop fix above, but kept as a hard guarantee). specs and docs - vmdefs.spec: document VMWORK contract ("reconsider before blocking"). - vmwork.spec: drop "and interrupts are enabled" from the clear rule. - futex_classic.spec, futex_pi.spec, popup.spec, intpool.spec: state the spurious-failure / retry contract for all block primitives. tests - futex/test/tests/vmwork: new directed unit test; cases B (VMWORK alone, no IE) and C (VMWORK|KILL) fail pre-fix, pass post-fix for both ref and opt builds. - vmwork/test: assert VMWORK is cleared after every non-KILL call, including the IE-disabled path. - vmop/test: updated assertions for new caller-kill behavior; KILL_THREAD and KILL_CHILD coverage. - popup/test, intpool/test: cases B and C mirroring the futex pattern. - libs/posix/pthread/test_h2: new pthread test suite covering join, detach, barriers, condvars, rwlocks, semaphores, TLS, and stuck-thread scenarios that exercise the vmkill path end-to-end. Verified: 1224 passed, 0 failed across all 9 variants (v68/v73/v81 x opt/ref/opt_si). Previously v68/opt and v81/opt_si pi-test hung on every run. Signed-off-by: Erich Plondke <erich@qti.qualcomm.com> * vmwork: remove H2K_vm_do_work wrapper; callers acquire BKL directly H2K_vm_do_work was a thin BKL acquire/call-withlock/release wrapper with a single call site (vmipi.ref.c). Inline the lock pair there and delete the wrapper, leaving H2K_vm_do_work_withlock as the sole public entry point. - vmipi.ref.c: inline BKL_LOCK/UNLOCK around _withlock call; drop duplicate #include <hw.h> - vmipi/test: stub H2K_vm_do_work_withlock instead of H2K_vm_do_work - vmwork.ref.c: remove wrapper; drop now-unused #include <hw.h> - vmwork.h: remove H2K_vm_do_work declaration - vmwork.spec: remove H2K_vm_do_work section; update description 1224/1224 tests pass across 9 variants. Signed-off-by: Erich Plondke <erich@qti.qualcomm.com> * popup,vmop: fix popup_cancel <<#2 typo and add dispatcher/cancel coverage tests popup.v4opt.S: H2K_popup_cancel used <<#2 (4-byte stride) when indexing inthandlers[], which is an array of 8-byte H2K_inthandler_t entries. For any intnum != 0 the wrong slot was cleared (e.g. cancelling intnum=2 cleared intnum=1's handler) and odd intnums produced a misaligned 64-bit store in the opt build. Fix to <<#3 to match H2K_popup_int and H2K_popup_wait which already use the correct stride. vmop/test: add dispatcher coverage -- OOB op codes (including -1, 100, and VMOP_MAX) must return -1, and VMOP_KILL_THREAD / VMOP_KILL_VM must dispatch correctly through H2K_vmop(). Declare H2K_vmop() in vmop.h so the test can call the dispatcher directly. popup/test: add H2K_popup_cancel coverage -- simulate the INTBLOCKED state left by popup_wait (handler registered, r00=intnum) and verify that cancel clears the handler slot and resets r00 to -1. Signed-off-by: Erich Plondke <erich@qti.qualcomm.com> * build: fix two bugs that broke make cov Makefile.inc.test: try_gmon_out is PHONY so it re-runs on every cov invocation, but hexagon-gmon-merge refuses to overwrite an existing gmon.out (exits 255). If a previous run left gmon.out behind (e.g. after a partial cov run following testall), subsequent cov runs would fail for every affected test. Fix by removing gmon.out at the top of try_gmon_out before the merge. makefile: the build recipe wrote "v$(ARCHV) $@ ..." to the ver file, so a plain "make build" produced "v81 build ..." instead of "v81 opt ...". merge_cov.py expects the second token to be "opt" or "ref" and exits with an error otherwise, preventing the merged cov.txt/cov.rpt from being generated. Fix by using $(TARGET) instead of $@. Signed-off-by: Erich Plondke <erich@qti.qualcomm.com> * build: don't symlink cov.txt from source tree into cov build dir The cov.txt rule symlinked all non-Makefile.inc files from the source directory into the per-test build directory. If a stale cov.txt happened to exist in the source tree (e.g. from a previous cov run that wrote through an earlier symlink), it would be linked in, causing the build-dir cov.txt target to write back through it into the source tree. On the next run, try_gmon_out would find gmon.out already present (from the previous testall run) and hexagon-gmon-merge would exit 255 with "gmon.out: File exists." Fix by excluding cov.txt and cov-*.txt from the symlink step, matching how generated_tests.dat and scenarios.h are already excluded. Signed-off-by: Erich Plondke <erich@qti.qualcomm.com> --------- Signed-off-by: Erich Plondke <erich@qti.qualcomm.com>
1 parent 364219e commit 06e67bb

154 files changed

Lines changed: 4791 additions & 97 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

kernel/event/intpool/intpool.ref.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <readylist.h>
1414
#include <switch.h>
1515
#include <dosched.h>
16+
#include <vmwork.h>
1617

1718
/*
1819
* EJP: FIXME: Should there be a single intpool for a vmblock?
@@ -72,6 +73,11 @@ int H2K_intpool_wait(u32_t int_ack_num, H2K_thread_context *me)
7273
H2K_intcontrol_enable(int_ack_num);
7374
}
7475
BKL_LOCK(&H2K_bkl);
76+
if (me->vmstatus & H2K_VMSTATUS_VMWORK) {
77+
H2K_vm_do_work_withlock(me);
78+
BKL_UNLOCK(&H2K_bkl);
79+
return -1;
80+
}
7581
if (unlikely(vmblock->intpool_anypending)) {
7682
if ((intno = get_pending_interrupt(vmblock)) >= 0) {
7783
BKL_UNLOCK(&H2K_bkl);

kernel/event/intpool/intpool.spec

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,22 @@ Note that this functionality is very similar to H2K_popup_int
5151
H2K_intpool_int
5252
---------------
5353

54-
.. c:function:: H2K_intpool_wait(u32_t int_ack_num, H2K_thread_context *me)
54+
.. c:function:: int H2K_intpool_wait(u32_t int_ack_num, H2K_thread_context *me)
5555

5656
:param int_ack_num: Interrupt to enable, or -1 for no interrupt
5757
:param me: Context for the current thread
58+
:returns: the interrupt number on wake, -1 on failure (including spurious VMWORK bail)
5859

5960
Description
6061
~~~~~~~~~~~
6162

6263
This function optionally enables an interrupt, and then waits for an interrupt to arrive.
6364

65+
Per the VMWORK contract (see :doc:`../../util/vmdefs/vmdefs`), -1 may
66+
be returned spuriously when VMWORK is observed after the BKL is
67+
acquired. Callers must treat -1 as "did not block -- re-check the
68+
wait condition and retry."
69+
6470
Functionality
6571
~~~~~~~~~~~~~
6672

kernel/event/intpool/test/test.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <checker_ready.h>
2020
#include <setjmp.h>
2121
#include <globals.h>
22+
#include <vmdefs.h>
2223

2324
void FAIL(const char *str)
2425
{
@@ -27,6 +28,14 @@ void FAIL(const char *str)
2728
exit(1);
2829
}
2930

31+
static int TH_saw_do_work;
32+
33+
int H2K_vm_do_work_withlock(H2K_thread_context *me)
34+
{
35+
TH_saw_do_work = 1;
36+
return -1;
37+
}
38+
3039
H2K_thread_context *TB_in;
3140

3241
u32_t TB_saw_dosched = 0;
@@ -358,6 +367,28 @@ int main()
358367
if ((fakeint[i/32+0x200/sizeof(u32_t)-1] & (1<<(i&0x1f))) == 0) FAIL("not acked");
359368
}
360369

370+
/* Case B: VMWORK alone -- gate must fire and return -1 without INTBLOCKED */
371+
H2K_runlist_push(&a);
372+
a.status = H2K_STATUS_RUNNING;
373+
a.vmstatus = H2K_VMSTATUS_VMWORK;
374+
TH_saw_do_work = 0;
375+
if (TH_call_intpool_wait(0, &a) >= 0) FAIL("B: intpool_wait should fail with VMWORK set");
376+
if (!TH_saw_do_work) FAIL("B: VMWORK alone must call vm_do_work");
377+
if (a.status == H2K_STATUS_INTBLOCKED) FAIL("B: must not set INTBLOCKED");
378+
a.vmstatus = 0;
379+
H2K_runlist_remove(&a);
380+
381+
/* Case C: VMWORK|KILL -- killed thread racing into intpool_wait */
382+
H2K_runlist_push(&a);
383+
a.status = H2K_STATUS_RUNNING;
384+
a.vmstatus = H2K_VMSTATUS_VMWORK | H2K_VMSTATUS_KILL;
385+
TH_saw_do_work = 0;
386+
if (TH_call_intpool_wait(0, &a) >= 0) FAIL("C: intpool_wait should fail with VMWORK|KILL");
387+
if (!TH_saw_do_work) FAIL("C: VMWORK|KILL must call vm_do_work");
388+
if (a.status == H2K_STATUS_INTBLOCKED) FAIL("C: must not set INTBLOCKED");
389+
a.vmstatus = 0;
390+
H2K_runlist_remove(&a);
391+
361392
puts("TEST PASSED\n");
362393
return 0;
363394
}

kernel/event/popup/popup.ref.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <switch.h>
1616
#include <max.h>
1717
#include <intcontrol.h>
18+
#include <vmwork.h>
1819

1920
void H2K_popup_int(u32_t intnum, H2K_thread_context *me, u32_t hwtnum, H2K_thread_context *woken)
2021
{
@@ -50,6 +51,11 @@ int H2K_popup_wait(u32_t intnum, H2K_thread_context *me)
5051
if (intnum == L2_CORE_INTERRUPT) return -1;
5152

5253
BKL_LOCK(&H2K_bkl);
54+
if (me->vmstatus & H2K_VMSTATUS_VMWORK) {
55+
H2K_vm_do_work_withlock(me);
56+
BKL_UNLOCK(&H2K_bkl);
57+
return -1;
58+
}
5359
if (H2K_gp->inthandlers[intnum].param != NULL) {
5460
BKL_UNLOCK(&H2K_bkl);
5561
return -1;

kernel/event/popup/popup.spec

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,22 @@ interrupt disabled, we allow it to pend while the popup thread executes.
4545
H2K_popup_int
4646
-------------
4747

48-
.. c:function:: H2K_popup_wait(u32_t intno, H2K_thread_context *me)
48+
.. c:function:: int H2K_popup_wait(u32_t intno, H2K_thread_context *me)
4949

5050
:param intno: Interrupt number
5151
:param me: Context for the current thread
52+
:returns: the interrupt number on wake, -1 on failure (including spurious VMWORK bail or cancel)
5253

5354
Description
5455
~~~~~~~~~~~
5556

5657
This function blocks a thread to waiting for an interrupt.
5758

59+
Per the VMWORK contract (see :doc:`../../util/vmdefs/vmdefs`), -1 may
60+
be returned spuriously when VMWORK is observed after the BKL is
61+
acquired. Callers must treat -1 as "did not block -- re-check the
62+
wait condition and retry."
63+
5864
Functionality
5965
~~~~~~~~~~~~~
6066

kernel/event/popup/popup.v4opt.S

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,27 @@ FUNC_START H2K_popup_wait .text.int.popup .falign
137137
#define ACKBASE r12
138138
#define MASK r13
139139
#define RETURN_PAIR r15:14
140+
{
141+
allocframe(#0)
142+
HWTNUM = memub(ME+#CONTEXT_hthread)
143+
}
140144
{
141145
p0 = cmp.gtu(INTNO,#MAX_INTERRUPTS-1)
142146
if (p0.new) jump:nt 9f
143147
p1 = cmp.eq(INTNO, #L2_CORE_INTERRUPT)
144148
if (p1.new) jump:nt 9f
145-
HWTNUM = memub(ME+#CONTEXT_hthread)
146149
}
147150
k0lock
151+
#define TMP INTHANDLERS_ADDR
152+
{
153+
TMP = memub(ME+#CONTEXT_vmstatus)
154+
MASK = #H2K_VMSTATUS_VMWORK
155+
}
156+
{
157+
p0 = bitsset(TMP,MASK)
158+
if (p0.new) jump:nt .Lpopup_wait_vmwork
159+
}
160+
#undef TMP
148161
{
149162
PARAM_HANDLER = combine(ME,##H2K_popup_int)
150163
INTHANDLERS_ADDR = add(H2K_GP,#KG_inthandlers)
@@ -199,7 +212,17 @@ FUNC_START H2K_popup_wait .text.int.popup .falign
199212
9:
200213
{
201214
r0 = #-1
202-
jumpr r31
215+
dealloc_return
216+
}
217+
.Lpopup_wait_vmwork:
218+
{
219+
r0 = ME
220+
call H2K_vm_do_work_withlock
221+
}
222+
k0unlock
223+
{
224+
r0 = #-1
225+
dealloc_return
203226
}
204227
#undef INTNO
205228
#undef ME
@@ -230,7 +253,7 @@ FUNC_START H2K_popup_cancel .text.int.popup
230253
LLZERO = #0
231254
}
232255
{
233-
memd(INTHANDLERS+INTNO<<#2) = LLZERO
256+
memd(INTHANDLERS+INTNO<<#3) = LLZERO
234257
memw(DEST+#CONTEXT_r00) = #-1
235258
jumpr r31
236259
}

kernel/event/popup/test/test.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <checker_ready.h>
1919
#include <setjmp.h>
2020
#include <globals.h>
21+
#include <vmdefs.h>
2122

2223
void FAIL(const char *str)
2324
{
@@ -26,6 +27,14 @@ void FAIL(const char *str)
2627
exit(1);
2728
}
2829

30+
static int TH_saw_do_work;
31+
32+
int H2K_vm_do_work_withlock(H2K_thread_context *me)
33+
{
34+
TH_saw_do_work = 1;
35+
return -1;
36+
}
37+
2938
H2K_thread_context *TB_in;
3039

3140
u32_t TB_saw_dosched = 0;
@@ -312,6 +321,54 @@ int main()
312321
TH_check_old(&a);
313322
TH_clear_ready(&a);
314323
}
324+
/* Case B: VMWORK alone -- gate must fire and return -1 without INTBLOCKED */
325+
H2K_runlist_push(&a);
326+
a.status = H2K_STATUS_RUNNING;
327+
a.vmstatus = H2K_VMSTATUS_VMWORK;
328+
TH_saw_do_work = 0;
329+
if (TH_call_popup_wait(0, &a) >= 0) FAIL("B: popup_wait should fail with VMWORK set");
330+
if (!TH_saw_do_work) FAIL("B: VMWORK alone must call vm_do_work");
331+
if (a.status == H2K_STATUS_INTBLOCKED) FAIL("B: must not set INTBLOCKED");
332+
a.vmstatus = 0;
333+
H2K_runlist_remove(&a);
334+
335+
/* Case C: VMWORK|KILL -- killed thread racing into popup_wait */
336+
H2K_runlist_push(&a);
337+
a.status = H2K_STATUS_RUNNING;
338+
a.vmstatus = H2K_VMSTATUS_VMWORK | H2K_VMSTATUS_KILL;
339+
TH_saw_do_work = 0;
340+
if (TH_call_popup_wait(0, &a) >= 0) FAIL("C: popup_wait should fail with VMWORK|KILL");
341+
if (!TH_saw_do_work) FAIL("C: VMWORK|KILL must call vm_do_work");
342+
if (a.status == H2K_STATUS_INTBLOCKED) FAIL("C: must not set INTBLOCKED");
343+
a.vmstatus = 0;
344+
H2K_runlist_remove(&a);
345+
346+
/* popup_cancel: thread was waiting on interrupt i; cancel must clear the
347+
* handler slot and reset r00 to -1. */
348+
{
349+
int i;
350+
for (i = 0; i < MAX_INTERRUPTS; i++) {
351+
#if ARCHV >= 4
352+
if (i == L2_CORE_INTERRUPT) continue;
353+
#endif
354+
TH_clear_popups();
355+
H2K_runlist_push(&a);
356+
/* Simulate the state popup_wait leaves: INTBLOCKED, r00=intnum, handler set */
357+
a.status = H2K_STATUS_INTBLOCKED;
358+
a.r0100 = i;
359+
H2K_gp->inthandlers[i].handler = H2K_popup_int;
360+
H2K_gp->inthandlers[i].param = &a;
361+
362+
BKL_LOCK();
363+
H2K_popup_cancel(&a);
364+
BKL_UNLOCK();
365+
366+
if (H2K_gp->inthandlers[i].raw != 0) FAIL("cancel: handler not cleared");
367+
if (a.r00 != (u32_t)-1) FAIL("cancel: r00 not reset");
368+
H2K_runlist_remove(&a);
369+
}
370+
}
371+
315372
puts("TEST PASSED\n");
316373
return 0;
317374
}

kernel/futex/futex/test/tests/vmwork/test.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@ void FAIL(const char *str)
1818
exit(1);
1919
}
2020

21+
int n_fails = 0;
22+
void CHECK_FAIL(const char *str)
23+
{
24+
printf("FAIL: %s\n", str);
25+
n_fails++;
26+
}
27+
#define CHECK(cond, msg) do { if (!(cond)) CHECK_FAIL(msg); } while (0)
28+
2129
H2K_thread_context a;
2230

2331
int TH_saw_do_work;
2432
int TH_work_return;
2533
int TH_expected_safemem;
2634

27-
int H2K_vm_do_work(H2K_thread_context *me)
35+
int H2K_vm_do_work_withlock(H2K_thread_context *me)
2836
{
2937
if (me != &a) FAIL("bad context");
3038
TH_saw_do_work = 1;
@@ -43,28 +51,55 @@ u32_t H2K_safemem_check_and_lock(void *va, u32_t perms, pa_t *out, H2K_thread_co
4351
int main()
4452
{
4553
u32_t i = 0;
54+
55+
/* Case A: VMWORK|IE -- the original gate fires today (and post-fix).
56+
* Expected to pass both pre- and post-fix. */
4657
a.vmstatus = H2K_VMSTATUS_VMWORK | H2K_VMSTATUS_IE;
4758

4859
TH_saw_do_work = 0;
49-
if (H2K_futex_wait(&i,i,&a) != -1) FAIL("return val");
50-
if (TH_saw_do_work == 0) FAIL("no vmwork");
60+
CHECK(H2K_futex_wait(&i,i,&a) == -1, "A: wait return val");
61+
CHECK(TH_saw_do_work != 0, "A: wait did not call vm_do_work");
5162

5263
TH_saw_do_work = 0;
53-
if (H2K_futex_lock_pi(&i,&a) != -1) FAIL("pi return val");
54-
if (TH_saw_do_work == 0) FAIL("no vmwork");
64+
CHECK(H2K_futex_lock_pi(&i,&a) == -1, "A: lock_pi return val");
65+
CHECK(TH_saw_do_work != 0, "A: lock_pi did not call vm_do_work");
5566

67+
/* Case B: VMWORK alone -- the no-block rule from vmdefs.spec. Pre-fix
68+
* the gate is VMWORK&&IE so falls through to safemem (allowed via
69+
* TH_expected_safemem) and returns -1 via the safemem-fail path with
70+
* vm_do_work never called. Post-fix the gate fires on VMWORK alone. */
5671
a.vmstatus = H2K_VMSTATUS_VMWORK;
5772

5873
TH_saw_do_work = 0;
5974
TH_expected_safemem = 1;
60-
if (H2K_futex_wait(&i,i,&a) != -1) FAIL("return val");
61-
if (TH_saw_do_work != 0) FAIL("vmwork");
75+
CHECK(H2K_futex_wait(&i,i,&a) == -1, "B: wait return val");
76+
CHECK(TH_saw_do_work != 0, "B: VMWORK alone must gate (wait)");
77+
78+
TH_saw_do_work = 0;
79+
TH_expected_safemem = 1;
80+
CHECK(H2K_futex_lock_pi(&i,&a) == -1, "B: lock_pi return val");
81+
CHECK(TH_saw_do_work != 0, "B: VMWORK alone must gate (lock_pi)");
82+
83+
/* Case C: VMWORK|KILL -- the killed-thread escape from VMKILL_race.md.
84+
* kill_thread_locked sets KILL|VMWORK and the victim is on its way into
85+
* a blocking syscall. Pre-fix same failure mode as case B (gate is
86+
* VMWORK&&IE; KILL isn't IE). */
87+
a.vmstatus = H2K_VMSTATUS_VMWORK | H2K_VMSTATUS_KILL;
88+
89+
TH_saw_do_work = 0;
90+
TH_expected_safemem = 1;
91+
CHECK(H2K_futex_wait(&i,i,&a) == -1, "C: wait return val");
92+
CHECK(TH_saw_do_work != 0, "C: VMWORK|KILL must gate (wait)");
6293

6394
TH_saw_do_work = 0;
6495
TH_expected_safemem = 1;
65-
if (H2K_futex_lock_pi(&i,&a) != -1) FAIL("pi return val");
66-
if (TH_saw_do_work != 0) FAIL("vmwork");
96+
CHECK(H2K_futex_lock_pi(&i,&a) == -1, "C: lock_pi return val");
97+
CHECK(TH_saw_do_work != 0, "C: VMWORK|KILL must gate (lock_pi)");
6798

99+
if (n_fails != 0) {
100+
puts("FAIL");
101+
exit(1);
102+
}
68103
puts("TEST PASSED\n");
69104
return 0;
70105
}

kernel/futex/futex_classic/futex_classic.ref.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ s32_t H2K_futex_wait(u32_t *lock, u32_t val, H2K_thread_context *me)
2424
u32_t readval;
2525
pa_t pa;
2626
BKL_LOCK();
27-
if ((me->vmstatus & H2K_VMSTATUS_VMWORK) && (me->vmstatus & H2K_VMSTATUS_IE)) {
27+
if (me->vmstatus & H2K_VMSTATUS_VMWORK) {
28+
H2K_vm_do_work_withlock(me);
2829
BKL_UNLOCK();
29-
H2K_vm_do_work(me);
3030
return -1;
3131
}
3232
if (!H2K_safemem_check_and_lock(lock,SAFEMEM_R,&pa,me)) {

kernel/futex/futex_classic/futex_classic.spec

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ If the value has changed, the kernel returns -1.
2424

2525
If the thread went to sleep, the kernel returns 0.
2626

27+
Per the VMWORK contract (see :doc:`../../util/vmdefs/vmdefs`), -1 may
28+
also be returned spuriously when VMWORK is observed after the BKL is
29+
acquired. Callers must treat -1 as "did not block -- re-check the
30+
wait condition and retry."
31+
2732
Functionality
2833
~~~~~~~~~~~~~
2934

0 commit comments

Comments
 (0)