Conversation
reusemappingdbUnmap() walks virtualMap from range.start and unmaps every tracked entry contained in the requested range, then releases whatever untracked VA is left over in the trailing "overhang" block. When the walk stopped on an entry not contained in the unmap range, the function took one of two different exits depending on whether any entry had already been processed: break on the first iteration, plain return on every later one. The return path skips the overhang unmap, so any untracked VA between the last removed entry and the end of the requested range is never released. That VA stays reserved for the lifetime of the BAR1 address space. Untracked mappings are routinely produced by reusemappingdbMap(): when a cached entry intersects the request without matching it exactly, bAddToMap is cleared, fresh mappings are allocated, and the entries are PORT_FREE()d without being inserted into virtualMap. The overhang unmap in reusemappingdbUnmap() is the only path that reclaims them. Use a single exit that records how far the overhang may extend. Clamping to the offending entry's start also fixes a latent over-unmap on the existing break path, where an entry straddling the end of the requested range could be unmapped while references to it remained, and makes the trailing size computation unable to underflow.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
reusemappingdbUnmap()can return without releasing untracked BAR1 VA, leaking addressspace on every affected unmap. On a GPU with a small fixed BAR1 the aperture eventually
fragments to the point where
dmaAllocMapping_GM107cannot place a new mapping, whichsurfaces as
NV_ERR_NO_MEMORYatmapping_reuse.c:274, then Xid 31, then a displayengine hang.
The leak is invisible in
nvidia-smi, whose BAR1 "Used" counts allocated memory ratherthan reserved address space — which is why these failures appear at ~40% reported BAR1
usage and why closing GPU clients does not recover.
Where the untracked VA comes from
reusemappingdbMap(), in the!bNoReuse && bSingleRangepath: when a cached entryintersects the requested range without being an exact match,
bAddToMapis cleared butthe function still falls through to
pMapCb()and allocates new mappings. In the appendloop those entries take the
elsebranch and arePORT_FREE()d without ever beinginserted into
virtualMapor the physical map.Those mappings exist in hardware and are tracked nowhere. The overhang unmap at the end of
reusemappingdbUnmap()is the only code that can reclaim them.Why the overhang unmap is skipped
The walk had two different exits for the same condition:
Both mean "this entry is not ours, stop walking". On the
returnpath, everything in[curOffset, mrangeLimit(range))— untracked VA inside the caller's own range — is neverhanded to
pUnmapCband stays reserved for the life of the address space.The change
Replace
bFirstRangewith anunmapLimitthat records how far the trailing unmap mayextend, and use one exit.
Beyond fixing the leak this addresses two latent issues on the path that already used
break:references to it remained;
unmapLimitis clamped to that entry's start, so the unmapnever reaches into it.
mrangeLimit(range) - curOffsetbehind a!=guard;unmapLimit > curOffsetcannot underflow ifcurOffsetever passes the limit.Reproduction and measurement
RTX 2080 (TU104), BAR1 fixed at 256 MiB — Turing cannot resize it, so the standard
"enable Resizable BAR" workaround does not apply. Driver 610.57.04 open modules, Linux
7.2.3, KDE/Wayland.
Stock 610.57.04, one 1h53m session:
The first failures appeared at a BAR1 high-water mark of 133 MiB (52%) and the session
ended in a display hang. Sampling every 5 minutes over the preceding six days recorded 23
such incidents, with VA failures accumulating from roughly 40% reported BAR1 usage.
With the unmap fix applied, same workload and same desktop session:
The patched module sustained 50 MiB beyond the level at which the stock module was already
failing, with no VA allocation failures.
One disclosure: the measured build carried only the minimal
return→breakchange. Inthe leaking case this PR's form is identical to what was tested; the
unmapLimitclamponly narrows the trailing unmap, and is there to keep the straddling-entry case safe.
Related
__nv_drm_gem_nvkms_mapcomposes a mapping that spans BAR1→BAR3, causingmapping_reuse.c:273 NV_ERR_NO_MEMORYandkrcWatchdogGPU lock — driver 595.71.05 (open kernel modules), Resizable BAR disabled #1132 — GB205, Resizable BAR disabled, sameNV_ERR_NO_MEMORYatmapping_reuse.cfollowed by a GPU lock.
err_unmapcleanup path ofreusemappingdbMap(); thisis a separate path in the same file.
The
bAddToMapbehaviour described above is left alone here — reusing partiallyoverlapping mappings likely needs a design decision rather than a local fix, and this
change makes the VA it produces reclaimable. Happy to follow up on it separately.