Skip to content

Commit

Permalink
tmpfs: limit regularFile.Translate() fill range
Browse files Browse the repository at this point in the history
When e.g. an application thread takes a page fault on an mmapped file, MM calls
`memmap.Mappable.Translate()` to obtain the corresponding host FD range that
should be mapped into the application's address space. It passes both the range
that *must* be mapped (e.g. the faulting page) as `required`, and the maximum
range that *may* be mapped (the previously-unfaulted part of the corresponding
VMA) as `optional`, such that file implementations can map more than `required`
to avoid future page faults.

Prior to this CL, `tmpfs.regularFile.Translate()` always returned translations
up to `optional`, under the assumption that allocating larger ranges from
`pgalloc.MemoryFile` has negligible incremental cost. This behavior dates to
the introduction of `memmap.Mappable.Translate()` (cl/182882705) and thus
predates the implementation of tmpfs size limits (cl/442686814). Now that the
latter exists, unconditionally translating - and therefore allocating pages -
up to `optional` can result in hitting tmpfs size limits prematurely.

Thus: Constrain optional translations returned by
`tmpfs.regularFile.Translate()`, applying the same logic as
`gofer.maxFillRange()`.
PiperOrigin-RevId: 712742057
  • Loading branch information
nixprime authored and gvisor-bot committed Jan 7, 2025
1 parent b94ab73 commit 6b70825
Showing 1 changed file with 13 additions and 0 deletions.
13 changes: 13 additions & 0 deletions pkg/sentry/fsimpl/tmpfs/regular_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,19 @@ func (rf *regularFile) Translate(ctx context.Context, required, optional memmap.
if optional.End > pgend {
optional.End = pgend
}
// Constrain allocation to at most maxOptionalBytes or required.Length(),
// whichever is greater.
const maxOptionalBytes = 64 << 10 // 64 KB, arbitrarily matches Linux's default fault_around_pages
if required.Length() >= maxOptionalBytes {
optional = required
} else {
if optional.Length() > maxOptionalBytes {
optional.Start = required.Start
if optional.Length() > maxOptionalBytes {
optional.End = optional.Start + maxOptionalBytes
}
}
}
pagesToFill := rf.data.PagesToFill(required, optional)
if !rf.inode.fs.accountPages(pagesToFill) {
// If we can not accommodate pagesToFill pages, then retry with just
Expand Down

0 comments on commit 6b70825

Please sign in to comment.