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 }