From bca981236a29b642e05d60f0f7e376b50a857a63 Mon Sep 17 00:00:00 2001 From: Dawei Shen Date: Thu, 10 Oct 2024 00:33:46 +0800 Subject: [PATCH] sentry: use read(2) host syscall to perform read on disk-backed MemoryFiles The mf.MapInternal()+safemem.CopySeq() approach used right now incurs a lot of page faults without page population. Page-by-page faults incurs a lot of context switching. This commits uses read(2) host syscall instead, which makes one context switch and faults all the pages that are touched during the read. Signed-off-by: Dawei Shen --- pkg/sentry/fsimpl/tmpfs/regular_file.go | 31 +++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/pkg/sentry/fsimpl/tmpfs/regular_file.go b/pkg/sentry/fsimpl/tmpfs/regular_file.go index 24c46fbaf9..f80551017f 100644 --- a/pkg/sentry/fsimpl/tmpfs/regular_file.go +++ b/pkg/sentry/fsimpl/tmpfs/regular_file.go @@ -655,14 +655,8 @@ func (rw *regularFileReadWriter) ReadToBlocks(dsts safemem.BlockSeq) (uint64, er mr := memmap.MappableRange{uint64(rw.off), uint64(end)} switch { case seg.Ok(): - // Get internal mappings. - ims, err := rw.file.inode.fs.mf.MapInternal(seg.FileRangeOf(seg.Range().Intersect(mr)), hostarch.Read) - if err != nil { - return done, err - } + n, err := rw.readFromMF(seg.FileRangeOf(seg.Range().Intersect(mr)), dsts) - // Copy from internal mappings. - n, err := safemem.CopySeq(dsts, ims) done += n rw.off += uint64(n) dsts = dsts.DropFirst64(n) @@ -812,6 +806,29 @@ exitLoop: return done, retErr } +func (rw *regularFileReadWriter) readFromMF(fr memmap.FileRange, dsts safemem.BlockSeq) (uint64, error) { + if rw.file.inode.fs.mf.IsDiskBacked() { + // Disk-backed files are not prepopulated. The safemem.CopySeq() approach + // used below incurs a lot of page faults without page prepopulation, which + // causes a lot of context switching. Use read(2) host syscall instead, + // which makes one context switch and faults all the pages that are touched + // during the read. + return hostfd.Preadv2( + int32(rw.file.inode.fs.mf.FD()), // fd + dsts.TakeFirst64(fr.Length()), // dsts + int64(fr.Start), // offset + 0, // flags + ) + } + // Get internal mappings. + ims, err := rw.file.inode.fs.mf.MapInternal(fr, hostarch.Read) + if err != nil { + return 0, err + } + // Copy from internal mappings. + return safemem.CopySeq(dsts, ims) +} + func (rw *regularFileReadWriter) writeToMF(fr memmap.FileRange, srcs safemem.BlockSeq) (uint64, error) { if rw.file.inode.fs.mf.IsDiskBacked() { // Disk-backed files are not prepopulated. The safemem.CopySeq() approach