LATX: clear stale jump caches after invalid-TB SIGILL - #406
Conversation
LaurenIsACoder
left a comment
There was a problem hiding this comment.
我对这里的触发链还有疑问。现有 do_tb_phys_invalidate() 在普通 jump cache 仍指向失效 TB 时,已经会同时清理 fast jump cache:
if (qatomic_read(&cpu->tb_jmp_cache[h]) == tb) {
latx_fast_jmp_cache_clear(cpu, h);
qatomic_set(&cpu->tb_jmp_cache[h], NULL);
}执行线程如果在清理前已经把旧 tc.ptr 加载到寄存器中,之后再进入一次 0x88888888 是可以理解的;但 SIGILL handler 会通过 context_switch_native_to_bt_ret_0 返回 dispatcher,而旧 TB 已经设置 CF_INVALID 并从 QHT 删除,正常情况下后续 lookup 应该重新查找或生成 TB,不应继续反复进入同一个旧块。
麻烦补充一下修复前 SIGILL 现场的实际 cache 状态,重点确认:
current_tb
current_tb->pc
current_tb->tc.ptr
hash
cpu->tb_jmp_cache[hash]
fast_jmp_cache[hash].pc
fast_jmp_cache[hash].ptr
特别想确认两点:
- 失效时是否确实出现了
cpu->tb_jmp_cache[hash] != current_tb,但 fast cache 仍然指向current_tb; - 是否确认是同一个
current_tb连续触发 SIGILL,以及第一次退出到 dispatcher 后为什么没有通过CF_INVALID检查和 QHT miss 恢复。
如果当时没有保留这些信息,可以加一版临时日志再跑原测试,至少把连续两次 SIGILL 的 current_tb、regular cache 和 fast cache 内容记录下来。这样才能判断是 invalidation 漏清 fast cache,还是已经加载的旧跳转、direct link/JRRA 等其他入口。
Concurrent JIT code invalidation can unlink a translation block while another thread is executing its two-word jump slot. Replacing the pair with one 64-bit store can combine an old pcaddu18i with a new jirl and send execution beyond the translated code. Publish an in-range B only after placing a SIGILL sentinel in the second word, and restore the original pair while the old B still skips that word. Retry a transient jump-slot SIGILL from the first word. Do not directly chain targets outside the B range, where publishing a two-word far jump cannot be made safe with this slot layout. When an invalid-TB sentinel reaches the signal handler, also remove matching regular and fast jump-cache entries before returning to the dispatcher. This prevents the dispatcher from repeatedly selecting the same invalid block and adds no checks or instructions to translated blocks. Tested with 20 consecutive direct runs of the MaxMetaspaceSize action and four complete runs of jdk/jfr/event/runtime/TestMetaspaceAllocationFailure.java on LoongArch, with AOT disabled in source and option_fork_unlink enabled. Signed-off-by: liuchaoyi <liuchaoyi@loongson.cn>
这个质疑是对的。之前的现场没有保存你列出的 regular jump cache 和 fast jump cache 内容,因此无法确认:
|
Summary / 变更说明
修复 LATX 在 JIT 代码失效期间可能反复执行无效翻译块、导致程序卡住的问题。
LATX 使用
0x88888888非法指令标记已失效的翻译块。线程执行该指令产生SIGILL后,原处理逻辑可能没有彻底清除当前 CPU 中指向旧翻译块的缓存,导致调度器再次进入同一个旧块并反复触发SIGILL。本次修改在处理该
SIGILL时:修改只作用于无效翻译块的异常处理,不在正常翻译块中增加检查指令。
Validation / 验证
在 LoongArch 主机上关闭 AOT 验证,设置
LATX_AOT=0。ninja -C build64:编译通过。单独运行
TestMetaspaceAllocationFailure.java中使用CompressedClassSpaceSize=10M的测试动作 10 次:连续运行以下完整测试 3 次:
TEST RESULT: Passed. Skipped: jtreg.SkippedException: Exceeded MAX_ITERATIONS of 100。SkippedException是测试达到 100 次循环上限后的预期结果。Checklist / 检查项
CONTRIBUTING.md. / 我已阅读CONTRIBUTING.md。git commit -s). /每个提交都包含 DCO 签署(
git commit -s)。are not applicable. /
我已提供相关构建或测试结果,或说明了不适用的原因。