github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/pgalloc/pgalloc_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pgalloc
    16  
    17  import (
    18  	"testing"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/hostarch"
    21  )
    22  
    23  const (
    24  	page     = hostarch.PageSize
    25  	hugepage = hostarch.HugePageSize
    26  	topPage  = (1 << 63) - page
    27  )
    28  
    29  func TestFindUnallocatedRange(t *testing.T) {
    30  	for _, test := range []struct {
    31  		desc       string
    32  		usage      *usageSegmentDataSlices
    33  		fileSize   int64
    34  		length     uint64
    35  		alignment  uint64
    36  		start      uint64
    37  		expectFail bool
    38  	}{
    39  		{
    40  			desc:      "Initial allocation succeeds",
    41  			usage:     &usageSegmentDataSlices{},
    42  			length:    page,
    43  			alignment: page,
    44  			start:     chunkSize - page, // Grows by chunkSize, allocate down.
    45  		},
    46  		{
    47  			desc: "Allocation finds empty space at start of file",
    48  			usage: &usageSegmentDataSlices{
    49  				Start:  []uint64{page},
    50  				End:    []uint64{2 * page},
    51  				Values: []usageInfo{{refs: 1}},
    52  			},
    53  			fileSize:  2 * page,
    54  			length:    page,
    55  			alignment: page,
    56  			start:     0,
    57  		},
    58  		{
    59  			desc: "Allocation finds empty space at end of file",
    60  			usage: &usageSegmentDataSlices{
    61  				Start:  []uint64{0},
    62  				End:    []uint64{page},
    63  				Values: []usageInfo{{refs: 1}},
    64  			},
    65  			fileSize:  2 * page,
    66  			length:    page,
    67  			alignment: page,
    68  			start:     page,
    69  		},
    70  		{
    71  			desc: "In-use frames are not allocatable",
    72  			usage: &usageSegmentDataSlices{
    73  				Start:  []uint64{0, page},
    74  				End:    []uint64{page, 2 * page},
    75  				Values: []usageInfo{{refs: 1}, {refs: 2}},
    76  			},
    77  			fileSize:  2 * page,
    78  			length:    page,
    79  			alignment: page,
    80  			start:     3 * page, // Double fileSize, allocate top-down.
    81  		},
    82  		{
    83  			desc: "Reclaimable frames are not allocatable",
    84  			usage: &usageSegmentDataSlices{
    85  				Start:  []uint64{0, page, 2 * page},
    86  				End:    []uint64{page, 2 * page, 3 * page},
    87  				Values: []usageInfo{{refs: 1}, {refs: 0}, {refs: 1}},
    88  			},
    89  			fileSize:  3 * page,
    90  			length:    page,
    91  			alignment: page,
    92  			start:     5 * page, // Double fileSize, grow down.
    93  		},
    94  		{
    95  			desc: "Gaps between in-use frames are allocatable",
    96  			usage: &usageSegmentDataSlices{
    97  				Start:  []uint64{0, 2 * page},
    98  				End:    []uint64{page, 3 * page},
    99  				Values: []usageInfo{{refs: 1}, {refs: 1}},
   100  			},
   101  			fileSize:  3 * page,
   102  			length:    page,
   103  			alignment: page,
   104  			start:     page,
   105  		},
   106  		{
   107  			desc: "Inadequately-sized gaps are rejected",
   108  			usage: &usageSegmentDataSlices{
   109  				Start:  []uint64{0, 2 * page},
   110  				End:    []uint64{page, 3 * page},
   111  				Values: []usageInfo{{refs: 1}, {refs: 1}},
   112  			},
   113  			fileSize:  3 * page,
   114  			length:    2 * page,
   115  			alignment: page,
   116  			start:     4 * page, // Double fileSize, grow down.
   117  		},
   118  		{
   119  			desc: "Alignment is honored at end of file",
   120  			usage: &usageSegmentDataSlices{
   121  				Start: []uint64{0, hugepage + page},
   122  				// Hugepage-sized gap here that shouldn't be allocated from
   123  				// since it's incorrectly aligned.
   124  				End:    []uint64{page, hugepage + 2*page},
   125  				Values: []usageInfo{{refs: 1}, {refs: 1}},
   126  			},
   127  			fileSize:  hugepage + 2*page,
   128  			length:    hugepage,
   129  			alignment: hugepage,
   130  			start:     3 * hugepage, // Double fileSize until alignment is satisfied, grow down.
   131  		},
   132  		{
   133  			desc: "Alignment is honored before end of file",
   134  			usage: &usageSegmentDataSlices{
   135  				Start: []uint64{0, 2*hugepage + page},
   136  				// Page will need to be shifted down from top.
   137  				End:    []uint64{page, 2*hugepage + 2*page},
   138  				Values: []usageInfo{{refs: 1}, {refs: 1}},
   139  			},
   140  			fileSize:  2*hugepage + 2*page,
   141  			length:    hugepage,
   142  			alignment: hugepage,
   143  			start:     hugepage,
   144  		},
   145  		{
   146  			desc:      "Allocation doubles file size more than once if necessary",
   147  			usage:     &usageSegmentDataSlices{},
   148  			fileSize:  page,
   149  			length:    4 * page,
   150  			alignment: page,
   151  			start:     0,
   152  		},
   153  		{
   154  			desc: "Allocations are compact if possible",
   155  			usage: &usageSegmentDataSlices{
   156  				Start:  []uint64{page, 3 * page},
   157  				End:    []uint64{2 * page, 4 * page},
   158  				Values: []usageInfo{{refs: 1}, {refs: 2}},
   159  			},
   160  			fileSize:  4 * page,
   161  			length:    page,
   162  			alignment: page,
   163  			start:     2 * page,
   164  		},
   165  		{
   166  			desc: "Top-down allocation within one gap",
   167  			usage: &usageSegmentDataSlices{
   168  				Start:  []uint64{page, 4 * page, 7 * page},
   169  				End:    []uint64{2 * page, 5 * page, 8 * page},
   170  				Values: []usageInfo{{refs: 1}, {refs: 2}, {refs: 1}},
   171  			},
   172  			fileSize:  8 * page,
   173  			length:    page,
   174  			alignment: page,
   175  			start:     6 * page,
   176  		},
   177  		{
   178  			desc: "Top-down allocation between multiple gaps",
   179  			usage: &usageSegmentDataSlices{
   180  				Start:  []uint64{page, 3 * page, 5 * page},
   181  				End:    []uint64{2 * page, 4 * page, 6 * page},
   182  				Values: []usageInfo{{refs: 1}, {refs: 2}, {refs: 1}},
   183  			},
   184  			fileSize:  6 * page,
   185  			length:    page,
   186  			alignment: page,
   187  			start:     4 * page,
   188  		},
   189  		{
   190  			desc: "Top-down allocation with large top gap",
   191  			usage: &usageSegmentDataSlices{
   192  				Start:  []uint64{page, 3 * page},
   193  				End:    []uint64{2 * page, 4 * page},
   194  				Values: []usageInfo{{refs: 1}, {refs: 2}},
   195  			},
   196  			fileSize:  8 * page,
   197  			length:    page,
   198  			alignment: page,
   199  			start:     7 * page,
   200  		},
   201  		{
   202  			desc: "Gaps found with possible overflow",
   203  			usage: &usageSegmentDataSlices{
   204  				Start:  []uint64{page, topPage - page},
   205  				End:    []uint64{2 * page, topPage},
   206  				Values: []usageInfo{{refs: 1}, {refs: 1}},
   207  			},
   208  			fileSize:  topPage,
   209  			length:    page,
   210  			alignment: page,
   211  			start:     topPage - 2*page,
   212  		},
   213  		{
   214  			desc: "Overflow detected",
   215  			usage: &usageSegmentDataSlices{
   216  				Start:  []uint64{page},
   217  				End:    []uint64{topPage},
   218  				Values: []usageInfo{{refs: 1}},
   219  			},
   220  			fileSize:   topPage,
   221  			length:     2 * page,
   222  			alignment:  page,
   223  			expectFail: true,
   224  		},
   225  	} {
   226  		t.Run(test.desc, func(t *testing.T) {
   227  			var usage usageSet
   228  			if err := usage.ImportSortedSlices(test.usage); err != nil {
   229  				t.Fatalf("Failed to initialize usage from %v: %v", test.usage, err)
   230  			}
   231  			fr, ok := findAvailableRange(&usage, test.fileSize, test.length, test.alignment)
   232  			if !test.expectFail && !ok {
   233  				t.Fatalf("findAvailableRange(%v, %x, %x, %x): got %x, false wanted %x, true", test.usage, test.fileSize, test.length, test.alignment, fr.Start, test.start)
   234  			}
   235  			if test.expectFail && ok {
   236  				t.Fatalf("findAvailableRange(%v, %x, %x, %x): got %x, true wanted %x, false", test.usage, test.fileSize, test.length, test.alignment, fr.Start, test.start)
   237  			}
   238  			if ok && fr.Start != test.start {
   239  				t.Errorf("findAvailableRange(%v, %x, %x, %x): got start=%x, wanted %x", test.usage, test.fileSize, test.length, test.alignment, fr.Start, test.start)
   240  			}
   241  			if ok && fr.End != test.start+test.length {
   242  				t.Errorf("findAvailableRange(%v, %x, %x, %x): got end=%x, wanted %x", test.usage, test.fileSize, test.length, test.alignment, fr.End, test.start+test.length)
   243  			}
   244  		})
   245  	}
   246  }