diff options
| -rw-r--r-- | kernel/gsp_sema_service.c | 71 | ||||
| -rw-r--r-- | tools/guest-diag/guest_pin_stream.py | 211 |
2 files changed, 250 insertions, 32 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 11549ce..2b435c4 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -22745,16 +22745,58 @@ unsigned gsp_sema_gr_setup_hold_ms; static unsigned long svc_gr_ring_tried; /* ranks that got a diagnostic line */ /* + * SESSION 153-CONTINUED-65: per-boot map of every DISTINCT beacon + * (0xbeef0xxx) GPFIFO entry the pump walks - (idx, va, len), first + * sighting order, dedup table of 512 triples (the ring re-walks entries + * every pass, so without dedup this floods). The r1124 setup-hold bound + * itself to entries 8 (0x4001004, 60216 B) and 15 (0x3200000, 153600 B); + * the client's 0x114 instruction-RAM program lives in the 0x3200000 + * family window, so binding the hold/demand/capture class to the ENTRY + * (not a size heuristic) starts from this map. Service-thread only. + */ +static void svc_beef_entry_note(u32 h, u32 idx, u64 va, u32 len) +{ + static u64 sva[512]; + static u32 sidx[512]; + static u32 slen[512]; + static u32 n; + u32 i; + + for (i = 0; i < n; i++) + if (sva[i] == va && sidx[i] == idx && slen[i] == len) + return; + if (n >= 512u) + return; + sva[n] = va; + sidx[n] = idx; + slen[n] = len; + n++; + pr_info("sema-chan: beef entry h=0x%x idx=%u va=0x%llx len=%u\n", + h, idx, va, len); +} + +/* * Is this a GR push whose readability the MME-setup capture depends on? * The client's C597 setup push is 12288 B; any GR push of that size is a * candidate. A smaller push qualifies only when it is READABLE and its * method runs hit the MME setup families (the unreadable case has no bytes * to scan, hence the length rule alone). + * + * SESSION 153-CONTINUED-65: the MME macro-upload family (0x3200000..) + * is setup-class by VA alone. The r1124 entry 15 (va=0x3200000, + * len=153600) carries the client's 0x114 instruction-RAM program - the + * guest RAM image of its window measures code=712 words with an exact-end + * stream walk - and the ubounded-tail demand must follow the ENTRY, not a + * size heuristic: the class match is what keeps the hold, the 4K-page + * demand and the strict pin anchored on the entry that actually feeds + * svc_mme_prelude_capture the 0x114 runs. */ -static bool svc_gr_setup_push(u32 len, const u8 *buf) +static bool svc_gr_setup_push(u64 va, u32 len, const u8 *buf) { u32 off = 0; + if (va >= 0x3200000u && va < 0x3300000u) + return true; if (len >= 12288u) return true; if (!buf) @@ -22788,8 +22830,14 @@ static bool svc_gr_setup_push(u32 len, const u8 *buf) * observed setup push is 60216 B (16 pages); pages 1..8 were demanded but * pages 9..15 never were, so the host's per-chunk read of the entry had no * source for the tail chunks and the whole read failed (unreadable=1, the - * hold re-armed forever). 0x10000 covers the full 60216 B push within the - * stalled-VA budget. + * hold re-armed forever). 0x10000 covered that push within the stalled-VA + * budget. + * + * SESSION 153-CONTINUED-65: the macro-upload carrier (r1124 entry 15, + * va=0x3200000) is 153600 B = 38 pages; 0x10000 demanded only 16 of them, + * so even a resolved window could never be read whole. Raise to 0x40000 + * (256 pages) - bounded by the 512-slot demand ring and the 256-entry + * stalled-VA table, both of which keep their own eviction. */ static void svc_gr_setup_hold_arm(struct svc_channel *ch, u64 va, u32 idx, u32 len) @@ -22807,7 +22855,7 @@ static void svc_gr_setup_hold_arm(struct svc_channel *ch, u64 va, u32 idx, pr_info("sema-chan: GR setup hold h=0x%x entry=%u va=0x%llx len=%u armed (demanding 4K pages; pending until the pin lands, window %u ms)\n", ch->h_object, idx, va, len, gsp_sema_gr_setup_hold_ms); } - for (poff = 0; poff < len && poff < 0x10000u; poff += SEMA_PAGE_SIZE) { + for (poff = 0; poff < len && poff < 0x40000u; poff += SEMA_PAGE_SIZE) { svc_log_stalled_va(page + poff); /* * SESSION 153-CONTINUED-56: also publish the page in the @@ -35894,6 +35942,15 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) } /* + * SESSION 153-CONTINUED-65: per-boot map of every + * distinct beacon entry (see svc_beef_entry_note) - + * the setup-hold/capture binding reads this list. + */ + if ((ch->h_object & 0xfffff000u) == 0xbeef0000u) + svc_beef_entry_note(ch->h_object, (u32)idx, + va, len); + + /* * r829: a zero-length / zero-VA entry is a bad read * (a stale or wrong translation), never work. The old * path consumed it as an empty push and advanced @@ -37317,7 +37374,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * same 4K-page demand + pending * hold as the unreadable one. */ - if (svc_gr_setup_push(len, pb)) + if (svc_gr_setup_push(va, len, pb)) svc_gr_setup_hold_arm(ch, va, (u32)idx, @@ -37327,7 +37384,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) u32 nocap_ms = gsp_sema_gr_nocap_hold_ms; - if (svc_gr_setup_push(len, pb) && + if (svc_gr_setup_push(va, len, pb) && gsp_sema_gr_setup_hold_ms > nocap_ms) nocap_ms = @@ -37627,7 +37684,7 @@ static void svc_pump_channels(struct nv_vgpu_device *vgpu) * pages and hold it pending. */ if (!ch->engine_type && - svc_gr_setup_push(len, NULL)) + svc_gr_setup_push(va, len, NULL)) svc_gr_setup_hold_arm(ch, va, (u32)idx, len); diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py index d1595b4..ddc7098 100644 --- a/tools/guest-diag/guest_pin_stream.py +++ b/tools/guest-diag/guest_pin_stream.py @@ -211,7 +211,11 @@ def read_u32(kc, va): return struct.unpack("<I", d)[0] if d else 0xFFFFFFFF -DBG_BUDGET = int(os.environ.get("STREAM_DBG_BUDGET", "600")) +DBG_BUDGET = int(os.environ.get("STREAM_DBG_BUDGET", "2400")) +# SESSION 153-CONTINUED-65: minimum seconds between full-offset sweeps of +# one strict anchor (the demand file re-sends every ~2 s; the sweep can cost +# tens of seconds - see the throttle comment at the strict processing loop). +ANCHOR_RETRY_S = float(os.environ.get("STREAM_ANCHOR_RETRY_S", "20")) _dbg_count = [0] @@ -484,6 +488,87 @@ def _stream_parse_full(buf): and code_words >= 8) +def _walk_measure(buf): + """Method-shape walk returning (end_ok, data_runs, mme_hits, code_words, + pair_runs, parsed_bytes). 'end_ok' mirrors _stream_parse_full's + exact-end + zero padding landing; 'pair_runs' counts runs addressing the + SA-pair method 0x11c (the client push signature); 'parsed_bytes' is how + far the walk got before a shape failure (or the window end).""" + off = 0 + methods = 0 + data_runs = 0 + mme_hits = 0 + code_words = 0 + pair_runs = 0 + n = len(buf) + end_ok = False + while off + 4 <= n: + hdr = struct.unpack_from('<I', buf, off)[0] + if hdr == 0: + off += 4 + continue + op = (hdr >> 29) & 7 + count = (hdr >> 16) & 0xFFF + if op in (1, 3, 5): + if not count or off + 4 + count * 4 > n: + break + data_runs += 1 + addr = (hdr & 0x1FFF) << 2 + if (0x114 <= addr <= 0x124 or addr == 0x234 or + 0x550 <= addr <= 0x568 or 0x3400 <= addr <= 0x34FC): + mme_hits += 1 + if addr == 0x114: + code_words += count + if addr == 0x11c and count >= 2: + pair_runs += 1 + off += 4 + count * 4 + elif op in (0, 2, 4, 7): + off += 4 + else: + break + methods += 1 + if methods > 8192: + break + else: + o2 = off + while o2 < n and buf[o2] == 0: + o2 += 1 + end_ok = (methods > 0 and o2 == n) + return end_ok, data_runs, mme_hits, code_words, pair_runs, off + + +def _window_accept(buf, ln): + """Acceptance for a client-mapping candidate window. + + SESSION 153-CONTINUED-65: the full-offset sweep of the client-mem + resolver needs a false-positive gate that survives pointer/event data. + The probe of the r1125 boot found a 60216-byte window of pointer junk + that walked to an exact end with one misinterpreted 0x114 run + (mme=1): the old accept (data>0, mme>0, code>=8) would have pinned it. + Large windows therefore require the client push signature - at least + two MME-setup runs or two SA-pair (0x11c) runs - in addition to the + counts; small windows keep the historical strict rule. The relaxed + path additionally accepts a window whose tail is unmapped/unwritten + (walk parses >= 0x1000) when it carries >= 64 code words (the macro + upload signature). + + Returns (ok, mode). + """ + if ln < 0x8000: + return _stream_parse_full(buf), "strict" + end_ok, data, mme, code, pairs, parsed = _walk_measure(buf) + # The client-push signature: SA-pair (0x11c) runs or 0x114 code runs. + # The r1125 probe of the live client mappings measured 10k+ pointer/float + # windows with data/mme hits but pairs=0, code=0 - MME-setup-family hits + # alone (0x124/0x34xx) are NOT a discriminator; the pair/code runs are. + sig = (pairs >= 2 or code >= 8) + if end_ok and data >= 2 and sig: + return True, "strict" + if data >= 2 and sig and parsed >= 0x1000: + return True, "relaxed" + return False, None + + def _prefix_walk_ok(buf, off): """Cheap first-page pre-filter for a strict window candidate. @@ -612,27 +697,48 @@ def resolve_va_client_mem(pid, ent_va, off, ln, out): if hi - lo < (need + 4) * 0x1000 or hi - lo > (64 << 20): continue pages = (hi - lo) >> 12 - if k is None: - # Unknown family (SESSION 153-CONTINUED-64e: the hold moved - # to the 0x3200000 window): no identity page index to key - # on, so sweep every page start of the mapping (bounded). + if k is None or k == 0: + # Unknown family, or the FAMILY-BASE page (k == 0): neither + # has an identity page index to key on, so sweep every page + # start of the mapping (bounded). SESSION 153-CONTINUED-65: + # the 0x3200000 macro-upload push's window sits at an + # arbitrary page offset inside the client's mapping (the + # r1124 power-of-two neighbourhood never tried it; the + # window content measures code=712, exact-end, under the + # strict walk). adjs = range(0, max(1, pages - need + 1)) else: - adjs = (0, 1, -1, 2, -2, 4, -4, 8, -8, 16, -16, 32, -32, - 64, -64, 128, -128, 256, -256) + # SESSION 153-CONTINUED-65: neighbourhood FIRST, then the + # FULL page sweep. The r1125 setup push (family pages k=1 + # and k=33, 60216 B) failed every power-of-two offset while + # a matching window could sit at an arbitrary mapping page; + # every candidate still passes _window_accept before any + # pin is emitted, so the widened sweep cannot pin a window + # the parse rejects. + near = [0, 1, -1, 2, -2, 4, -4, 8, -8, 16, -16, 32, -32, + 64, -64, 128, -128, 256, -256] + near_set = set(near) + rest = [a for a in range(-k, pages - need + 1 - k) + if a not in near_set] + adjs = near + rest for adj in adjs: idx = (k + adj) if k is not None else adj if idx < 0 or idx + need > pages: continue win = lo + idx * 0x1000 + off tried += 1 - if tried > (8192 if k is None else 192): + if tried > 8192: break try: first = os.pread(mfd, 0x1000 - off, win) except OSError: continue - if not first or not _mme_run_in_page(first): + if not first: + continue + # An all-zero first page is a legitimate push head (the + # method stream may start later); only a page with non-zero + # content and no MME-setup run is rejected outright. + if not _mme_run_in_page(first) and any(first): continue buf = b'' left = ln @@ -651,7 +757,10 @@ def resolve_va_client_mem(pid, ent_va, off, ln, out): buf += d a += n left -= n - if not ok or not _stream_parse_full(buf): + if not ok: + continue + acc, mode = _window_accept(buf, ln) + if not acc: continue pas = [] for j in range(need): @@ -668,11 +777,11 @@ def resolve_va_client_mem(pid, ent_va, off, ln, out): for j, pa in enumerate(pas): out.append("0x%x,0x%x,1\n" % ((ent_va & ~0xFFF) + j * 0x1000, pa)) - dbg("demand-va: 0x%x -> pa=0x%x (strict client-mem parse" + dbg("demand-va: 0x%x -> pa=0x%x (%s client-mem parse" " %u B ok pages=%u map=0x%x idx=%d)" - % (ent_va & ~0xFFF, pas[0], ln, need, lo, idx)) + % (ent_va & ~0xFFF, pas[0], mode, ln, need, lo, idx)) return True - if tried > 192: + if tried > 8192: break return False finally: @@ -719,11 +828,26 @@ def resolve_va_strict(kc, ent_va, off, ln, out, pid=None): fam = _gl_fam.get(gl_base) if fam is not None and any(a_lo == fam[0] for (a_lo, _h, _p) in areas): cands.append(fam) - for (lo, _hi, pages) in areas: - for adj in _STRICT_ADJ: - c = (lo, adj) - if c not in cands: - cands.append(c) + if k == 0: + # SESSION 153-CONTINUED-65: a family-BASE anchor (the 0x3200000 + # macro-upload push) has no identity page index to key on. The + # r1126 macro window lives in a KERNEL-mapped allocation (no + # process pagemap maps it: the whole-process reverse scan found + # none), so the kcore vmap sweep is the only resolver - and the + # power-of-two neighborhood missed the window's arbitrary page + # index. Sweep every page of every nv_vm_map_pages area; the + # _window_accept parse gates each candidate. + for (lo, _hi, pages) in areas: + for adj in range(0, max(1, pages - need + 1)): + c = (lo, adj) + if c not in cands: + cands.append(c) + else: + for (lo, _hi, pages) in areas: + for adj in _STRICT_ADJ: + c = (lo, adj) + if c not in cands: + cands.append(c) tried = 0 for (lo, adj) in cands: pages = next(p for (a_lo, _h, p) in areas if a_lo == lo) @@ -731,16 +855,26 @@ def resolve_va_strict(kc, ent_va, off, ln, out, pid=None): if idx < 0 or idx + need > pages: continue tried += 1 - if tried > 128: + if tried > (8192 if k == 0 else 128): break cpu = lo + idx * 0x1000 first = kc.read(cpu, 0x1000) - if not first or not any(first): + if not first: continue - if not _prefix_walk_ok(first, off): + # SESSION 153-CONTINUED-65: an all-zero first page is a legitimate + # push head - the r1126 macro-upload window (0x3200000, 153600 B) + # starts with a fully zero page (the method stream lands later in + # the window); the old `not any(first)` skip threw exactly that + # candidate away (the r1128 sweep tried 166 kernel-vmap windows and + # rejected the true one on this check). The prefix walk trivially + # passes an all-zero page; the full-window accept is the gate. + if any(first) and not _prefix_walk_ok(first, off): continue buf = _read_window(kc, cpu, off, ln) - if buf is None or not _stream_parse_full(buf): + if buf is None: + continue + acc, mode = _window_accept(buf, ln) + if not acc: continue pas = [] for j in range(need): @@ -755,8 +889,8 @@ def resolve_va_strict(kc, ent_va, off, ln, out, pid=None): out.append("0x%x,0x%x,1\n" % (page + j * 0x1000, vpa)) _area_off[lo] = adj _gl_fam[gl_base] = (lo, adj) - dbg("demand-va: 0x%x -> pa=0x%x (strict parse %u B ok pages=%u" - " area=0x%x adj=%+d)" % (page, pas[0], ln, need, lo, adj)) + dbg("demand-va: 0x%x -> pa=0x%x (%s parse %u B ok pages=%u" + " area=0x%x adj=%+d)" % (page, pas[0], mode, ln, need, lo, adj)) return True dbg("demand-va: 0x%x strict parse failed (%u B, %u windows tried)" % (page, ln, tried)) @@ -2095,7 +2229,8 @@ def main(): # deinit), so the set deliberately persists across client switches. pool_pages = {} pool_reemit_next = 0.0 - _want_state = {"mtime": None, "done": set(), "strict_pending": {}} + _want_state = {"mtime": None, "done": set(), "strict_pending": {}, + "strict_try": {}} loop = 0 while True: pid = cdt_pid() @@ -2243,6 +2378,18 @@ def main(): epage = ev & ~0xFFF if epage in _want_state["done"]: continue + # SESSION 153-CONTINUED-65: throttle anchor + # re-resolution. The full-offset sweep can read + # thousands of windows (tens of seconds of CPU); + # the demand file re-sends the anchor every ~2 s, + # so without this throttle the scanner starves the + # client (both measured at ~91% CPU at r1125). One + # attempt per anchor per window is enough - content + # that lands late is picked up on the next window. + lt = _want_state["strict_try"].get(epage, 0.0) + if now_d - lt < ANCHOR_RETRY_S: + continue + _want_state["strict_try"][epage] = now_d got = [] try: ok = resolve_va_strict(kc, ev, eoff, eln, got, @@ -2250,6 +2397,20 @@ def main(): except Exception: ok = False got = [] + if not ok and eoff: + # The demand's off is the entry va's in-page + # offset; the push content can be page-aligned + # instead (r1123 vs r1125 parse behaviour). A + # second try at page alignment is cheap next to + # the completed sweep and the same pins result. + got2 = [] + try: + ok = resolve_va_strict(kc, ev & ~0xFFF, 0, + eln, got2, pid) + except Exception: + ok = False + if ok: + got = got2 if ok and got: lines.extend(got) for g in got: |
