segment.Set API improvements.

- Replace Add with TryInsertRange; for symmetry with RemoveRange, to establish
  the convention that *Range methods perform an implicit search in the set, and
  so that we can fork InsertRange which has Insert-like semantics (panics on
  conflict), which the majority of callers want.

- Rename MergeRange and MergeAdjacent to MergeInsideRange and MergeOutsideRange
  respectively; for the same convention, and to more clearly describe the
  difference between these functions.

- Add MergePrev and MergeNext. These solve the longstanding problem of
  requiring a separate call to Merge{Inside,Outside}Range (which will perform
  additional searches) after mutating a set in a relatively simple manner.

- Add SplitBefore and SplitAfter, which are halves of Isolate. These are
  slightly preferable to Isolate in many use cases for the latter (when
  iterating segments within a range, only the first segment can include a key
  before the start, so this saves some useless comparisons in almost every
  iteration of such loops), and are useful in some more complex algorithms.
  Also add LowerBoundSegmentSplitBefore and UpperBoundSegmentSplitAfter as
  ergonomic aids for the former use case.

- Add {Visit,Mutate}[Full]Range, which are convenience wrappers around the
  iterator API (including new functions) for simple use cases (and hence also
  serve to demonstrate how the new iterator functions are used).
  MutateFullRange in particular replaces ApplyContiguous and adds merging
  during iteration.

- Add RemoveFullRange, which (analogous to {Visit,Mutate}FullRange) is a
  variant of RemoveRange that checks that the range is fully covered by
  segments.

- Add Unisolate, which combines MergePrev and MergeNext in the same way that
  Isolate combines SplitBefore and SplitAfter. This is useful for merging after
  mutation of a single segment.

- Add {First,Last,LowerBound,UpperBound}LargeEnoughGap, which are convenient
  loop starters when using gap tracking.

- Replace SegmentDataSlices with FlatSegment, which is easier to use when
  specifying "set literals" (as in tests).

- Make {prev,next}LargeEnoughGapHelper iterative rather than tail-recursive.

- Slightly optimize Iterator.{Prev,Next}NonEmpty: GapIterator.{Start,End} needs
  to find the corresponding Iterator, so call Iterator.{Prev,Next}Segment
  directly rather than doing so twice.

PiperOrigin-RevId: 583506148
This commit is contained in:
Jamie Liu
2023-11-17 15:51:12 -08:00
committed by gVisor bot
parent 2ef56fee94
commit 88d35cd8f1
16 changed files with 770 additions and 492 deletions
+509 -163
View File
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -14,12 +14,16 @@
package segment
func (s *Set) saveRoot() *SegmentDataSlices {
return s.ExportSortedSlices()
func (s *Set) saveRoot() []FlatSegment {
fs := s.ExportSlice()
// The state package saves data in slice capacity beyond slice length; save
// it some time by cutting ours off.
fs = fs[:len(fs):len(fs)]
return fs
}
func (s *Set) loadRoot(sds *SegmentDataSlices) {
if err := s.ImportSortedSlices(sds); err != nil {
func (s *Set) loadRoot(fs []FlatSegment) {
if err := s.ImportSlice(fs); err != nil {
panic(err)
}
}
+98 -124
View File
@@ -94,10 +94,7 @@ func TestAddRandom(t *testing.T) {
order := rand.Perm(testSize)
var nrInsertions int
for i, j := range order {
if !s.AddWithoutMerging(Range{j, j + 1}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
s.InsertWithoutMergingRange(Range{j, j + 1}, j+valueOffset)
nrInsertions++
if err := s.segmentTestCheck(nrInsertions, validate); err != nil {
t.Errorf("Iteration %d: %v", i, err)
@@ -116,9 +113,7 @@ func TestAddRandom(t *testing.T) {
func TestRemoveRandom(t *testing.T) {
var s Set
for i := 0; i < testSize; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i+valueOffset) {
t.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i+valueOffset)
}
order := rand.Perm(testSize)
var nrRemovals int
@@ -150,10 +145,7 @@ func TestMaxGapAddRandom(t *testing.T) {
order := rand.Perm(testSize)
var nrInsertions int
for i, j := range order {
if !s.AddWithoutMerging(Range{j, j + 1}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
s.InsertWithoutMergingRange(Range{j, j + 1}, j+valueOffset)
nrInsertions++
if err := s.segmentTestCheck(nrInsertions, validate); err != nil {
t.Errorf("Iteration %d: %v", i, err)
@@ -178,10 +170,7 @@ func TestMaxGapAddRandomWithRandomInterval(t *testing.T) {
order := randIntervalPermutation(testSize)
var nrInsertions int
for i, j := range order {
if !s.AddWithoutMerging(Range{j, j + rand.Intn(intervalLength-1) + 1}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
s.InsertWithoutMergingRange(Range{j, j + rand.Intn(intervalLength-1) + 1}, j+valueOffset)
nrInsertions++
if err := s.segmentTestCheck(nrInsertions, validate); err != nil {
t.Errorf("Iteration %d: %v", i, err)
@@ -204,18 +193,14 @@ func TestMaxGapAddRandomWithRandomInterval(t *testing.T) {
func TestMaxGapAddRandomWithMerge(t *testing.T) {
var s gapSet
order := randIntervalPermutation(testSize)
nrInsertions := 1
for i, j := range order {
if !s.Add(Range{j, j + intervalLength}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
for _, j := range order {
s.InsertRange(Range{j, j + intervalLength}, 0)
if err := checkSetMaxGap(&s); err != nil {
t.Errorf("When inserting %d: %v", j, err)
break
}
}
if got, want := s.countSegments(), nrInsertions; got != want {
if got, want := s.countSegments(), 1; got != want {
t.Errorf("Wrong final number of segments: got %d, wanted %d", got, want)
}
if t.Failed() {
@@ -227,9 +212,7 @@ func TestMaxGapAddRandomWithMerge(t *testing.T) {
func TestMaxGapRemoveRandom(t *testing.T) {
var s gapSet
for i := 0; i < testSize; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i+valueOffset) {
t.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i+valueOffset)
}
order := rand.Perm(testSize)
var nrRemovals int
@@ -264,9 +247,7 @@ func TestMaxGapRemoveRandom(t *testing.T) {
func TestMaxGapRemoveHalfRandom(t *testing.T) {
var s gapSet
for i := 0; i < testSize; i++ {
if !s.AddWithoutMerging(Range{intervalLength * i, intervalLength*i + rand.Intn(intervalLength-1) + 1}, intervalLength*i+valueOffset) {
t.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{intervalLength * i, intervalLength*i + rand.Intn(intervalLength-1) + 1}, intervalLength*i+valueOffset)
}
order := randIntervalPermutation(testSize)
order = order[:testSize/2]
@@ -299,29 +280,15 @@ func TestMaxGapRemoveHalfRandom(t *testing.T) {
}
}
func TestMaxGapAddRandomRemoveRandomHalfWithMerge(t *testing.T) {
func TestMaxGapRemoveHalfRandomWithMerge(t *testing.T) {
var s gapSet
order := randIntervalPermutation(testSize * 2)
order = order[:testSize]
for i, j := range order {
if !s.Add(Range{j, j + intervalLength}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
if err := checkSetMaxGap(&s); err != nil {
t.Errorf("When inserting %d: %v", j, err)
break
}
}
shuffle(order)
s.InsertRange(Range{0, intervalLength * testSize}, 0)
order := randIntervalPermutation(testSize)
order = order[:testSize/2]
var nrRemovals int
for _, j := range order {
seg := s.FindSegment(j)
if !seg.Ok() {
continue
}
temprange := seg.Range()
s.Remove(seg)
temprange := Range{j, j + intervalLength}
s.RemoveFullRange(temprange)
nrRemovals++
if err := checkSetMaxGap(&s); err != nil {
t.Errorf("When removing %v: %v", temprange, err)
@@ -339,11 +306,8 @@ func TestNextLargeEnoughGap(t *testing.T) {
var s gapSet
order := randIntervalPermutation(testSize * 2)
order = order[:testSize]
for i, j := range order {
if !s.Add(Range{j, j + rand.Intn(intervalLength-1) + 1}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
for _, j := range order {
s.InsertRange(Range{j, j + rand.Intn(intervalLength-1) + 1}, j+valueOffset)
if err := checkSetMaxGap(&s); err != nil {
t.Errorf("When inserting %d: %v", j, err)
break
@@ -392,11 +356,8 @@ func TestPrevLargeEnoughGap(t *testing.T) {
var s gapSet
order := randIntervalPermutation(testSize * 2)
order = order[:testSize]
for i, j := range order {
if !s.Add(Range{j, j + rand.Intn(intervalLength-1) + 1}, j+valueOffset) {
t.Errorf("Iteration %d: failed to insert segment with key %d", i, j)
break
}
for _, j := range order {
s.InsertRange(Range{j, j + rand.Intn(intervalLength-1) + 1}, j+valueOffset)
if err := checkSetMaxGap(&s); err != nil {
t.Errorf("When inserting %d: %v", j, err)
break
@@ -445,9 +406,7 @@ func TestAddSequentialAdjacent(t *testing.T) {
var s Set
var nrInsertions int
for i := 0; i < testSize; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i+valueOffset) {
t.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i+valueOffset)
nrInsertions++
if err := s.segmentTestCheck(nrInsertions, validate); err != nil {
t.Errorf("Iteration %d: %v", i, err)
@@ -499,9 +458,7 @@ func TestAddSequentialNonAdjacent(t *testing.T) {
for i := 0; i < testSize; i++ {
// The range here differs from TestAddSequentialAdjacent so that
// consecutive segments are not adjacent.
if !s.AddWithoutMerging(Range{2 * i, 2*i + 1}, 2*i+valueOffset) {
t.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{2 * i, 2*i + 1}, 2*i+valueOffset)
nrInsertions++
if err := s.segmentTestCheck(nrInsertions, validate); err != nil {
t.Errorf("Iteration %d: %v", i, err)
@@ -527,7 +484,7 @@ func TestAddSequentialNonAdjacent(t *testing.T) {
}
}
func TestMergeSplit(t *testing.T) {
func TestMerge(t *testing.T) {
tests := []struct {
name string
initial []Range
@@ -536,60 +493,26 @@ func TestMergeSplit(t *testing.T) {
final []Range
}{
{
name: "Add merges after existing segment",
name: "InsertRange merges after existing segment",
initial: []Range{{1000, 1100}, {1100, 1200}},
final: []Range{{1000, 1200}},
},
{
name: "Add merges before existing segment",
name: "InsertRange merges before existing segment",
initial: []Range{{1100, 1200}, {1000, 1100}},
final: []Range{{1000, 1200}},
},
{
name: "Add merges between existing segments",
name: "InsertRange merges between existing segments",
initial: []Range{{1000, 1100}, {1200, 1300}, {1100, 1200}},
final: []Range{{1000, 1300}},
},
{
name: "SplitAt does nothing at a free address",
initial: []Range{{100, 200}},
split: true,
splitAddr: 300,
final: []Range{{100, 200}},
},
{
name: "SplitAt does nothing at the beginning of a segment",
initial: []Range{{100, 200}},
split: true,
splitAddr: 100,
final: []Range{{100, 200}},
},
{
name: "SplitAt does nothing at the end of a segment",
initial: []Range{{100, 200}},
split: true,
splitAddr: 200,
final: []Range{{100, 200}},
},
{
name: "SplitAt splits in the middle of a segment",
initial: []Range{{100, 200}},
split: true,
splitAddr: 150,
final: []Range{{100, 150}, {150, 200}},
},
}
Tests:
for _, test := range tests {
var s Set
for _, r := range test.initial {
if !s.Add(r, 0) {
t.Errorf("%s: Add(%v) failed; set contents:\n%v", test.name, r, &s)
continue Tests
}
}
if test.split {
s.SplitAt(test.splitAddr)
s.InsertRange(r, 0)
}
var i int
for seg := s.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
@@ -667,13 +590,76 @@ Tests:
}
}
func TestMutateRange(t *testing.T) {
tests := []struct {
name string
initial []FlatSegment
increment Range
final []FlatSegment
}{
{
name: "MutateRange no-op in empty set",
increment: Range{100, 200},
},
{
name: "MutateRange modifies existing segment",
initial: []FlatSegment{
{100, 200, 0},
},
increment: Range{100, 200},
final: []FlatSegment{
{100, 200, 1},
},
},
{
name: "MutateRange splits segments",
initial: []FlatSegment{
{50, 150, 0},
{150, 250, 2},
},
increment: Range{100, 200},
final: []FlatSegment{
{50, 100, 0},
{100, 150, 1},
{150, 200, 3},
{200, 250, 2},
},
},
{
name: "MutateRange merges compatible segments",
initial: []FlatSegment{
{0, 100, 1},
{100, 200, 0},
{200, 300, 1},
},
increment: Range{100, 200},
final: []FlatSegment{
{0, 300, 1},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var s Set
if err := s.ImportSlice(test.initial); err != nil {
t.Fatalf("Failed to import initial set: %v", err)
}
s.MutateRange(test.increment, func(seg Iterator) bool {
(*seg.ValuePtr())++
return true
})
if got := s.ExportSlice(); !reflect.DeepEqual(got, test.final) {
t.Errorf("Set mismatch after mutation: got %v, wanted %v", got, test.final)
}
})
}
}
func benchmarkAddSequential(b *testing.B, size int) {
for n := 0; n < b.N; n++ {
var s Set
for i := 0; i < size; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
}
}
@@ -685,9 +671,7 @@ func benchmarkAddRandom(b *testing.B, size int) {
for n := 0; n < b.N; n++ {
var s Set
for _, i := range order {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
}
}
@@ -695,9 +679,7 @@ func benchmarkAddRandom(b *testing.B, size int) {
func benchmarkFindSequential(b *testing.B, size int) {
var s Set
for i := 0; i < size; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
b.ResetTimer()
@@ -713,9 +695,7 @@ func benchmarkFindSequential(b *testing.B, size int) {
func benchmarkFindRandom(b *testing.B, size int) {
var s Set
for i := 0; i < size; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
order := rand.Perm(size)
@@ -732,9 +712,7 @@ func benchmarkFindRandom(b *testing.B, size int) {
func benchmarkIteration(b *testing.B, size int) {
var s Set
for i := 0; i < size; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
b.ResetTimer()
@@ -753,9 +731,7 @@ func benchmarkAddFindRemoveSequential(b *testing.B, size int) {
for n := 0; n < b.N; n++ {
var s Set
for i := 0; i < size; i++ {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
for i := 0; i < size; i++ {
seg := s.FindSegment(i)
@@ -777,9 +753,7 @@ func benchmarkAddFindRemoveRandom(b *testing.B, size int) {
for n := 0; n < b.N; n++ {
var s Set
for _, i := range order {
if !s.AddWithoutMerging(Range{i, i + 1}, i) {
b.Fatalf("Failed to insert segment %d", i)
}
s.InsertWithoutMergingRange(Range{i, i + 1}, i)
}
for _, i := range order {
seg := s.FindSegment(i)
+2 -2
View File
@@ -29,8 +29,8 @@ func (setFunctions) MaxKey() int {
func (setFunctions) ClearValue(*int) {}
func (setFunctions) Merge(_ Range, val1 int, _ Range, _ int) (int, bool) {
return val1, true
func (setFunctions) Merge(_ Range, val1 int, _ Range, val2 int) (int, bool) {
return val1, val1 == val2
}
func (setFunctions) Split(_ Range, val int, _ int) (int, int) {
+2 -6
View File
@@ -15,8 +15,6 @@
package accel
import (
"fmt"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/gasket"
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -117,12 +115,10 @@ func gasketMapBufferIoctl(ctx context.Context, t *kernel.Task, hostFd int32, fd
defer fd.device.mu.Unlock()
for _, pr := range prs {
rlen := uint64(pr.Source.Length())
if !fd.device.devAddrSet.Add(DevAddrRange{
fd.device.devAddrSet.InsertRange(DevAddrRange{
devAddr,
devAddr + rlen,
}, pinnedAccelMem{pinnedRange: pr, pageTableIndex: userIoctlParams.PageTableIndex}) {
panic(fmt.Sprintf("unexpected overlap of devaddr range [%#x-%#x)", devAddr, devAddr+rlen))
}
}, pinnedAccelMem{pinnedRange: pr, pageTableIndex: userIoctlParams.PageTableIndex})
devAddr += rlen
}
return n, nil
+20 -20
View File
@@ -73,57 +73,57 @@ func (dirtySetFunctions) Split(_ memmap.MappableRange, val DirtyInfo, _ uint64)
// MarkClean marks all offsets in mr as not dirty, except for those to which
// KeepDirty has been applied.
func (ds *DirtySet) MarkClean(mr memmap.MappableRange) {
seg := ds.LowerBoundSegment(mr.Start)
func (s *DirtySet) MarkClean(mr memmap.MappableRange) {
seg := s.LowerBoundSegment(mr.Start)
for seg.Ok() && seg.Start() < mr.End {
if seg.Value().Keep {
seg = seg.NextSegment()
continue
}
seg = ds.Isolate(seg, mr)
seg = ds.Remove(seg).NextSegment()
seg = s.Isolate(seg, mr)
seg = s.Remove(seg).NextSegment()
}
}
// KeepClean marks all offsets in mr as not dirty, even those that were
// previously kept dirty by KeepDirty.
func (ds *DirtySet) KeepClean(mr memmap.MappableRange) {
ds.RemoveRange(mr)
func (s *DirtySet) KeepClean(mr memmap.MappableRange) {
s.RemoveRange(mr)
}
// MarkDirty marks all offsets in mr as dirty.
func (ds *DirtySet) MarkDirty(mr memmap.MappableRange) {
ds.setDirty(mr, false)
func (s *DirtySet) MarkDirty(mr memmap.MappableRange) {
s.setDirty(mr, false)
}
// KeepDirty marks all offsets in mr as dirty and prevents them from being
// marked as clean by MarkClean.
func (ds *DirtySet) KeepDirty(mr memmap.MappableRange) {
ds.setDirty(mr, true)
func (s *DirtySet) KeepDirty(mr memmap.MappableRange) {
s.setDirty(mr, true)
}
func (ds *DirtySet) setDirty(mr memmap.MappableRange, keep bool) {
func (s *DirtySet) setDirty(mr memmap.MappableRange, keep bool) {
var changedAny bool
defer func() {
if changedAny {
// Merge segments split by Isolate to reduce cost of iteration.
ds.MergeRange(mr)
s.MergeInsideRange(mr)
}
}()
seg, gap := ds.Find(mr.Start)
seg, gap := s.Find(mr.Start)
for {
switch {
case seg.Ok() && seg.Start() < mr.End:
if keep && !seg.Value().Keep {
changedAny = true
seg = ds.Isolate(seg, mr)
seg = s.Isolate(seg, mr)
seg.ValuePtr().Keep = true
}
seg, gap = seg.NextNonEmpty()
case gap.Ok() && gap.Start() < mr.End:
changedAny = true
seg = ds.Insert(gap, gap.Range().Intersect(mr), DirtyInfo{keep})
seg = s.Insert(gap, gap.Range().Intersect(mr), DirtyInfo{keep})
seg, gap = seg.NextNonEmpty()
default:
@@ -135,18 +135,18 @@ func (ds *DirtySet) setDirty(mr memmap.MappableRange, keep bool) {
// AllowClean allows MarkClean to mark offsets in mr as not dirty, ending the
// effect of a previous call to KeepDirty. (It does not itself mark those
// offsets as not dirty.)
func (ds *DirtySet) AllowClean(mr memmap.MappableRange) {
func (s *DirtySet) AllowClean(mr memmap.MappableRange) {
var changedAny bool
defer func() {
if changedAny {
// Merge segments split by Isolate to reduce cost of iteration.
ds.MergeRange(mr)
s.MergeInsideRange(mr)
}
}()
for seg := ds.LowerBoundSegment(mr.Start); seg.Ok() && seg.Start() < mr.End; seg = seg.NextSegment() {
for seg := s.LowerBoundSegment(mr.Start); seg.Ok() && seg.Start() < mr.End; seg = seg.NextSegment() {
if seg.Value().Keep {
changedAny = true
seg = ds.Isolate(seg, mr)
seg = s.Isolate(seg, mr)
seg.ValuePtr().Keep = false
}
}
@@ -163,7 +163,7 @@ func SyncDirty(ctx context.Context, mr memmap.MappableRange, cache *FileRangeSet
defer func() {
if changedDirty {
// Merge segments split by Isolate to reduce cost of iteration.
dirty.MergeRange(mr)
dirty.MergeInsideRange(mr)
}
}()
dseg := dirty.LowerBoundSegment(mr.Start)
+3 -5
View File
@@ -27,12 +27,10 @@ func TestDirtySet(t *testing.T) {
set.MarkDirty(memmap.MappableRange{0, 2 * hostarch.PageSize})
set.KeepDirty(memmap.MappableRange{hostarch.PageSize, 2 * hostarch.PageSize})
set.MarkClean(memmap.MappableRange{0, 2 * hostarch.PageSize})
want := &DirtySegmentDataSlices{
Start: []uint64{hostarch.PageSize},
End: []uint64{2 * hostarch.PageSize},
Values: []DirtyInfo{{Keep: true}},
want := []DirtyFlatSegment{
{hostarch.PageSize, 2 * hostarch.PageSize, DirtyInfo{Keep: true}},
}
if got := set.ExportSortedSlices(); !reflect.DeepEqual(got, want) {
if got := set.ExportSlice(); !reflect.DeepEqual(got, want) {
t.Errorf("set:\n\tgot %v,\n\twant %v", got, want)
}
}
+17 -18
View File
@@ -80,9 +80,9 @@ func (seg FileRangeIterator) FileRangeOf(mr memmap.MappableRange) memmap.FileRan
// PagesToFill returns the number of pages that that Fill() will allocate
// for the given required and optional parameters.
func (frs *FileRangeSet) PagesToFill(required, optional memmap.MappableRange) uint64 {
func (s *FileRangeSet) PagesToFill(required, optional memmap.MappableRange) uint64 {
var numPages uint64
gap := frs.LowerBoundGap(required.Start)
gap := s.LowerBoundGap(required.Start)
for gap.Ok() && gap.Start() < required.End {
gr := gap.Range().Intersect(optional)
numPages += gr.Length() / hostarch.PageSize
@@ -111,8 +111,8 @@ func (frs *FileRangeSet) PagesToFill(required, optional memmap.MappableRange) ui
// - required.Length() > 0.
// - optional.IsSupersetOf(required).
// - required and optional must be page-aligned.
func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.MappableRange, fileSize uint64, mf *pgalloc.MemoryFile, kind usage.MemoryKind, allocMode pgalloc.AllocationMode, readAt func(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error)) (uint64, error) {
gap := frs.LowerBoundGap(required.Start)
func (s *FileRangeSet) Fill(ctx context.Context, required, optional memmap.MappableRange, fileSize uint64, mf *pgalloc.MemoryFile, kind usage.MemoryKind, allocMode pgalloc.AllocationMode, readAt func(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error)) (uint64, error) {
gap := s.LowerBoundGap(required.Start)
var pagesAlloced uint64
memCgID := pgalloc.MemoryCgroupIDFromContext(ctx)
for gap.Ok() && gap.Start() < required.End {
@@ -175,7 +175,7 @@ func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.Map
if done := fr.Length(); done != 0 {
gr.End = gr.Start + done
pagesAlloced += gr.Length() / hostarch.PageSize
gap = frs.Insert(gap, gr, fr.Start).NextGap()
gap = s.Insert(gap, gr, fr.Start).NextGap()
}
if err != nil {
@@ -189,43 +189,42 @@ func (frs *FileRangeSet) Fill(ctx context.Context, required, optional memmap.Map
// corresponding memmap.FileRanges.
//
// Preconditions: mr must be page-aligned.
func (frs *FileRangeSet) Drop(mr memmap.MappableRange, mf *pgalloc.MemoryFile) {
seg := frs.LowerBoundSegment(mr.Start)
func (s *FileRangeSet) Drop(mr memmap.MappableRange, mf *pgalloc.MemoryFile) {
seg := s.LowerBoundSegment(mr.Start)
for seg.Ok() && seg.Start() < mr.End {
seg = frs.Isolate(seg, mr)
seg = s.Isolate(seg, mr)
mf.DecRef(seg.FileRange())
seg = frs.Remove(seg).NextSegment()
seg = s.Remove(seg).NextSegment()
}
}
// DropAll removes all segments in mr, freeing the corresponding
// memmap.FileRanges. It returns the number of pages freed.
func (frs *FileRangeSet) DropAll(mf *pgalloc.MemoryFile) uint64 {
func (s *FileRangeSet) DropAll(mf *pgalloc.MemoryFile) uint64 {
var pagesFreed uint64
for seg := frs.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
for seg := s.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
mf.DecRef(seg.FileRange())
pagesFreed += seg.Range().Length() / hostarch.PageSize
}
frs.RemoveAll()
s.RemoveAll()
return pagesFreed
}
// Truncate updates frs to reflect Mappable truncation to the given length:
// Truncate updates s to reflect Mappable truncation to the given length:
// bytes after the new EOF on the same page are zeroed, and pages after the new
// EOF are freed. It returns the number of pages freed.
func (frs *FileRangeSet) Truncate(end uint64, mf *pgalloc.MemoryFile) uint64 {
func (s *FileRangeSet) Truncate(end uint64, mf *pgalloc.MemoryFile) uint64 {
var pagesFreed uint64
pgendaddr, ok := hostarch.Addr(end).RoundUp()
if ok {
pgend := uint64(pgendaddr)
// Free truncated pages.
frs.SplitAt(pgend)
seg := frs.LowerBoundSegment(pgend)
seg := s.LowerBoundSegmentSplitBefore(pgend)
for seg.Ok() {
mf.DecRef(seg.FileRange())
pagesFreed += seg.Range().Length() / hostarch.PageSize
seg = frs.Remove(seg).NextSegment()
seg = s.Remove(seg).NextSegment()
}
if end == pgend {
@@ -236,7 +235,7 @@ func (frs *FileRangeSet) Truncate(end uint64, mf *pgalloc.MemoryFile) uint64 {
// Here we know end < end.RoundUp(). If the new EOF lands in the
// middle of a page that we have, zero out its contents beyond the new
// length.
seg := frs.FindSegment(end)
seg := s.FindSegment(end)
if seg.Ok() {
fr := seg.FileRange()
fr.Start += end - seg.Start()
+10 -10
View File
@@ -64,21 +64,21 @@ func (FrameRefSetFunctions) Split(_ memmap.FileRange, val FrameRefSegInfo, _ uin
// are accounted as host page cache memory mappings. The new segments will be
// associated with the memCgID, if the segment already exists then the memCgID
// will not be changed.
func (frSet *FrameRefSet) IncRefAndAccount(fr memmap.FileRange, memCgID uint32) {
seg, gap := frSet.Find(fr.Start)
func (s *FrameRefSet) IncRefAndAccount(fr memmap.FileRange, memCgID uint32) {
seg, gap := s.Find(fr.Start)
for {
switch {
case seg.Ok() && seg.Start() < fr.End:
seg = frSet.Isolate(seg, fr)
seg = s.Isolate(seg, fr)
seg.ValuePtr().refs++
seg, gap = seg.NextNonEmpty()
case gap.Ok() && gap.Start() < fr.End:
newRange := gap.Range().Intersect(fr)
usage.MemoryAccounting.Inc(newRange.Length(), usage.Mapped, memCgID)
frInfo := FrameRefSegInfo{refs: 1, memCgID: memCgID}
seg, gap = frSet.InsertWithoutMerging(gap, newRange, frInfo).NextNonEmpty()
seg, gap = s.InsertWithoutMerging(gap, newRange, frInfo).NextNonEmpty()
default:
frSet.MergeAdjacent(fr)
s.MergeOutsideRange(fr)
return
}
}
@@ -86,18 +86,18 @@ func (frSet *FrameRefSet) IncRefAndAccount(fr memmap.FileRange, memCgID uint32)
// DecRefAndAccount removes a reference on the range fr and untracks segments
// that are removed from memory accounting.
func (frSet *FrameRefSet) DecRefAndAccount(fr memmap.FileRange) {
seg := frSet.FindSegment(fr.Start)
func (s *FrameRefSet) DecRefAndAccount(fr memmap.FileRange) {
seg := s.FindSegment(fr.Start)
for seg.Ok() && seg.Start() < fr.End {
seg = frSet.Isolate(seg, fr)
seg = s.Isolate(seg, fr)
if old := seg.ValuePtr().refs; old == 1 {
usage.MemoryAccounting.Dec(seg.Range().Length(), usage.Mapped, seg.ValuePtr().memCgID)
seg = frSet.Remove(seg).NextSegment()
seg = s.Remove(seg).NextSegment()
} else {
seg.ValuePtr().refs--
seg = seg.NextSegment()
}
}
frSet.MergeAdjacent(fr)
s.MergeOutsideRange(fr)
}
+4 -4
View File
@@ -185,10 +185,10 @@ func (ns *UserNamespace) trySetUIDMap(entries []IDMapEntry) error {
return linuxerr.EPERM
}
// If either of these Adds fail, we have an overlapping range.
if !ns.uidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) {
if !ns.uidMapFromParent.TryInsertRange(idMapRange{e.FirstParentID, lastParentID}, e.FirstID).Ok() {
return linuxerr.EINVAL
}
if !ns.uidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) {
if !ns.uidMapToParent.TryInsertRange(idMapRange{e.FirstID, lastID}, e.FirstParentID).Ok() {
return linuxerr.EINVAL
}
}
@@ -248,10 +248,10 @@ func (ns *UserNamespace) trySetGIDMap(entries []IDMapEntry) error {
if !ns.parent.allIDsMapped(&ns.parent.gidMapToParent, e.FirstParentID, lastParentID) {
return linuxerr.EPERM
}
if !ns.gidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) {
if !ns.gidMapFromParent.TryInsertRange(idMapRange{e.FirstParentID, lastParentID}, e.FirstID).Ok() {
return linuxerr.EINVAL
}
if !ns.gidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) {
if !ns.gidMapToParent.TryInsertRange(idMapRange{e.FirstID, lastID}, e.FirstParentID).Ok() {
return linuxerr.EINVAL
}
}
+2 -3
View File
@@ -76,9 +76,8 @@ func NewRootUserNamespace() *UserNamespace {
&ns.gidMapFromParent,
&ns.gidMapToParent,
} {
if !m.Add(idMapRange{0, math.MaxUint32}, 0) {
panic("Failed to insert into empty ID map")
}
// Insertion into an empty map shouldn't fail.
m.InsertRange(idMapRange{0, math.MaxUint32}, 0)
}
return &ns
}
+1 -1
View File
@@ -228,7 +228,7 @@ func (s *MappingSet) RemoveMapping(ms MappingSpace, ar hostarch.AddrRange, offse
seg = seg.NextSegment()
}
}
s.MergeAdjacent(mr)
s.MergeOutsideRange(mr)
return unmapped
}
+1 -1
View File
@@ -144,7 +144,7 @@ func (mm *MemoryManager) Fork(ctx context.Context) (*MemoryManager, error) {
mm2.activeMu.NestedLock(activeLockForked)
defer mm2.activeMu.NestedUnlock(activeLockForked)
if dontforks {
defer mm.pmas.MergeRange(mm.applicationAddrRange())
defer mm.pmas.MergeInsideRange(mm.applicationAddrRange())
}
srcvseg := mm.vmas.FirstSegment()
dstpgap := mm2.pmas.FirstGap()
+10 -10
View File
@@ -657,10 +657,10 @@ func (mm *MemoryManager) MProtect(addr hostarch.Addr, length uint64, realPerms h
mm.activeMu.Lock()
defer mm.activeMu.Unlock()
defer func() {
mm.vmas.MergeRange(ar)
mm.vmas.MergeAdjacent(ar)
mm.pmas.MergeRange(ar)
mm.pmas.MergeAdjacent(ar)
mm.vmas.MergeInsideRange(ar)
mm.vmas.MergeOutsideRange(ar)
mm.pmas.MergeInsideRange(ar)
mm.pmas.MergeOutsideRange(ar)
}()
pseg := mm.pmas.LowerBoundSegment(ar.Start)
var didUnmapAS bool
@@ -869,8 +869,8 @@ func (mm *MemoryManager) MLock(ctx context.Context, addr hostarch.Addr, length u
}
vseg, _ = vseg.NextNonEmpty()
}
mm.vmas.MergeRange(ar)
mm.vmas.MergeAdjacent(ar)
mm.vmas.MergeInsideRange(ar)
mm.vmas.MergeOutsideRange(ar)
if unmapped {
mm.mappingMu.Unlock()
return linuxerr.ENOMEM
@@ -1034,8 +1034,8 @@ func (mm *MemoryManager) SetNumaPolicy(addr hostarch.Addr, length uint64, policy
mm.mappingMu.Lock()
defer mm.mappingMu.Unlock()
defer func() {
mm.vmas.MergeRange(ar)
mm.vmas.MergeAdjacent(ar)
mm.vmas.MergeInsideRange(ar)
mm.vmas.MergeOutsideRange(ar)
}()
vseg := mm.vmas.LowerBoundSegment(ar.Start)
lastEnd := ar.Start
@@ -1067,8 +1067,8 @@ func (mm *MemoryManager) SetDontFork(addr hostarch.Addr, length uint64, dontfork
mm.mappingMu.Lock()
defer mm.mappingMu.Unlock()
defer func() {
mm.vmas.MergeRange(ar)
mm.vmas.MergeAdjacent(ar)
mm.vmas.MergeInsideRange(ar)
mm.vmas.MergeOutsideRange(ar)
}()
for vseg := mm.vmas.LowerBoundSegment(ar.Start); vseg.Ok() && vseg.Start() < ar.End; vseg = vseg.NextSegment() {
+16 -27
View File
@@ -576,13 +576,11 @@ func (f *MemoryFile) allocate(length uint64, opts *AllocOpts) (memmap.FileRange,
}
}
// Mark selected pages as in use.
if !f.usage.Add(fr, usageInfo{
f.usage.InsertRange(fr, usageInfo{
kind: opts.Kind,
refs: 1,
memCgID: opts.MemCgID,
}) {
panic(fmt.Sprintf("allocating %v: failed to insert into usage set:\n%v", fr, &f.usage))
}
})
return fr, nil
}
@@ -849,7 +847,7 @@ func (f *MemoryFile) markDecommitted(fr memmap.FileRange) {
defer f.mu.Unlock()
// Since we're changing the knownCommitted attribute, we need to merge
// across the entire range to ensure that the usage tree is minimal.
gap := f.usage.ApplyContiguous(fr, func(seg usageIterator) {
f.usage.MutateFullRange(fr, func(seg usageIterator) bool {
val := seg.ValuePtr()
if val.knownCommitted {
// Drop the usageExpected appropriately.
@@ -859,11 +857,8 @@ func (f *MemoryFile) markDecommitted(fr memmap.FileRange) {
val.knownCommitted = false
}
val.memCgID = 0
return true
})
if gap.Ok() {
panic(fmt.Sprintf("Decommit(%v): attempted to decommit unallocated pages %v:\n%v", fr, gap.Range(), &f.usage))
}
f.usage.MergeRange(fr)
}
// HasUniqueRef returns true if all pages in the given range have exactly one
@@ -875,16 +870,15 @@ func (f *MemoryFile) markDecommitted(fr memmap.FileRange) {
func (f *MemoryFile) HasUniqueRef(fr memmap.FileRange) bool {
f.mu.Lock()
defer f.mu.Unlock()
seg := f.usage.FindSegment(fr.Start)
for {
hasUniqueRef := true
f.usage.VisitFullRange(fr, func(seg usageIterator) bool {
if seg.ValuePtr().refs != 1 {
hasUniqueRef = false
return false
}
seg = seg.NextSegment()
if !seg.Ok() || fr.End <= seg.Start() {
return true
}
}
return true
})
return hasUniqueRef
}
// IncRef implements memmap.File.IncRef.
@@ -896,14 +890,10 @@ func (f *MemoryFile) IncRef(fr memmap.FileRange, memCgID uint32) {
f.mu.Lock()
defer f.mu.Unlock()
gap := f.usage.ApplyContiguous(fr, func(seg usageIterator) {
f.usage.MutateFullRange(fr, func(seg usageIterator) bool {
seg.ValuePtr().refs++
return true
})
if gap.Ok() {
panic(fmt.Sprintf("IncRef(%v): attempted to IncRef on unallocated pages %v:\n%v", fr, gap.Range(), &f.usage))
}
f.usage.MergeAdjacent(fr)
}
// DecRef implements memmap.File.DecRef.
@@ -917,15 +907,14 @@ func (f *MemoryFile) DecRef(fr memmap.FileRange) {
f.mu.Lock()
defer f.mu.Unlock()
for seg := f.usage.FindSegment(fr.Start); seg.Ok() && seg.Start() < fr.End; seg = seg.NextSegment() {
seg = f.usage.Isolate(seg, fr)
f.usage.MutateFullRange(fr, func(seg usageIterator) bool {
val := seg.ValuePtr()
if val.refs == 0 {
panic(fmt.Sprintf("DecRef(%v): 0 existing references on %v:\n%v", fr, seg.Range(), &f.usage))
}
val.refs--
if val.refs == 0 {
f.reclaim.Add(seg.Range(), reclaimSetValue{})
f.reclaim.InsertRange(seg.Range(), reclaimSetValue{})
freed = true
// Reclassify memory as System, until it's freed by the reclaim
// goroutine.
@@ -934,8 +923,8 @@ func (f *MemoryFile) DecRef(fr memmap.FileRange) {
}
val.kind = usage.System
}
}
f.usage.MergeAdjacent(fr)
return true
})
if freed {
f.reclaimable = true
+67 -94
View File
@@ -30,7 +30,7 @@ const (
func TestFindUnallocatedRange(t *testing.T) {
for _, test := range []struct {
name string
usage *usageSegmentDataSlices
usage []usageFlatSegment
fileSize int64
length uint64
alignment uint64
@@ -40,7 +40,6 @@ func TestFindUnallocatedRange(t *testing.T) {
}{
{
name: "Initial allocation succeeds",
usage: &usageSegmentDataSlices{},
length: page,
alignment: page,
direction: BottomUp,
@@ -48,7 +47,6 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Initial allocation succeeds",
usage: &usageSegmentDataSlices{},
length: page,
alignment: page,
direction: TopDown,
@@ -56,10 +54,8 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Allocation begins at start of file",
usage: &usageSegmentDataSlices{
Start: []uint64{page},
End: []uint64{2 * page},
Values: []usageInfo{{refs: 1}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
},
length: page,
alignment: page,
@@ -68,10 +64,8 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Allocation finds empty space at start of file",
usage: &usageSegmentDataSlices{
Start: []uint64{page},
End: []uint64{2 * page},
Values: []usageInfo{{refs: 1}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
},
fileSize: 2 * page,
length: page,
@@ -80,10 +74,8 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Allocation finds empty space at end of file",
usage: &usageSegmentDataSlices{
Start: []uint64{0},
End: []uint64{page},
Values: []usageInfo{{refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
},
fileSize: 2 * page,
length: page,
@@ -93,10 +85,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "In-use frames are not allocatable",
usage: &usageSegmentDataSlices{
Start: []uint64{0, page},
End: []uint64{page, 2 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{page, 2 * page, usageInfo{refs: 2}},
},
length: page,
alignment: page,
@@ -105,10 +96,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "In-use frames are not allocatable",
usage: &usageSegmentDataSlices{
Start: []uint64{0, page},
End: []uint64{page, 2 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{page, 2 * page, usageInfo{refs: 2}},
},
fileSize: 2 * page,
length: page,
@@ -118,10 +108,10 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Reclaimable frames are not allocatable",
usage: &usageSegmentDataSlices{
Start: []uint64{0, page, 2 * page},
End: []uint64{page, 2 * page, 3 * page},
Values: []usageInfo{{refs: 1}, {refs: 0}, {refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{page, 2 * page, usageInfo{refs: 0}},
{2 * page, 3 * page, usageInfo{refs: 1}},
},
length: page,
alignment: page,
@@ -130,10 +120,10 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Reclaimable frames are not allocatable",
usage: &usageSegmentDataSlices{
Start: []uint64{0, page, 2 * page},
End: []uint64{page, 2 * page, 3 * page},
Values: []usageInfo{{refs: 1}, {refs: 0}, {refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{page, 2 * page, usageInfo{refs: 0}},
{2 * page, 3 * page, usageInfo{refs: 1}},
},
fileSize: 3 * page,
length: page,
@@ -143,10 +133,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Gaps between in-use frames are allocatable",
usage: &usageSegmentDataSlices{
Start: []uint64{0, 2 * page},
End: []uint64{page, 3 * page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{2 * page, 3 * page, usageInfo{refs: 1}},
},
length: page,
alignment: page,
@@ -155,10 +144,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Gaps between in-use frames are allocatable",
usage: &usageSegmentDataSlices{
Start: []uint64{0, 2 * page},
End: []uint64{page, 3 * page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{2 * page, 3 * page, usageInfo{refs: 1}},
},
fileSize: 3 * page,
length: page,
@@ -168,10 +156,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Inadequately-sized gaps are rejected",
usage: &usageSegmentDataSlices{
Start: []uint64{0, 2 * page},
End: []uint64{page, 3 * page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{2 * page, 3 * page, usageInfo{refs: 1}},
},
length: 2 * page,
alignment: page,
@@ -180,10 +167,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Inadequately-sized gaps are rejected",
usage: &usageSegmentDataSlices{
Start: []uint64{0, 2 * page},
End: []uint64{page, 3 * page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
{2 * page, 3 * page, usageInfo{refs: 1}},
},
fileSize: 3 * page,
length: 2 * page,
@@ -193,12 +179,11 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Alignment is honored at end of file",
usage: &usageSegmentDataSlices{
Start: []uint64{0, hugepage + page},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
// Hugepage-sized gap here that shouldn't be allocated from
// since it's incorrectly aligned.
End: []uint64{page, hugepage + 2*page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
{hugepage + page, hugepage + 2*page, usageInfo{refs: 1}},
},
length: hugepage,
alignment: hugepage,
@@ -207,12 +192,11 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Alignment is honored at end of file",
usage: &usageSegmentDataSlices{
Start: []uint64{0, hugepage + page},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
// Hugepage-sized gap here that shouldn't be allocated from
// since it's incorrectly aligned.
End: []uint64{page, hugepage + 2*page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
{hugepage + page, hugepage + 2*page, usageInfo{refs: 1}},
},
fileSize: hugepage + 2*page,
length: hugepage,
@@ -222,11 +206,10 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Alignment is honored before end of file",
usage: &usageSegmentDataSlices{
Start: []uint64{0, 2*hugepage + page},
usage: []usageFlatSegment{
{0, page, usageInfo{refs: 1}},
// Page will need to be shifted down from top.
End: []uint64{page, 2*hugepage + 2*page},
Values: []usageInfo{{refs: 1}, {refs: 1}},
{2*hugepage + page, 2*hugepage + 2*page, usageInfo{refs: 1}},
},
fileSize: 2*hugepage + 2*page,
length: hugepage,
@@ -236,7 +219,6 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Allocation doubles file size more than once if necessary",
usage: &usageSegmentDataSlices{},
fileSize: page,
length: 4 * page,
alignment: page,
@@ -245,7 +227,6 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Allocation doubles file size more than once if necessary",
usage: &usageSegmentDataSlices{},
fileSize: page,
length: 4 * page,
alignment: page,
@@ -254,10 +235,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Allocations are compact if possible",
usage: &usageSegmentDataSlices{
Start: []uint64{page, 3 * page},
End: []uint64{2 * page, 4 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
{3 * page, 4 * page, usageInfo{refs: 2}},
},
fileSize: 4 * page,
length: page,
@@ -267,10 +247,10 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Top-down allocation within one gap",
usage: &usageSegmentDataSlices{
Start: []uint64{page, 4 * page, 7 * page},
End: []uint64{2 * page, 5 * page, 8 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}, {refs: 1}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
{4 * page, 5 * page, usageInfo{refs: 2}},
{7 * page, 8 * page, usageInfo{refs: 1}},
},
fileSize: 8 * page,
length: page,
@@ -280,10 +260,10 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Top-down allocation between multiple gaps",
usage: &usageSegmentDataSlices{
Start: []uint64{page, 3 * page, 5 * page},
End: []uint64{2 * page, 4 * page, 6 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}, {refs: 1}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
{3 * page, 4 * page, usageInfo{refs: 2}},
{5 * page, 6 * page, usageInfo{refs: 1}},
},
fileSize: 6 * page,
length: page,
@@ -293,10 +273,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Top-down allocation with large top gap",
usage: &usageSegmentDataSlices{
Start: []uint64{page, 3 * page},
End: []uint64{2 * page, 4 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
{3 * page, 4 * page, usageInfo{refs: 2}},
},
fileSize: 8 * page,
length: page,
@@ -306,10 +285,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Gaps found with possible overflow",
usage: &usageSegmentDataSlices{
Start: []uint64{page, topPage - page},
End: []uint64{2 * page, topPage},
Values: []usageInfo{{refs: 1}, {refs: 1}},
usage: []usageFlatSegment{
{page, 2 * page, usageInfo{refs: 1}},
{topPage - page, topPage, usageInfo{refs: 1}},
},
fileSize: topPage,
length: page,
@@ -319,10 +297,8 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Overflow detected",
usage: &usageSegmentDataSlices{
Start: []uint64{page},
End: []uint64{topPage},
Values: []usageInfo{{refs: 1}},
usage: []usageFlatSegment{
{page, topPage, usageInfo{refs: 1}},
},
fileSize: topPage,
length: 2 * page,
@@ -332,10 +308,8 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "Overflow detected",
usage: &usageSegmentDataSlices{
Start: []uint64{page},
End: []uint64{topPage},
Values: []usageInfo{{refs: 1}},
usage: []usageFlatSegment{
{page, topPage, usageInfo{refs: 1}},
},
fileSize: topPage,
length: 2 * page,
@@ -345,10 +319,9 @@ func TestFindUnallocatedRange(t *testing.T) {
},
{
name: "start may be in the middle of segment",
usage: &usageSegmentDataSlices{
Start: []uint64{0, 3 * page},
End: []uint64{2 * page, 4 * page},
Values: []usageInfo{{refs: 1}, {refs: 2}},
usage: []usageFlatSegment{
{0, 2 * page, usageInfo{refs: 1}},
{3 * page, 4 * page, usageInfo{refs: 2}},
},
length: page,
alignment: page,
@@ -359,7 +332,7 @@ func TestFindUnallocatedRange(t *testing.T) {
name := fmt.Sprintf("%s (%v)", test.name, test.direction)
t.Run(name, func(t *testing.T) {
f := MemoryFile{fileSize: test.fileSize}
if err := f.usage.ImportSortedSlices(test.usage); err != nil {
if err := f.usage.ImportSlice(test.usage); err != nil {
t.Fatalf("Failed to initialize usage from %v: %v", test.usage, err)
}
if fr, ok := f.findAvailableRange(test.length, test.alignment, test.direction); ok {