diff options
Diffstat (limited to 'kernel/gsp_sema_service.c')
| -rw-r--r-- | kernel/gsp_sema_service.c | 132 |
1 files changed, 116 insertions, 16 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 7f61c57..e156339 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -1064,6 +1064,7 @@ struct svc_scrub { bool pa_ok; /* scanner registered fin/hsem PAs */ u64 fin_pa; /* finish payload word (u32) */ u64 hsem_pa; /* channel host semaphore word (u32) */ + unsigned long pa_at; /* last successful walk re-resolution */ bool seeded; u32 put_last; u64 submitted; /* blocks submitted since seeding */ @@ -2374,6 +2375,14 @@ void gsp_sema_service_channel_add(const struct sema_channel_desc *desc) s->gpfifo_va != desc->gpfifo_va) { s->seeded = false; s->submitted = 0; + /* + * r1668: a re-registered scrubber may reuse the + * same GPU VA with a new backing page. Force the + * finish-word resolution to run again instead of + * authoring the previous allocation's page. + */ + s->pa_ok = false; + s->authored_gen = 0; } s->session_gen = svc_sess_gen; s->userd_ok = true; @@ -45302,6 +45311,62 @@ int gsp_sema_service_scrubbuf_add(u64 fin_pa, u64 hsem_pa) } /* + * Resolve a scrubber channel-buffer VA to guest RAM, preferring the RM + * kernel VAS (h=0xc, the address space the guest's own CPU mapping of the + * channel buffer lives in). Several VA spaces can map the same VA: the + * client's VAS (e.g. beef0300) maps it to its own emulated vidmem object, + * and the kernel VAS maps the real channel buffer in RAM. Picking the first + * walk success (newest VAS first) therefore picked a vidmem page and the + * authored finish word never reached the guest. Returns true on a RAM hit. + */ +static bool svc_scrub_resolve_va(struct nv_vgpu_device *vgpu, u64 va, + u64 *pa, int *ap, u32 *hvas) +{ + struct svc_vaspace local[SVC_MAX_VASPACES]; + unsigned long flags; + u64 best_pa = 0; + int best_ap = SVC_APERTURE_SYS; + u32 best_h = 0; + bool found = false; + int i, n = 0; + + spin_lock_irqsave(&svc.vas_lock, flags); + for (i = 0; i < SVC_MAX_VASPACES; i++) + if (svc.vas[i].valid) + local[n++] = svc.vas[i]; + spin_unlock_irqrestore(&svc.vas_lock, flags); + + for (i = 0; i < n; i++) { + u64 wpa = 0; + int wap = SVC_APERTURE_SYS; + + if (svc_gmmu_walk(vgpu, local[i].pdb, local[i].entries, va, + local[i].ap, &wpa, &wap, false) || !wpa) + continue; + if (svc_ap_of(wap) != SVC_APERTURE_SYS) + continue; + if (local[i].h_vaspace == 0xc) { + *pa = wpa; + *ap = wap; + *hvas = local[i].h_vaspace; + return true; + } + if (!found) { + best_pa = wpa; + best_ap = wap; + best_h = local[i].h_vaspace; + found = true; + } + } + if (found) { + *pa = best_pa; + *ap = best_ap; + *hvas = best_h; + } + return found; +} + +/* * Author the CeUtils scrubber channel's progress once per service pass. * * The physical CE would advance, per completed scrub block, the channel's @@ -45340,10 +45405,11 @@ static void svc_scrub_pass(struct nv_vgpu_device *vgpu) * entries*8 and fin = hsem + 4. Resolve them through the learned map * or the VAS walk and register in place. */ - if (s->userd_ok && !s->pa_ok && s->gpfifo_va && s->entries) { + if (s->userd_ok && s->gpfifo_va && s->entries) { u64 hva = s->gpfifo_va + (u64)s->entries * 8; u64 pa = 0; int ap = SVC_APERTURE_SYS; + u32 hv = 0; /* * r1667: resolve through the GPU page tables FIRST. The learned @@ -45353,23 +45419,37 @@ static void svc_scrub_pass(struct nv_vgpu_device *vgpu) * old page from a previous allocation and the scrub wait never * completed). Fall back to the pair map only when no walkable VAS * maps the VA. + * + * r1668: re-resolve on EVERY pass, not just the first. The guest + * re-creates the RM kernel VAS (h=0xc) and re-maps the scrubber + * buffer under the same GPU VA when its RM resets (observed after + * the first GZDoom run: the same gpfifo VA 0x120064000 moved from + * PA 0x127e6a000 to 0x1122e9000). A once-only resolution then + * authored a page the guest no longer polls and the scrub wait + * never completed, which in turn kept the guest's RM lock held and + * froze the status-queue consumer (the pump's event posts then + * blocked for 200 ms each, starving this very pass). */ - if (svc_walk_vaspaces(vgpu, hva, &pa, &ap) || !pa) { - if (svc_lookup(vgpu, hva, &pa, &ap) || !pa) - pa = 0; - } - /* The scrub words are guest RAM; an FB translation is not. */ - if (pa && svc_ap_of(ap) != SVC_APERTURE_SYS) - pa = 0; - if (pa) { - s->hsem_pa = pa; - s->fin_pa = pa + 4; + if (svc_scrub_resolve_va(vgpu, hva, &pa, &ap, &hv) && pa) { + if (!s->pa_ok || pa != s->hsem_pa) { + if (s->log_budget < 24) { + s->log_budget++; + pr_info("sema-svc: scrub geometry hsem_va=0x%llx fin_va=0x%llx -> hsem_pa=0x%llx fin_pa=0x%llx vas=0x%x\n", + hva, hva + 4, pa, pa + 4, hv); + } + s->hsem_pa = pa; + s->fin_pa = pa + 4; + } s->pa_ok = true; - if (s->log_budget < 16) { - s->log_budget++; - pr_info("sema-svc: scrub geometry hsem_va=0x%llx fin_va=0x%llx -> hsem_pa=0x%llx fin_pa=0x%llx\n", - hva, hva + 4, s->hsem_pa, - s->fin_pa); + s->pa_at = jiffies; + } else if (!s->pa_ok) { + /* Last resort: the learned pair map, RAM only. */ + if (!svc_lookup(vgpu, hva, &pa, &ap) && pa && + svc_ap_of(ap) == SVC_APERTURE_SYS) { + s->hsem_pa = pa; + s->fin_pa = pa + 4; + s->pa_ok = true; + s->pa_at = jiffies; } } } @@ -45453,6 +45533,26 @@ static void svc_scrub_pass(struct nv_vgpu_device *vgpu) } /* + * r1668: run the scrub step from inside a blocked event post. + * + * When the guest's status-queue consumer is frozen (typically because the + * X server's PMA scrub wait holds the RM lock the event callback needs), + * the pump's critical-event posts wait out their full ring-full budget + * (200 ms each) before dropping. A pass can then take many seconds, and + * the next pass's scrub step - the only thing that can release the scrub + * wait - is starved. This hook is called from the send path's wait loop, + * on the service thread itself, so it runs the same state machine without + * any concurrency. It is a no-op from any other thread (RPC replies run + * on the GSP emulation thread). + */ +void gsp_sema_service_scrub_pump(struct nv_vgpu_device *vgpu) +{ + if (!svc.task || !vgpu || current != svc.task) + return; + svc_scrub_pass(vgpu); +} + +/* * r539: init-stall watchdog. The r530-r538 stall boots all share one shape: * the pump keeps looping (rebuilds, pending retries) but the entry loop stops * consuming anything, a client spins at DEVICE (uvm channel_pool_add -> |
