gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "fmt" 19 "testing" 20 21 "gvisor.dev/gvisor/pkg/hostarch" 22 ) 23 24 const ( 25 page = hostarch.PageSize 26 hugepage = hostarch.HugePageSize 27 topPage = (1 << 63) - page 28 ) 29 30 func TestFindUnallocatedRange(t *testing.T) { 31 for _, test := range []struct { 32 name string 33 usage []usageFlatSegment 34 fileSize int64 35 length uint64 36 alignment uint64 37 direction Direction 38 want uint64 39 expectFail bool 40 }{ 41 { 42 name: "Initial allocation succeeds", 43 length: page, 44 alignment: page, 45 direction: BottomUp, 46 want: 0, 47 }, 48 { 49 name: "Initial allocation succeeds", 50 length: page, 51 alignment: page, 52 direction: TopDown, 53 want: chunkSize - page, // Grows by chunkSize, allocate down. 54 }, 55 { 56 name: "Allocation begins at start of file", 57 usage: []usageFlatSegment{ 58 {page, 2 * page, usageInfo{refs: 1}}, 59 }, 60 length: page, 61 alignment: page, 62 direction: BottomUp, 63 want: 0, 64 }, 65 { 66 name: "Allocation finds empty space at start of file", 67 usage: []usageFlatSegment{ 68 {page, 2 * page, usageInfo{refs: 1}}, 69 }, 70 fileSize: 2 * page, 71 length: page, 72 alignment: page, 73 direction: TopDown, 74 }, 75 { 76 name: "Allocation finds empty space at end of file", 77 usage: []usageFlatSegment{ 78 {0, page, usageInfo{refs: 1}}, 79 }, 80 fileSize: 2 * page, 81 length: page, 82 alignment: page, 83 direction: TopDown, 84 want: page, 85 }, 86 { 87 name: "In-use frames are not allocatable", 88 usage: []usageFlatSegment{ 89 {0, page, usageInfo{refs: 1}}, 90 {page, 2 * page, usageInfo{refs: 2}}, 91 }, 92 length: page, 93 alignment: page, 94 direction: BottomUp, 95 want: 2 * page, 96 }, 97 { 98 name: "In-use frames are not allocatable", 99 usage: []usageFlatSegment{ 100 {0, page, usageInfo{refs: 1}}, 101 {page, 2 * page, usageInfo{refs: 2}}, 102 }, 103 fileSize: 2 * page, 104 length: page, 105 alignment: page, 106 direction: TopDown, 107 want: 3 * page, // Double fileSize, allocate top-down. 108 }, 109 { 110 name: "Reclaimable frames are not allocatable", 111 usage: []usageFlatSegment{ 112 {0, page, usageInfo{refs: 1}}, 113 {page, 2 * page, usageInfo{refs: 0}}, 114 {2 * page, 3 * page, usageInfo{refs: 1}}, 115 }, 116 length: page, 117 alignment: page, 118 direction: BottomUp, 119 want: 3 * page, 120 }, 121 { 122 name: "Reclaimable frames are not allocatable", 123 usage: []usageFlatSegment{ 124 {0, page, usageInfo{refs: 1}}, 125 {page, 2 * page, usageInfo{refs: 0}}, 126 {2 * page, 3 * page, usageInfo{refs: 1}}, 127 }, 128 fileSize: 3 * page, 129 length: page, 130 alignment: page, 131 direction: TopDown, 132 want: 5 * page, // Double fileSize, grow down. 133 }, 134 { 135 name: "Gaps between in-use frames are allocatable", 136 usage: []usageFlatSegment{ 137 {0, page, usageInfo{refs: 1}}, 138 {2 * page, 3 * page, usageInfo{refs: 1}}, 139 }, 140 length: page, 141 alignment: page, 142 direction: BottomUp, 143 want: page, 144 }, 145 { 146 name: "Gaps between in-use frames are allocatable", 147 usage: []usageFlatSegment{ 148 {0, page, usageInfo{refs: 1}}, 149 {2 * page, 3 * page, usageInfo{refs: 1}}, 150 }, 151 fileSize: 3 * page, 152 length: page, 153 alignment: page, 154 direction: TopDown, 155 want: page, 156 }, 157 { 158 name: "Inadequately-sized gaps are rejected", 159 usage: []usageFlatSegment{ 160 {0, page, usageInfo{refs: 1}}, 161 {2 * page, 3 * page, usageInfo{refs: 1}}, 162 }, 163 length: 2 * page, 164 alignment: page, 165 direction: BottomUp, 166 want: 3 * page, 167 }, 168 { 169 name: "Inadequately-sized gaps are rejected", 170 usage: []usageFlatSegment{ 171 {0, page, usageInfo{refs: 1}}, 172 {2 * page, 3 * page, usageInfo{refs: 1}}, 173 }, 174 fileSize: 3 * page, 175 length: 2 * page, 176 alignment: page, 177 direction: TopDown, 178 want: 4 * page, // Double fileSize, grow down. 179 }, 180 { 181 name: "Alignment is honored at end of file", 182 usage: []usageFlatSegment{ 183 {0, page, usageInfo{refs: 1}}, 184 // Hugepage-sized gap here that shouldn't be allocated from 185 // since it's incorrectly aligned. 186 {hugepage + page, hugepage + 2*page, usageInfo{refs: 1}}, 187 }, 188 length: hugepage, 189 alignment: hugepage, 190 direction: BottomUp, 191 want: 2 * hugepage, 192 }, 193 { 194 name: "Alignment is honored at end of file", 195 usage: []usageFlatSegment{ 196 {0, page, usageInfo{refs: 1}}, 197 // Hugepage-sized gap here that shouldn't be allocated from 198 // since it's incorrectly aligned. 199 {hugepage + page, hugepage + 2*page, usageInfo{refs: 1}}, 200 }, 201 fileSize: hugepage + 2*page, 202 length: hugepage, 203 alignment: hugepage, 204 direction: TopDown, 205 want: 3 * hugepage, // Double fileSize until alignment is satisfied, grow down. 206 }, 207 { 208 name: "Alignment is honored before end of file", 209 usage: []usageFlatSegment{ 210 {0, page, usageInfo{refs: 1}}, 211 // Page will need to be shifted down from top. 212 {2*hugepage + page, 2*hugepage + 2*page, usageInfo{refs: 1}}, 213 }, 214 fileSize: 2*hugepage + 2*page, 215 length: hugepage, 216 alignment: hugepage, 217 direction: TopDown, 218 want: hugepage, 219 }, 220 { 221 name: "Allocation doubles file size more than once if necessary", 222 fileSize: page, 223 length: 4 * page, 224 alignment: page, 225 direction: BottomUp, 226 want: 0, 227 }, 228 { 229 name: "Allocation doubles file size more than once if necessary", 230 fileSize: page, 231 length: 4 * page, 232 alignment: page, 233 direction: TopDown, 234 want: 0, 235 }, 236 { 237 name: "Allocations are compact if possible", 238 usage: []usageFlatSegment{ 239 {page, 2 * page, usageInfo{refs: 1}}, 240 {3 * page, 4 * page, usageInfo{refs: 2}}, 241 }, 242 fileSize: 4 * page, 243 length: page, 244 alignment: page, 245 direction: TopDown, 246 want: 2 * page, 247 }, 248 { 249 name: "Top-down allocation within one gap", 250 usage: []usageFlatSegment{ 251 {page, 2 * page, usageInfo{refs: 1}}, 252 {4 * page, 5 * page, usageInfo{refs: 2}}, 253 {7 * page, 8 * page, usageInfo{refs: 1}}, 254 }, 255 fileSize: 8 * page, 256 length: page, 257 alignment: page, 258 direction: TopDown, 259 want: 6 * page, 260 }, 261 { 262 name: "Top-down allocation between multiple gaps", 263 usage: []usageFlatSegment{ 264 {page, 2 * page, usageInfo{refs: 1}}, 265 {3 * page, 4 * page, usageInfo{refs: 2}}, 266 {5 * page, 6 * page, usageInfo{refs: 1}}, 267 }, 268 fileSize: 6 * page, 269 length: page, 270 alignment: page, 271 direction: TopDown, 272 want: 4 * page, 273 }, 274 { 275 name: "Top-down allocation with large top gap", 276 usage: []usageFlatSegment{ 277 {page, 2 * page, usageInfo{refs: 1}}, 278 {3 * page, 4 * page, usageInfo{refs: 2}}, 279 }, 280 fileSize: 8 * page, 281 length: page, 282 alignment: page, 283 direction: TopDown, 284 want: 7 * page, 285 }, 286 { 287 name: "Gaps found with possible overflow", 288 usage: []usageFlatSegment{ 289 {page, 2 * page, usageInfo{refs: 1}}, 290 {topPage - page, topPage, usageInfo{refs: 1}}, 291 }, 292 fileSize: topPage, 293 length: page, 294 alignment: page, 295 direction: TopDown, 296 want: topPage - 2*page, 297 }, 298 { 299 name: "Overflow detected", 300 usage: []usageFlatSegment{ 301 {page, topPage, usageInfo{refs: 1}}, 302 }, 303 fileSize: topPage, 304 length: 2 * page, 305 alignment: page, 306 direction: BottomUp, 307 expectFail: true, 308 }, 309 { 310 name: "Overflow detected", 311 usage: []usageFlatSegment{ 312 {page, topPage, usageInfo{refs: 1}}, 313 }, 314 fileSize: topPage, 315 length: 2 * page, 316 alignment: page, 317 direction: TopDown, 318 expectFail: true, 319 }, 320 { 321 name: "start may be in the middle of segment", 322 usage: []usageFlatSegment{ 323 {0, 2 * page, usageInfo{refs: 1}}, 324 {3 * page, 4 * page, usageInfo{refs: 2}}, 325 }, 326 length: page, 327 alignment: page, 328 direction: BottomUp, 329 want: 2 * page, 330 }, 331 } { 332 name := fmt.Sprintf("%s (%v)", test.name, test.direction) 333 t.Run(name, func(t *testing.T) { 334 f := MemoryFile{fileSize: test.fileSize} 335 if err := f.usage.ImportSlice(test.usage); err != nil { 336 t.Fatalf("Failed to initialize usage from %v: %v", test.usage, err) 337 } 338 if fr, ok := f.findAvailableRange(test.length, test.alignment, test.direction); ok { 339 if test.expectFail { 340 t.Fatalf("findAvailableRange(%v, %x, %x, %x, %v): got: %x, want: fail", test.usage, test.fileSize, test.length, test.alignment, test.direction, fr.Start) 341 } 342 if fr.Start != test.want { 343 t.Errorf("findAvailableRange(%v, %x, %x, %x, %v): got: start=%x, want: %x", test.usage, test.fileSize, test.length, test.alignment, test.direction, fr.Start, test.want) 344 } 345 if fr.End != test.want+test.length { 346 t.Errorf("findAvailableRange(%v, %x, %x, %x, %v): got: end=%x, want: %x", test.usage, test.fileSize, test.length, test.alignment, test.direction, fr.End, test.want+test.length) 347 } 348 } else if !test.expectFail { 349 t.Fatalf("findAvailableRange(%v, %x, %x, %x, %v): failed, want: %x", test.usage, test.fileSize, test.length, test.alignment, test.direction, test.want) 350 } 351 }) 352 } 353 }