diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-04 17:40:47 -0500 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-11-04 17:40:47 -0500 |
| commit | b6c8e74164716acdcdb3d34e05966deec4f9f35b (patch) | |
| tree | ec10cd351feca570a7b256a3279f78f566c57f0e /kernel/gsp_sema_service.c | |
| parent | 70d8f709224ed4d2064bff8d176db3dbdb74fce5 (diff) | |
| download | open-iov-b6c8e74164716acdcdb3d34e05966deec4f9f35b.tar.gz open-iov-b6c8e74164716acdcdb3d34e05966deec4f9f35b.zip | |
vgsp-diag: whole-vidmem page-hash bracket - sema_gl_vidmem_bracket hashes every 4KB page of the BAR1 shadow before the GR publish and diffs after the output-retirement wait, reporting any changed page outside the sampled output set; vidmem_hash_snap/vidmem_hash_diff manual module params; default 0 is a strict no-op
Diffstat (limited to 'kernel/gsp_sema_service.c')
| -rw-r--r-- | kernel/gsp_sema_service.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/kernel/gsp_sema_service.c b/kernel/gsp_sema_service.c index 64139f0..498198d 100644 --- a/kernel/gsp_sema_service.c +++ b/kernel/gsp_sema_service.c @@ -41074,6 +41074,138 @@ int gsp_sema_service_find_vidmem(u64 pattern, u64 max_hits) } /* + * Whole-vidmem page-hash bracket. Snapshot a hash of every 4 KB page of the + * first open device's BAR1 shadow, then re-hash later and report the pages + * whose content changed. This proves whether an engine wrote any page of the + * emulated vidmem, including pages the output copy-out does not sample. + * The hash array is allocated once per shadow size and kept until the + * service stops. + */ +static DEFINE_MUTEX(gsp_sema_vidmem_hash_lock); +static u64 *gsp_sema_vidmem_hash; +static unsigned int gsp_sema_vidmem_hash_pages; + +static inline u64 gsp_sema_vidmem_fnv1a64(const u8 *p, size_t len) +{ + u64 h = 0xcbf29ce484222325ULL; + size_t i; + + for (i = 0; i < len; i++) { + h ^= p[i]; + h *= 0x100000001b3ULL; + } + return h; +} + +/* + * Resolve the shadow to bracket: the first mdev a guest has open, the same + * resolution the vidmem debug helpers use. Only whole 4 KB pages inside + * bars[1] are hashed. + */ +static int gsp_sema_vidmem_hash_target(u8 **base, u64 *size) +{ + struct nv_vgpu_device *vgpu = NULL; + struct nv_vgpu_device *d; + + mutex_lock(&nv_vgpu_driver.lock); + list_for_each_entry(d, &nv_vgpu_driver.devices, list) { + if (atomic_read(&d->vfio_dev_open_count) > 0) { + vgpu = d; + break; + } + } + mutex_unlock(&nv_vgpu_driver.lock); + if (!vgpu || !vgpu->bars[1].vaddr || !vgpu->bars[1].size) + return -ENODEV; + *base = vgpu->bars[1].vaddr; + *size = vgpu->bars[1].size; + return 0; +} + +int gsp_sema_service_vidmem_hash_snapshot(void) +{ + u8 *base; + u64 size; + unsigned int pages, i; + int ret; + + ret = gsp_sema_vidmem_hash_target(&base, &size); + if (ret) + return ret; + pages = (unsigned int)(size >> SEMA_PAGE_SHIFT); + if (!pages) + return -ENODEV; + + mutex_lock(&gsp_sema_vidmem_hash_lock); + if (!gsp_sema_vidmem_hash || gsp_sema_vidmem_hash_pages != pages) { + u64 *hashes; + + hashes = vmalloc((size_t)pages * sizeof(*hashes)); + if (!hashes) { + ret = -ENOMEM; + goto out_unlock; + } + vfree(gsp_sema_vidmem_hash); + gsp_sema_vidmem_hash = hashes; + gsp_sema_vidmem_hash_pages = pages; + } + for (i = 0; i < pages; i++) + gsp_sema_vidmem_hash[i] = gsp_sema_vidmem_fnv1a64( + base + ((u64)i << SEMA_PAGE_SHIFT), SEMA_PAGE_SIZE); + ret = 0; +out_unlock: + mutex_unlock(&gsp_sema_vidmem_hash_lock); + return ret; +} + +int gsp_sema_service_vidmem_hash_diff(const char *tag, unsigned int max_pages) +{ + static DEFINE_RATELIMIT_STATE(detail_rs, 5 * HZ, 4); + u8 *base; + u64 size; + unsigned int pages, i, changed = 0; + const char *name = tag ? tag : "diff"; + bool detail; + int ret; + + ret = gsp_sema_vidmem_hash_target(&base, &size); + if (ret) + return ret; + pages = (unsigned int)(size >> SEMA_PAGE_SHIFT); + if (!pages) + return -ENODEV; + if (max_pages > 256) + max_pages = 256; + + mutex_lock(&gsp_sema_vidmem_hash_lock); + if (!gsp_sema_vidmem_hash || gsp_sema_vidmem_hash_pages != pages) { + mutex_unlock(&gsp_sema_vidmem_hash_lock); + return -EINVAL; + } + detail = __ratelimit(&detail_rs); + for (i = 0; i < pages; i++) { + const u8 *p = base + ((u64)i << SEMA_PAGE_SHIFT); + u64 h = gsp_sema_vidmem_fnv1a64(p, SEMA_PAGE_SIZE); + + if (h == gsp_sema_vidmem_hash[i]) + continue; + changed++; + if (detail && changed <= max_pages) { + u64 w0; + + memcpy(&w0, p, sizeof(w0)); + pr_info("vgsp-gr: VIDMEM-BRACKET page=0x%x off=0x%llx w0=0x%016llx hash=0x%016llx\n", + i, (unsigned long long)i << SEMA_PAGE_SHIFT, + w0, h); + } + } + mutex_unlock(&gsp_sema_vidmem_hash_lock); + pr_info_ratelimited("vgsp-gr: VIDMEM-BRACKET %s changed=%u/%u\n", + name, changed, pages); + return (int)changed; +} + +/* * Register a channel's GPU completion counter (guest PA of the u32 the GPU * would advance as pushes complete). Called from the sema_counter debug * parameter; plain state update, so it runs in the caller's context. @@ -41813,6 +41945,12 @@ void gsp_sema_service_stop(void) svc_setup_demand_debugfs_fini(); /* A fresh session must not inherit shadowed bytes from the last one. */ svc_vas_free_all(); + /* Drop the bracket hash array; a later session allocates it again. */ + mutex_lock(&gsp_sema_vidmem_hash_lock); + vfree(gsp_sema_vidmem_hash); + gsp_sema_vidmem_hash = NULL; + gsp_sema_vidmem_hash_pages = 0; + mutex_unlock(&gsp_sema_vidmem_hash_lock); } /* |
