@@ -16,8 +16,10 @@ package mm
1616
1717import (
1818 "bytes"
19+ "io"
1920 "os"
2021 "testing"
22+ "time"
2123
2224 "golang.org/x/sys/unix"
2325 "gvisor.dev/gvisor/pkg/context"
@@ -32,6 +34,8 @@ import (
3234 "gvisor.dev/gvisor/pkg/sentry/memmap"
3335 "gvisor.dev/gvisor/pkg/sentry/pgalloc"
3436 "gvisor.dev/gvisor/pkg/sentry/platform"
37+ "gvisor.dev/gvisor/pkg/sentry/state/stateio"
38+ "gvisor.dev/gvisor/pkg/sentry/usage"
3539 "gvisor.dev/gvisor/pkg/usermem"
3640)
3741
@@ -1080,3 +1084,124 @@ func TestSealedMapping(t *testing.T) {
10801084 defer mm2 .DecUsers (ctx )
10811085 wantError (t , linuxerr .EPERM , "child MUnmap(sealed)" , mm2 .MUnmap (ctx , sealedAddr , pageSize ))
10821086}
1087+
1088+ // gatedReaderAt blocks reads past offset from until `gate` is closed.
1089+ type gatedReaderAt struct {
1090+ io.ReaderAt
1091+ from int64
1092+ gate chan struct {}
1093+ }
1094+
1095+ // ReadAt implements `io.ReaderAt.ReadAt`.
1096+ func (g * gatedReaderAt ) ReadAt (p []byte , off int64 ) (int , error ) {
1097+ if off + int64 (len (p )) > g .from {
1098+ <- g .gate
1099+ }
1100+ return g .ReaderAt .ReadAt (p , off )
1101+ }
1102+
1103+ func newTestMemoryFile (t * testing.T ) * pgalloc.MemoryFile {
1104+ memfd , err := memutil .CreateMemFD ("mm-test-memory" , 0 )
1105+ if err != nil {
1106+ t .Fatalf ("CreateMemFD: got err %v, want nil" , err )
1107+ }
1108+ memfile := os .NewFile (uintptr (memfd ), "mm-test-memory" )
1109+ mf , err := pgalloc .NewMemoryFile (memfile , pgalloc.MemoryFileOpts {
1110+ DisableMemoryAccounting : true ,
1111+ })
1112+ if err != nil {
1113+ memfile .Close ()
1114+ t .Fatalf ("NewMemoryFile: got err %v, want nil" , err )
1115+ }
1116+ t .Cleanup (mf .Destroy )
1117+ return mf
1118+ }
1119+
1120+ // TestIODuringAsyncPageLoad tests that I/O to part of a pma whose pages are
1121+ // being loaded asynchronously does not wait for the rest of the pma to load.
1122+ func TestIODuringAsyncPageLoad (t * testing.T ) {
1123+ const (
1124+ size = 2 * asyncLoadMapUnit
1125+ gated = asyncLoadMapUnit
1126+ )
1127+ ctx := contexttest .Context (t )
1128+
1129+ src := newTestMemoryFile (t )
1130+ fr , err := src .Allocate (size , pgalloc.AllocOpts {Kind : usage .Anonymous })
1131+ if err != nil {
1132+ t .Fatalf ("Allocate got err %v want nil" , err )
1133+ }
1134+ if _ , err := unix .Pwrite (src .FD (), bytes .Repeat ([]byte {1 }, size ), int64 (fr .Start )); err != nil {
1135+ t .Fatalf ("Pwrite got err %v want nil" , err )
1136+ }
1137+ var metadata , pages bytes.Buffer
1138+ saveDone := make (chan error , 1 )
1139+ apfs , err := pgalloc .StartAsyncPagesFileSave (stateio .NewIOWriter (& pages , 1 << 20 , 1 , 1 ), func (err error ) { saveDone <- err })
1140+ if err != nil {
1141+ t .Fatalf ("StartAsyncPagesFileSave got err %v want nil" , err )
1142+ }
1143+ if err := src .SaveTo (ctx , & metadata , & pgalloc.SaveOpts {PagesFile : apfs }); err != nil {
1144+ t .Fatalf ("SaveTo got err %v want nil" , err )
1145+ }
1146+ apfs .MemoryFilesDone ()
1147+ if err := <- saveDone ; err != nil {
1148+ t .Fatalf ("async page save got err %v want nil" , err )
1149+ }
1150+
1151+ mf := newTestMemoryFile (t )
1152+ gate := make (chan struct {})
1153+ loadDone := make (chan error , 1 )
1154+ apfl , err := pgalloc .StartAsyncPagesFileLoad (stateio .NewIOReader (& gatedReaderAt {bytes .NewReader (pages .Bytes ()), gated , gate }, 1 << 20 , 1 , 2 ), func (err error ) { loadDone <- err }, nil , pgalloc.AsyncPagesFileLoadOpts {})
1155+ if err != nil {
1156+ t .Fatalf ("StartAsyncPagesFileLoad got err %v want nil" , err )
1157+ }
1158+ if err := mf .LoadFrom (ctx , & metadata , & pgalloc.LoadOpts {PagesFile : apfl }); err != nil {
1159+ t .Fatalf ("LoadFrom got err %v want nil" , err )
1160+ }
1161+ apfl .MemoryFilesDone ()
1162+
1163+ // Map the restored pages with a single pma.
1164+ mm := testMemoryManager (ctx , t )
1165+ mm .mf = mf // mm has no mappings yet.
1166+ defer mm .DecUsers (ctx )
1167+ addr , err := mm .MMap (ctx , memmap.MMapOpts {
1168+ Length : size ,
1169+ Private : true ,
1170+ Perms : hostarch .ReadWrite ,
1171+ MaxPerms : hostarch .AnyAccess ,
1172+ })
1173+ if err != nil {
1174+ t .Fatalf ("MMap got err %v want nil" , err )
1175+ }
1176+ ar := hostarch.AddrRange {addr , addr + size }
1177+ mm .activeMu .Lock ()
1178+ mm .pmas .Insert (mm .pmas .FindGap (ar .Start ), ar , pma {
1179+ file : mf ,
1180+ off : fr .Start ,
1181+ translatePerms : hostarch .AnyAccess ,
1182+ effectivePerms : hostarch .ReadWrite ,
1183+ maxPerms : hostarch .AnyAccess ,
1184+ private : true ,
1185+ })
1186+ mm .addRSSLocked (ar )
1187+ mm .activeMu .Unlock ()
1188+
1189+ timer := time .AfterFunc (10 * time .Second , func () {
1190+ t .Errorf ("CopyIn waited for pages past offset %#x" , gated )
1191+ close (gate )
1192+ })
1193+ b := make ([]byte , 8 )
1194+ _ , err = mm .CopyIn (ctx , addr + pageSizeAddr , b , usermem.IOOpts {})
1195+ if timer .Stop () {
1196+ close (gate )
1197+ }
1198+ if err := <- loadDone ; err != nil {
1199+ t .Errorf ("async page load got err %v want nil" , err )
1200+ }
1201+ if err != nil {
1202+ t .Fatalf ("CopyIn got err %v want nil" , err )
1203+ }
1204+ if want := bytes .Repeat ([]byte {1 }, len (b )); ! bytes .Equal (b , want ) {
1205+ t .Errorf ("CopyIn got %v want %v" , b , want )
1206+ }
1207+ }
0 commit comments