diff options
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go | 6 | ||||
-rw-r--r-- | cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go | 61 | ||||
-rw-r--r-- | lib/btrfsutil/rebuilt_forrest.go | 11 | ||||
-rw-r--r-- | lib/btrfsutil/rebuilt_tree.go | 76 |
4 files changed, 98 insertions, 56 deletions
diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go index 2160969..427070a 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild.go @@ -198,11 +198,13 @@ func (o *rebuilder) processAddedItemQueue(ctx context.Context) error { ctx := dlog.WithField(ctx, "btrfs.inspect.rebuild-trees.rebuild.settle.item", key) tree := o.rebuilt.RebuiltTree(ctx, key.TreeID) - incPtr, ok := tree.RebuiltItems(ctx).Load(key.Key) + incPtr, ok := tree.RebuiltAcquireItems(ctx).Load(key.Key) + tree.RebuiltReleaseItems() if !ok { panic(fmt.Errorf("should not happen: failed to load already-added item: %v", key)) } - excPtr, ok := tree.RebuiltPotentialItems(ctx).Load(key.Key) + excPtr, ok := tree.RebuiltAcquirePotentialItems(ctx).Load(key.Key) + tree.RebuiltReleasePotentialItems() if ok && tree.RebuiltShouldReplace(incPtr.Node, excPtr.Node) { wantKey := wantWithTree{ TreeID: key.TreeID, diff --git a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go index eff2a83..0526e93 100644 --- a/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go +++ b/cmd/btrfs-rec/inspect/rebuildtrees/rebuild_wantcb.go @@ -43,18 +43,22 @@ func (o graphCallbacks) Want(ctx context.Context, reason string, treeID btrfspri } func (o *rebuilder) _want(ctx context.Context, wantKey wantWithTree) (key btrfsprim.Key, ok bool) { - if o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) == nil { + tree := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) + if tree == nil { o.enqueueRetry(wantKey.TreeID) return btrfsprim.Key{}, false } + tgt := wantKey.Key.Key() + // check if we already have it - tgt := wantKey.Key.Key() - if key, _, ok := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltItems(ctx).Search(func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { + key, _, ok = tree.RebuiltAcquireItems(ctx).Search(func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { key.Offset = 0 return tgt.Compare(key) - }); ok { + }) + tree.RebuiltReleaseItems() + if ok { return key, true } @@ -64,15 +68,16 @@ func (o *rebuilder) _want(ctx context.Context, wantKey wantWithTree) (key btrfsp return btrfsprim.Key{}, false } wants := make(containers.Set[btrfsvol.LogicalAddr]) - o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltPotentialItems(ctx).Subrange( + tree.RebuiltAcquirePotentialItems(ctx).Subrange( func(k btrfsprim.Key, _ btrfsutil.ItemPtr) int { k.Offset = 0 return tgt.Compare(k) }, func(_ btrfsprim.Key, v btrfsutil.ItemPtr) bool { - wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltLeafToRoots(ctx, v.Node)) + wants.InsertFrom(tree.RebuiltLeafToRoots(ctx, v.Node)) return true }) + tree.RebuiltReleasePotentialItems() o.wantAugment(ctx, wantKey, wants) return btrfsprim.Key{}, false } @@ -93,15 +98,19 @@ func (o graphCallbacks) WantOff(ctx context.Context, reason string, treeID btrfs } func (o *rebuilder) _wantOff(ctx context.Context, wantKey wantWithTree) (ok bool) { - if o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) == nil { + tree := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID) + if tree == nil { o.enqueueRetry(wantKey.TreeID) return false } + tgt := wantKey.Key.Key() + // check if we already have it - tgt := wantKey.Key.Key() - if _, ok := o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltItems(ctx).Load(tgt); ok { + _, ok = tree.RebuiltAcquireItems(ctx).Load(tgt) + tree.RebuiltReleaseItems() + if ok { return true } @@ -111,12 +120,13 @@ func (o *rebuilder) _wantOff(ctx context.Context, wantKey wantWithTree) (ok bool return false } wants := make(containers.Set[btrfsvol.LogicalAddr]) - o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltPotentialItems(ctx).Subrange( + tree.RebuiltAcquirePotentialItems(ctx).Subrange( func(k btrfsprim.Key, _ btrfsutil.ItemPtr) int { return tgt.Compare(k) }, func(_ btrfsprim.Key, v btrfsutil.ItemPtr) bool { - wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, wantKey.TreeID).RebuiltLeafToRoots(ctx, v.Node)) + wants.InsertFrom(tree.RebuiltLeafToRoots(ctx, v.Node)) return true }) + tree.RebuiltReleasePotentialItems() o.wantAugment(ctx, wantKey, wants) return false } @@ -134,16 +144,18 @@ func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID } ctx = withWant(ctx, logFieldItemWant, reason, wantKey) - if o.rebuilt.RebuiltTree(ctx, treeID) == nil { + tree := o.rebuilt.RebuiltTree(ctx, treeID) + if tree == nil { o.enqueueRetry(treeID) return } + tgt := wantKey.Key.Key() + // check if we already have it - tgt := wantKey.Key.Key() found := false - o.rebuilt.RebuiltTree(ctx, treeID).RebuiltItems(ctx).Subrange( + tree.RebuiltAcquireItems(ctx).Subrange( func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { key.Offset = 0 return tgt.Compare(key) @@ -154,6 +166,7 @@ func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID } return !found }) + tree.RebuiltReleaseItems() if found { return } @@ -164,17 +177,18 @@ func (o graphCallbacks) WantDirIndex(ctx context.Context, reason string, treeID return } wants := make(containers.Set[btrfsvol.LogicalAddr]) - o.rebuilt.RebuiltTree(ctx, treeID).RebuiltPotentialItems(ctx).Subrange( + tree.RebuiltAcquirePotentialItems(ctx).Subrange( func(key btrfsprim.Key, _ btrfsutil.ItemPtr) int { key.Offset = 0 return tgt.Compare(key) }, func(_ btrfsprim.Key, ptr btrfsutil.ItemPtr) bool { if itemName, ok := o.scan.Names[ptr]; ok && bytes.Equal(itemName, name) { - wants.InsertFrom(o.rebuilt.RebuiltTree(ctx, treeID).RebuiltLeafToRoots(ctx, ptr.Node)) + wants.InsertFrom(tree.RebuiltLeafToRoots(ctx, ptr.Node)) } return true }) + tree.RebuiltReleasePotentialItems() o.wantAugment(ctx, wantKey, wants) } @@ -259,7 +273,8 @@ func (o graphCallbacks) _wantRange( ctx = withWant(ctx, logFieldItemWant, reason, wantKey) wantKey.Key.OffsetType = offsetRange - if o.rebuilt.RebuiltTree(ctx, treeID) == nil { + tree := o.rebuilt.RebuiltTree(ctx, treeID) + if tree == nil { o.enqueueRetry(treeID) return } @@ -275,7 +290,7 @@ func (o graphCallbacks) _wantRange( }) o._walkRange( ctx, - o.rebuilt.RebuiltTree(ctx, treeID).RebuiltItems(ctx), + tree.RebuiltAcquireItems(ctx), treeID, objID, typ, beg, end, func(runKey btrfsprim.Key, _ btrfsutil.ItemPtr, runBeg, runEnd uint64) { var overlappingGaps []*containers.RBNode[gap] @@ -315,12 +330,13 @@ func (o graphCallbacks) _wantRange( }) } }) + tree.RebuiltReleaseItems() // Step 2: Fill each gap. if gaps.Len() == 0 { return } - potentialItems := o.rebuilt.RebuiltTree(ctx, treeID).RebuiltPotentialItems(ctx) + potentialItems := tree.RebuiltAcquirePotentialItems(ctx) gaps.Range(func(rbNode *containers.RBNode[gap]) bool { gap := rbNode.Value last := gap.Beg @@ -340,7 +356,7 @@ func (o graphCallbacks) _wantRange( wantKey.Key.OffsetLow = gap.Beg wantKey.Key.OffsetHigh = gap.End wantCtx := withWant(ctx, logFieldItemWant, reason, wantKey) - o.wantAugment(wantCtx, wantKey, o.rebuilt.RebuiltTree(wantCtx, treeID).RebuiltLeafToRoots(wantCtx, v.Node)) + o.wantAugment(wantCtx, wantKey, tree.RebuiltLeafToRoots(wantCtx, v.Node)) last = runEnd }) if last < gap.End { @@ -352,6 +368,7 @@ func (o graphCallbacks) _wantRange( } return true }) + tree.RebuiltReleasePotentialItems() } // WantCSum implements btrfscheck.GraphCallbacks. @@ -372,7 +389,9 @@ func (o graphCallbacks) WantCSum(ctx context.Context, reason string, inodeTree, o.enqueueRetry(inodeTree) return } - inodePtr, ok := o.rebuilt.RebuiltTree(inodeCtx, inodeTree).RebuiltItems(inodeCtx).Load(inodeWant.Key.Key()) + tree := o.rebuilt.RebuiltTree(inodeCtx, inodeTree) + inodePtr, ok := tree.RebuiltAcquireItems(inodeCtx).Load(inodeWant.Key.Key()) + tree.RebuiltReleaseItems() if !ok { panic(fmt.Errorf("should not happen: could not load key: %v", inodeWant)) } diff --git a/lib/btrfsutil/rebuilt_forrest.go b/lib/btrfsutil/rebuilt_forrest.go index fcfb353..d6e1fbb 100644 --- a/lib/btrfsutil/rebuilt_forrest.go +++ b/lib/btrfsutil/rebuilt_forrest.go @@ -43,10 +43,11 @@ func (cb noopRebuiltForrestCallbacks) LookupRoot(ctx context.Context, tree btrfs ObjectID: tree, ItemType: btrfsprim.ROOT_ITEM_KEY, } - itemKey, itemPtr, ok := rootTree.RebuiltItems(ctx).Search(func(key btrfsprim.Key, _ ItemPtr) int { + itemKey, itemPtr, ok := rootTree.RebuiltAcquireItems(ctx).Search(func(key btrfsprim.Key, _ ItemPtr) int { key.Offset = 0 return tgt.Compare(key) }) + rootTree.RebuiltReleaseItems() if !ok { return 0, btrfsitem.Root{}, false } @@ -70,7 +71,8 @@ func (cb noopRebuiltForrestCallbacks) LookupUUID(ctx context.Context, uuid btrfs return 0, false } tgt := btrfsitem.UUIDToKey(uuid) - itemPtr, ok := uuidTree.RebuiltItems(ctx).Load(tgt) + itemPtr, ok := uuidTree.RebuiltAcquireItems(ctx).Load(tgt) + uuidTree.RebuiltReleaseItems() if !ok { return 0, false } @@ -118,8 +120,9 @@ func (cb noopRebuiltForrestCallbacks) LookupUUID(ctx context.Context, uuid btrfs // - it provides several RebuiltTree methods that provide advice on // what roots should be added to a tree in order to repair it: // -// .RebuiltItems() and RebuiltPotentialItems() to compare what's -// in the tree and what could be in the tree. +// .RebuiltAcquireItems()/.RebuiltReleaseItems() and +// .RebuiltAcquirePotentialItems()/.RebuiltReleasePotentialItems() +// to compare what's in the tree and what could be in the tree. // // .RebuiltLeafToRoots() to map potential items to things that can // be passed to .RebuiltAddRoot(). diff --git a/lib/btrfsutil/rebuilt_tree.go b/lib/btrfsutil/rebuilt_tree.go index 016bb1d..61bbb5e 100644 --- a/lib/btrfsutil/rebuilt_tree.go +++ b/lib/btrfsutil/rebuilt_tree.go @@ -37,19 +37,21 @@ type RebuiltTree struct { // derived from tree.Roots, which is why it's OK if they get // evicted. // - // 1. tree.leafToRoots() = tree.forrest.leafs.Acquire(tree.ID) - // 2. tree.RebuiltItems() = tree.forrest.incItems.Acquire(tree.ID) - // 3. tree.RebuiltPotentialItems() = tree.forrest.excItems.Acquire(tree.ID) + // 1. tree.acquireLeafToRoots() = tree.forrest.leafs.Acquire(tree.ID) + // 2. tree.RebuiltAcquireItems() = tree.forrest.incItems.Acquire(tree.ID) + // 3. tree.RebuiltAcquirePotentialItems() = tree.forrest.excItems.Acquire(tree.ID) } -// evictable member 1: .leafToRoots() ////////////////////////////////////////////////////////////////////////////////// +// evictable member 1: .acquireLeafToRoots() /////////////////////////////////////////////////////////////////////////// -// leafToRoots returns all leafs (lvl=0) in the filesystem that pass -// .isOwnerOK, whether or not they're in the tree. -func (tree *RebuiltTree) leafToRoots(ctx context.Context) map[btrfsvol.LogicalAddr]containers.Set[btrfsvol.LogicalAddr] { - ret := *tree.forrest.leafs.Acquire(ctx, tree.ID) +// acquireLeafToRoots returns all leafs (lvl=0) in the filesystem that +// pass .isOwnerOK, whether or not they're in the tree. +func (tree *RebuiltTree) acquireLeafToRoots(ctx context.Context) map[btrfsvol.LogicalAddr]containers.Set[btrfsvol.LogicalAddr] { + return *tree.forrest.leafs.Acquire(ctx, tree.ID) +} + +func (tree *RebuiltTree) releaseLeafToRoots() { tree.forrest.leafs.Release(tree.ID) - return ret } func (tree *RebuiltTree) uncachedLeafToRoots(ctx context.Context) map[btrfsvol.LogicalAddr]containers.Set[btrfsvol.LogicalAddr] { @@ -133,35 +135,48 @@ func (tree *RebuiltTree) isOwnerOK(owner btrfsprim.ObjID, gen btrfsprim.Generati } } -// evictable members 2 and 3: .RebuiltItems() and .RebuiltPotentialItems() ///////////////////////////////////////////// +// evictable members 2 and 3: .Rebuilt{Acquire,Release}{Potential,}Items() ///////////////////////////////////////////// -// RebuiltItems returns a map of the items contained in this tree. +// RebuiltAcquireItems returns a map of the items contained in this +// tree. // // Do not mutate the returned map; it is a pointer to the // RebuiltTree's internal map! -func (tree *RebuiltTree) RebuiltItems(ctx context.Context) *containers.SortedMap[btrfsprim.Key, ItemPtr] { - ret := *tree.forrest.incItems.Acquire(ctx, tree.ID) +// +// When done with the map, call .RebuiltReleaseItems(). +func (tree *RebuiltTree) RebuiltAcquireItems(ctx context.Context) *containers.SortedMap[btrfsprim.Key, ItemPtr] { + return tree.forrest.incItems.Acquire(ctx, tree.ID) +} + +// RebuiltReleaseItems releases resources after a call to +// .RebuiltAcquireItems(). +func (tree *RebuiltTree) RebuiltReleaseItems() { tree.forrest.incItems.Release(tree.ID) - return ret } -// RebuiltPotentialItems returns a map of items that could be added to -// this tree with .RebuiltAddRoot(). +// RebuiltAcquirePotentialItems returns a map of items that could be +// added to this tree with .RebuiltAddRoot(). // // Do not mutate the returned map; it is a pointer to the // RebuiltTree's internal map! -func (tree *RebuiltTree) RebuiltPotentialItems(ctx context.Context) *containers.SortedMap[btrfsprim.Key, ItemPtr] { - ret := *tree.forrest.excItems.Acquire(ctx, tree.ID) +// +// When done with the map, call .RebuiltReleasePotentialItems(). +func (tree *RebuiltTree) RebuiltAcquirePotentialItems(ctx context.Context) *containers.SortedMap[btrfsprim.Key, ItemPtr] { + return tree.forrest.excItems.Acquire(ctx, tree.ID) +} + +// RebuiltReleasePotentialItems releases resources after a call to +// .RebuiltAcquirePotentialItems(). +func (tree *RebuiltTree) RebuiltReleasePotentialItems() { tree.forrest.excItems.Release(tree.ID) - return ret } -func (tree *RebuiltTree) uncachedIncItems(ctx context.Context) *containers.SortedMap[btrfsprim.Key, ItemPtr] { +func (tree *RebuiltTree) uncachedIncItems(ctx context.Context) containers.SortedMap[btrfsprim.Key, ItemPtr] { ctx = dlog.WithField(ctx, "btrfs.util.rebuilt-tree.index-inc-items", fmt.Sprintf("tree=%v", tree.ID)) return tree.items(ctx, tree.Roots.HasAny) } -func (tree *RebuiltTree) uncachedExcItems(ctx context.Context) *containers.SortedMap[btrfsprim.Key, ItemPtr] { +func (tree *RebuiltTree) uncachedExcItems(ctx context.Context) containers.SortedMap[btrfsprim.Key, ItemPtr] { ctx = dlog.WithField(ctx, "btrfs.util.rebuilt-tree.index-exc-items", fmt.Sprintf("tree=%v", tree.ID)) return tree.items(ctx, func(roots containers.Set[btrfsvol.LogicalAddr]) bool { @@ -169,7 +184,7 @@ func (tree *RebuiltTree) uncachedExcItems(ctx context.Context) *containers.Sorte }) } -type itemIndex = *containers.SortedMap[btrfsprim.Key, ItemPtr] +type itemIndex = containers.SortedMap[btrfsprim.Key, ItemPtr] type itemStats struct { Leafs textui.Portion[int] @@ -182,23 +197,24 @@ func (s itemStats) String() string { s.Leafs, s.NumItems, s.NumDups) } -func (tree *RebuiltTree) items(ctx context.Context, leafFn func(roots containers.Set[btrfsvol.LogicalAddr]) bool) *containers.SortedMap[btrfsprim.Key, ItemPtr] { +func (tree *RebuiltTree) items(ctx context.Context, leafFn func(roots containers.Set[btrfsvol.LogicalAddr]) bool) containers.SortedMap[btrfsprim.Key, ItemPtr] { tree.mu.RLock() defer tree.mu.RUnlock() var leafs []btrfsvol.LogicalAddr - for leaf, roots := range tree.leafToRoots(ctx) { + for leaf, roots := range tree.acquireLeafToRoots(ctx) { if leafFn(roots) { leafs = append(leafs, leaf) } } + tree.releaseLeafToRoots() slices.Sort(leafs) var stats itemStats stats.Leafs.D = len(leafs) progressWriter := textui.NewProgress[itemStats](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second)) - index := new(containers.SortedMap[btrfsprim.Key, ItemPtr]) + var index containers.SortedMap[btrfsprim.Key, ItemPtr] for i, leaf := range leafs { stats.Leafs.N = i progressWriter.Set(stats) @@ -282,9 +298,8 @@ func (tree *RebuiltTree) RebuiltAddRoot(ctx context.Context, rootNode btrfsvol.L ctx = dlog.WithField(ctx, "btrfs.util.rebuilt-tree.add-root", fmt.Sprintf("tree=%v rootNode=%v", tree.ID, rootNode)) dlog.Info(ctx, "adding root...") - leafToRoots := tree.leafToRoots(ctx) - var stats rootStats + leafToRoots := tree.acquireLeafToRoots(ctx) stats.Leafs.D = len(leafToRoots) progressWriter := textui.NewProgress[rootStats](ctx, dlog.LogLevelInfo, textui.Tunable(1*time.Second)) for i, leaf := range maps.SortedKeys(leafToRoots) { @@ -305,6 +320,7 @@ func (tree *RebuiltTree) RebuiltAddRoot(ctx context.Context, rootNode btrfsvol.L } } stats.Leafs.N = len(leafToRoots) + tree.releaseLeafToRoots() progressWriter.Set(stats) progressWriter.Done() @@ -335,7 +351,8 @@ func (tree *RebuiltTree) RebuiltCOWDistance(parentID btrfsprim.ObjID) (dist int, // ReadItem reads an item from a tree. func (tree *RebuiltTree) ReadItem(ctx context.Context, key btrfsprim.Key) btrfsitem.Item { - ptr, ok := tree.RebuiltItems(ctx).Load(key) + ptr, ok := tree.RebuiltAcquireItems(ctx).Load(key) + tree.RebuiltReleaseItems() if !ok { panic(fmt.Errorf("should not happen: btrfsutil.RebuiltTree.ReadItem called for not-included key: %v", key)) } @@ -352,13 +369,14 @@ func (tree *RebuiltTree) RebuiltLeafToRoots(ctx context.Context, leaf btrfsvol.L tree.mu.RLock() defer tree.mu.RUnlock() ret := make(containers.Set[btrfsvol.LogicalAddr]) - for root := range tree.leafToRoots(ctx)[leaf] { + for root := range tree.acquireLeafToRoots(ctx)[leaf] { if tree.Roots.Has(root) { panic(fmt.Errorf("should not happen: (tree=%v).RebuiltLeafToRoots(leaf=%v): tree contains root=%v but not leaf", tree.ID, leaf, root)) } ret.Insert(root) } + tree.releaseLeafToRoots() if len(ret) == 0 { return nil } |