Re: [linux-next:master] [mm] 7b32f64bc5: pts.svt-av1.Preset13.Bosphorus4K.frames_per_second 45.8% regression
Bo Zhang <[email protected]> Tue, 28 Jul 2026 13:25:26 +0800
| Newsgroups | dev.linux.lists.oe-lkp,org.kernel.vger.linux-fsdevel,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
On Thu, Jul 23, 2026 at 01:34:38PM -0700, Frederick Mayle wrote:
> I maybe missing something subtle, but, I think you've changed it from "don't
> read beyond the VMA" to "if there is a chance we could read beyond the VMA,
> don't read beyond the VMA", which seems like a more complex expression of the
> same behavior.
Hi Frederick,
I looked into the SVT-AV1 benchmark that triggered this regression and
found why unconditional _max_index causes the 870% major fault increase.
SVT-AV1 uses per-frame mmap on Linux to read its input video file.
From Source/App/EncApp/EbAppProcessCmd.c:
void read_input_frames(EbConfig *config, ...) {
if (config->mmap.enable) {
int64_t offset = get_mmap_offset(config, read_size);
input_ptr->luma = svt_mmap(&config->mmap, offset, luma_read_size);
offset += luma_read_size;
input_ptr->cb = svt_mmap(&config->mmap, offset, chroma_read_size);
offset += chroma_read_size;
input_ptr->cr = svt_mmap(&config->mmap, offset, chroma_read_size);
}
...
svt_av1_enc_send_picture(component_handle, header_ptr);
if (config->mmap.enable)
release_memory_mapped_file(config, is_16bit, header_ptr); /* munmap */
}
Where svt_mmap() is:
void *svt_mmap(MemMapFile *h, int64_t offset, int64_t size) {
uint8_t *base = mmap(NULL, size, PROT_READ, MAP_PRIVATE, h->fd, offset);
return base + align;
}
So for Bosphorus 4K (3840x2160 YUV420 8bit), each frame creates 3
separate mmap regions on the SAME file, the layout is:
Frame N: [luma 8MB][cb 2MB][cr 2MB] (file offsets contiguous)
Frame N+1: [luma 8MB][cb 2MB][cr 2MB] (immediately follows)
In page counts (ra_pages=32 assumed):
luma VMA: ~2025 pages
chroma VMA: ~506 pages each
These become 3 independent VMAs per frame, but all map contiguous
regions of the same file. The encoder accesses each sequentially,
then munmaps before processing the next frame.
Here is the problem with unconditional _max_index:
1. Encoder accesses luma VMA sequentially, readahead ramps up.
Last async readahead window: ra->start=1993, ra->size=32.
2. Fault at page ~1993 hits PG_readahead, triggers async readahead:
ra->start += ra->size; Then ra->start = 2025
3. In page_cache_ra_order():
limit = min(file_end, _max_index); so limit = 2024
while (index <= limit) ... so 2025 > 2024, reads NOTHING
4. Pages 2025+ (which are the cb plane data) are never prefetched.
When encoder accesses the cb mmap, it takes a major fault.
5. Same thing happens at cb->cr boundary, and at frame N->N+1.
Result: 3 major faults per frame × 120 frames = hundreds of
extra major faults, explaining the 870% increase.
Without _max_index (original kernel):
At step 2, readahead proceeds past page 2025, prefetching the cb
data into page cache. When the cb mmap is created and accessed,
the pages are already cached, so minor fault happens only.
With my conditional patch:
At step 2, vmf->pgoff=1993, vma_pages_left = 2025-1993 = 32.
The condition "32 < 32" is false, so _max_index stays ULONG_MAX.
Readahead proceeds normally, prefetching cb data.
Only when vma_pages_left < ra_pages (for example, the last few pages
of the VMA), we will set the limit, at that point the readahead window
has already pushed useful data into the page cache.
For workloads that mmap sequential regions of the same file (SVT-AV1
frames, ELF segment loading, mprotect-split VMAs), the data beyond the
VMA boundary is useful and will be accessed soon via a different mapping.
Blocking readahead at the VMA boundary in these cases hurts performance.
The conditional check preserves the benefit of VMA-bounded readahead
(avoiding useless prefetch for small partial mappings) while allowing
cross-VMA prefetch when the access is clearly sequential and far from
the VMA end.
Thanks to Oliver for reporting this regression and providing the test
case details, looking into the actual SVT-AV1 code made the mechanism
much clearer.
Bo