From c95ae0b25133dc068e3d471a4a2c798be45b7930 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 24 Aug 2022 09:27:49 -0600 Subject: wip: work on rebuildmappings --- .../btrfsinspect/rebuildmappings/blockgroups.go | 11 +- .../btrfsinspect/rebuildmappings/csumitems.go | 119 --------- .../btrfsinspect/rebuildmappings/logicalsums.go | 162 ++++++++++++ .../btrfsinspect/rebuildmappings/matchsums.go | 292 +++++++++++++++++++++ .../btrfsinspect/rebuildmappings/physicalsums.go | 89 +++++++ .../rebuildmappings/rebuildmappings.go | 60 ++++- .../btrfsinspect/rebuildmappings/scan.go | 292 --------------------- .../rebuildmappings/unmappedregions.go | 80 ------ 8 files changed, 599 insertions(+), 506 deletions(-) delete mode 100644 lib/btrfsprogs/btrfsinspect/rebuildmappings/csumitems.go create mode 100644 lib/btrfsprogs/btrfsinspect/rebuildmappings/logicalsums.go create mode 100644 lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go create mode 100644 lib/btrfsprogs/btrfsinspect/rebuildmappings/physicalsums.go delete mode 100644 lib/btrfsprogs/btrfsinspect/rebuildmappings/scan.go delete mode 100644 lib/btrfsprogs/btrfsinspect/rebuildmappings/unmappedregions.go (limited to 'lib/btrfsprogs/btrfsinspect/rebuildmappings') diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/blockgroups.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/blockgroups.go index bd390e0..91fecbc 100644 --- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/blockgroups.go +++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/blockgroups.go @@ -19,7 +19,7 @@ type BlockGroup struct { Flags btrfsvol.BlockGroupFlags } -func DedupBlockGroups(scanResults btrfsinspect.ScanDevicesResult) ([]BlockGroup, error) { +func DedupBlockGroups(scanResults btrfsinspect.ScanDevicesResult) (map[btrfsvol.LogicalAddr]BlockGroup, error) { // Dedup bgsSet := make(map[BlockGroup]struct{}) for _, devResults := range scanResults { @@ -47,6 +47,11 @@ func DedupBlockGroups(scanResults btrfsinspect.ScanDevicesResult) ([]BlockGroup, pos = bg.LAddr.Add(bg.Size) } - // Return - return bgsOrdered, nil + // Return. We return a map instead of a slice in order to + // facilitate easy deletes. + bgsMap := make(map[btrfsvol.LogicalAddr]BlockGroup, len(bgsSet)) + for bg := range bgsSet { + bgsMap[bg.LAddr] = bg + } + return bgsMap, nil } diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/csumitems.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/csumitems.go deleted file mode 100644 index eaa6f06..0000000 --- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/csumitems.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package rebuildmappings - -import ( - "context" - "strings" - - "github.com/datawire/dlib/dlog" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" - "git.lukeshu.com/btrfs-progs-ng/lib/maps" - "git.lukeshu.com/btrfs-progs-ng/lib/slices" -) - -func MapLogicalSums(ctx context.Context, scanResults btrfsinspect.ScanDevicesResult) btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] { - dlog.Info(ctx, "Mapping the logical address space...") - type record struct { - Gen btrfs.Generation - Sum btrfssum.ShortSum - } - addrspace := make(map[btrfsvol.LogicalAddr]record) - var sumSize int - for _, devResults := range scanResults { - sumSize = devResults.Checksums.ChecksumSize - for _, sumItem := range devResults.FoundExtentCSums { - _ = sumItem.Sums.Walk(ctx, func(pos btrfsvol.LogicalAddr, sum btrfssum.ShortSum) error { - new := record{ - Gen: sumItem.Generation, - Sum: sum, - } - if old, ok := addrspace[pos]; ok { - switch { - case old.Gen > new.Gen: - // do nothing - case old.Gen < new.Gen: - addrspace[pos] = new - case old.Gen == new.Gen: - if old != new { - dlog.Errorf(ctx, "mismatch of laddr=%v sum: %v != %v", pos, old, new) - } - } - } else { - addrspace[pos] = new - } - return nil - }) - } - } - dlog.Info(ctx, "... done mapping") - - var flattened btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] - if len(addrspace) == 0 { - return flattened - } - - dlog.Info(ctx, "Flattening the map ...") - var curAddr btrfsvol.LogicalAddr - var curSums strings.Builder - for _, laddr := range maps.SortedKeys(addrspace) { - if laddr != curAddr+(btrfsvol.LogicalAddr(curSums.Len()/sumSize)*btrfssum.BlockSize) { - if curSums.Len() > 0 { - flattened.Runs = append(flattened.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{ - ChecksumSize: sumSize, - Addr: curAddr, - Sums: btrfssum.ShortSum(curSums.String()), - }) - } - curAddr = laddr - curSums.Reset() - } - curSums.WriteString(string(addrspace[laddr].Sum)) - } - if curSums.Len() > 0 { - flattened.Runs = append(flattened.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{ - ChecksumSize: sumSize, - Addr: curAddr, - Sums: btrfssum.ShortSum(curSums.String()), - }) - } - flattened.Addr = flattened.Runs[0].Addr - last := flattened.Runs[len(flattened.Runs)-1] - end := last.Addr.Add(last.Size()) - flattened.Size = end.Sub(flattened.Addr) - dlog.Info(ctx, "... done flattening") - - return flattened -} - -func SumsForLogicalRegion(sums btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr], beg btrfsvol.LogicalAddr, size btrfsvol.AddrDelta) btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] { - runs := btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]{ - Addr: beg, - Size: size, - } - for laddr := beg; laddr < beg.Add(size); { - run, next, ok := sums.RunForAddr(laddr) - if !ok { - laddr = next - continue - } - off := int((laddr-run.Addr)/btrfssum.BlockSize) * run.ChecksumSize - deltaAddr := slices.Min[btrfsvol.AddrDelta]( - size-laddr.Sub(beg), - btrfsvol.AddrDelta((len(run.Sums)-off)/run.ChecksumSize)*btrfssum.BlockSize) - deltaOff := int(deltaAddr/btrfssum.BlockSize) * run.ChecksumSize - runs.Runs = append(runs.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{ - ChecksumSize: run.ChecksumSize, - Addr: laddr, - Sums: run.Sums[off : off+deltaOff], - }) - laddr = laddr.Add(deltaAddr) - } - return runs -} diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/logicalsums.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/logicalsums.go new file mode 100644 index 0000000..3430502 --- /dev/null +++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/logicalsums.go @@ -0,0 +1,162 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package rebuildmappings + +import ( + "context" + "strings" + + "github.com/datawire/dlib/dlog" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" + "git.lukeshu.com/btrfs-progs-ng/lib/maps" + "git.lukeshu.com/btrfs-progs-ng/lib/slices" +) + +func ExtractLogicalSums(ctx context.Context, scanResults btrfsinspect.ScanDevicesResult) btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] { + dlog.Info(ctx, "... Mapping the logical address space...") + type record struct { + Gen btrfs.Generation + Sum btrfssum.ShortSum + } + addrspace := make(map[btrfsvol.LogicalAddr]record) + var sumSize int + for _, devResults := range scanResults { + sumSize = devResults.Checksums.ChecksumSize + for _, sumItem := range devResults.FoundExtentCSums { + _ = sumItem.Sums.Walk(ctx, func(pos btrfsvol.LogicalAddr, sum btrfssum.ShortSum) error { + new := record{ + Gen: sumItem.Generation, + Sum: sum, + } + if old, ok := addrspace[pos]; ok { + switch { + case old.Gen > new.Gen: + // do nothing + case old.Gen < new.Gen: + addrspace[pos] = new + case old.Gen == new.Gen: + if old != new { + dlog.Errorf(ctx, "mismatch of laddr=%v sum: %v != %v", pos, old, new) + } + } + } else { + addrspace[pos] = new + } + return nil + }) + } + } + dlog.Info(ctx, "... ... done mapping") + + var flattened btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] + if len(addrspace) == 0 { + return flattened + } + + dlog.Info(ctx, "... Flattening the map ...") + var curAddr btrfsvol.LogicalAddr + var curSums strings.Builder + for _, laddr := range maps.SortedKeys(addrspace) { + if laddr != curAddr+(btrfsvol.LogicalAddr(curSums.Len()/sumSize)*btrfssum.BlockSize) { + if curSums.Len() > 0 { + flattened.Runs = append(flattened.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: sumSize, + Addr: curAddr, + Sums: btrfssum.ShortSum(curSums.String()), + }) + } + curAddr = laddr + curSums.Reset() + } + curSums.WriteString(string(addrspace[laddr].Sum)) + } + if curSums.Len() > 0 { + flattened.Runs = append(flattened.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: sumSize, + Addr: curAddr, + Sums: btrfssum.ShortSum(curSums.String()), + }) + } + flattened.Addr = flattened.Runs[0].Addr + last := flattened.Runs[len(flattened.Runs)-1] + end := last.Addr.Add(last.Size()) + flattened.Size = end.Sub(flattened.Addr) + dlog.Info(ctx, "... ... done flattening") + + return flattened +} + +func ListUnmappedLogicalRegions(fs *btrfs.FS, logicalSums btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]) []btrfssum.SumRun[btrfsvol.LogicalAddr] { + // There are a lot of ways this algorithm could be made + // faster. + var ret []btrfssum.SumRun[btrfsvol.LogicalAddr] + var cur struct { + Addr btrfsvol.LogicalAddr + Size btrfsvol.AddrDelta + } + for _, run := range logicalSums.Runs { + for addr := run.Addr; addr < run.Addr.Add(run.Size()); addr += btrfssum.BlockSize { + if _, maxlen := fs.LV.Resolve(addr); maxlen < btrfssum.BlockSize { + if cur.Size == 0 { + cur.Addr = addr + cur.Size = 0 + } + cur.Size += btrfssum.BlockSize + } else if cur.Size > 0 { + begIdx := int(cur.Addr.Sub(run.Addr)/btrfssum.BlockSize) * run.ChecksumSize + lenIdx := (int(cur.Size) / btrfssum.BlockSize) * run.ChecksumSize + endIdx := begIdx + lenIdx + ret = append(ret, btrfssum.SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: run.ChecksumSize, + Addr: cur.Addr, + Sums: run.Sums[begIdx:endIdx], + }) + cur.Size = 0 + } + } + if cur.Size > 0 { + begIdx := int(cur.Addr.Sub(run.Addr)/btrfssum.BlockSize) * run.ChecksumSize + lenIdx := (int(cur.Size) / btrfssum.BlockSize) * run.ChecksumSize + endIdx := begIdx + lenIdx + ret = append(ret, btrfssum.SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: run.ChecksumSize, + Addr: cur.Addr, + Sums: run.Sums[begIdx:endIdx], + }) + cur.Size = 0 + } + } + return ret +} + +func SumsForLogicalRegion(sums btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr], beg btrfsvol.LogicalAddr, size btrfsvol.AddrDelta) btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr] { + runs := btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]{ + Addr: beg, + Size: size, + } + for laddr := beg; laddr < beg.Add(size); { + run, next, ok := sums.RunForAddr(laddr) + if !ok { + laddr = next + continue + } + off := int((laddr-run.Addr)/btrfssum.BlockSize) * run.ChecksumSize + deltaAddr := slices.Min[btrfsvol.AddrDelta]( + size-laddr.Sub(beg), + btrfsvol.AddrDelta((len(run.Sums)-off)/run.ChecksumSize)*btrfssum.BlockSize) + deltaOff := int(deltaAddr/btrfssum.BlockSize) * run.ChecksumSize + runs.Runs = append(runs.Runs, btrfssum.SumRun[btrfsvol.LogicalAddr]{ + ChecksumSize: run.ChecksumSize, + Addr: laddr, + Sums: run.Sums[off : off+deltaOff], + }) + laddr = laddr.Add(deltaAddr) + } + return runs +} diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go new file mode 100644 index 0000000..6126820 --- /dev/null +++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/matchsums.go @@ -0,0 +1,292 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package rebuildmappings + +import ( + "context" + + "github.com/datawire/dlib/dlog" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/containers" + "git.lukeshu.com/btrfs-progs-ng/lib/diskio" + "git.lukeshu.com/btrfs-progs-ng/lib/maps" +) + +func matchBlockGroupSums(ctx context.Context, + fs *btrfs.FS, + blockgroups map[btrfsvol.LogicalAddr]BlockGroup, + physicalSums map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr], + logicalSums btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr], +) error { + dlog.Info(ctx, "... Pairing up blockgroups and sums...") + bgSums := make(map[btrfsvol.LogicalAddr]btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]) + for i, bgLAddr := range maps.SortedKeys(blockgroups) { + blockgroup := blockgroups[bgLAddr] + runs := SumsForLogicalRegion(logicalSums, blockgroup.LAddr, blockgroup.Size) + bgSums[blockgroup.LAddr] = runs + dlog.Infof(ctx, "... (%v/%v) blockgroup[laddr=%v] has %v runs covering %v%%", + i+1, len(blockgroups), bgLAddr, len(runs.Runs), int(100*runs.PctFull())) + } + dlog.Info(ctx, "... ... done pairing") + + dlog.Info(ctx, "... Searching for unmapped blockgroups in unmapped physical regions...") + regions := ListUnmappedPhysicalRegions(fs) + bgMatches := make(map[btrfsvol.LogicalAddr][]btrfsvol.QualifiedPhysicalAddr) + for i, bgLAddr := range maps.SortedKeys(blockgroups) { + bgRun := bgSums[bgLAddr] + if len(bgRun.Runs) == 0 { + dlog.Errorf(ctx, "... (%v/%v) blockgroup[laddr=%v] can't be matched because it has 0 runs", + i+1, len(bgSums), bgLAddr) + continue + } + + if err := WalkUnmappedPhysicalRegions(ctx, physicalSums, regions, func(devID btrfsvol.DeviceID, region btrfssum.SumRun[btrfsvol.PhysicalAddr]) error { + matches, err := diskio.IndexAll[int64, btrfssum.ShortSum](region, bgRun) + if err != nil { + return err + } + for _, match := range matches { + bgMatches[bgLAddr] = append(bgMatches[bgLAddr], btrfsvol.QualifiedPhysicalAddr{ + Dev: devID, + Addr: region.Addr + (btrfsvol.PhysicalAddr(match) * btrfssum.BlockSize), + }) + } + return nil + }); err != nil { + return err + } + + lvl := dlog.LogLevelInfo + if len(bgMatches[bgLAddr]) == 0 { + lvl = dlog.LogLevelError + } + dlog.Logf(ctx, lvl, "... (%v/%v) blockgroup[laddr=%v] has %v matches based on %v%% coverage", + i+1, len(bgSums), bgLAddr, len(bgMatches[bgLAddr]), int(100*bgRun.PctFull())) + } + dlog.Info(ctx, "... ... done searching") + + dlog.Info(ctx, "... Applying those mappings...") + for _, bgLAddr := range maps.SortedKeys(bgMatches) { + matches := bgMatches[bgLAddr] + if len(matches) != 1 { + continue + } + blockgroup := blockgroups[bgLAddr] + mapping := btrfsvol.Mapping{ + LAddr: blockgroup.LAddr, + PAddr: matches[0], + Size: blockgroup.Size, + SizeLocked: true, + Flags: containers.Optional[btrfsvol.BlockGroupFlags]{ + OK: true, + Val: blockgroup.Flags, + }, + } + if err := fs.LV.AddMapping(mapping); err != nil { + dlog.Errorf(ctx, "... error: %v", err) + continue + } + delete(blockgroups, bgLAddr) + } + dlog.Info(ctx, "... ... done applying") + + return nil +} + +/* TODO + + dlog.Info(ctx, "Reverse-indexing remaining unmapped logical sums...") + sum2laddrs := make(map[btrfssum.ShortSum][]btrfsvol.LogicalAddr) + var numUnmappedBlocks int64 + if err := logicalSums.Walk(ctx, func(laddr btrfsvol.LogicalAddr, sum btrfssum.ShortSum) error { + var dat [btrfssum.BlockSize]byte + if _, err := fs.ReadAt(dat[:], laddr); err != nil { + if errors.Is(err, btrfsvol.ErrCouldNotMap) { + sum2laddrs[sum] = append(sum2laddrs[sum], laddr) + numUnmappedBlocks++ + return nil + } + return err + } + return nil + }); err != nil { + return err + } + dlog.Infof(ctx, "... done reverse-indexing; %v still unmapped logical sums", + numUnmappedBlocks) + + dlog.Info(ctx, "Cross-referencing sums to re-construct mappings...") + newMappings := &ExtentMappings{ + InLV: &fs.LV, + InBlockGroups: blockgroups, + InSums: sums, + InReverseSums: sum2laddrs, + } + regions := ListPhysicalRegions(fs) + for _, devID := range maps.SortedKeys(regions) { + if err := newMappings.ScanOneDevice(ctx, + devID, devs[devID].Name(), + regions[devID], + ); err != nil { + return err + } + } + dlog.Info(ctx, "... done cross-referencing") + + dlog.Info(ctx, "Applying those mappings...") + for laddr, mappings := range newMappings.OutSum2mappings { + if len(mappings) > 1 { + dlog.Errorf(ctx, "multiple possibilities for laddr=%v :", laddr) + for _, mapping := range mappings { + dlog.Errorf(ctx, " - %#v", *mapping) + } + continue + } + if err := fs.LV.AddMapping(*mappings[0]); err != nil { + dlog.Error(ctx, err) + } + } + dlog.Info(ctx, "... done applying") + + return nil +} + +type ExtentMappings struct { + // input + InLV *btrfsvol.LogicalVolume[*btrfs.Device] + InBlockGroups *BlockGroupTree + InSums AllSums + InReverseSums map[ShortSum][]btrfsvol.LogicalAddr + + // state + internedMappings map[btrfsvol.Mapping]*btrfsvol.Mapping + + // output + OutSum2mappings map[ShortSum][]*btrfsvol.Mapping +} + +func (em *ExtentMappings) considerMapping(ctx context.Context, laddr btrfsvol.LogicalAddr, paddr btrfsvol.QualifiedPhysicalAddr) (btrfsvol.Mapping, bool) { + blockgroup := LookupBlockGroup(em.InBlockGroups, laddr, btrfssum.BlockSize) + if blockgroup == nil { + return btrfsvol.Mapping{ + LAddr: laddr, + PAddr: paddr, + Size: btrfssum.BlockSize, + }, true + } + mapping := btrfsvol.Mapping{ + LAddr: blockgroup.LAddr, + PAddr: btrfsvol.QualifiedPhysicalAddr{ + Dev: paddr.Dev, + Addr: paddr.Addr.Add(laddr.Sub(blockgroup.LAddr)), + }, + Size: blockgroup.Size, + SizeLocked: true, + Flags: containers.Optional[btrfsvol.BlockGroupFlags]{ + OK: true, + Val: blockgroup.Flags, + }, + } + if !em.InLV.CouldAddMapping(mapping) { + return btrfsvol.Mapping{}, false + } + + for offset := btrfsvol.AddrDelta(0); offset <= mapping.Size; offset += btrfssum.BlockSize { + expCSum, ok := em.InSums.SumForLAddr(mapping.LAddr.Add(offset)) + if !ok { + continue + } + actCSum, _ := em.InSums.SumForPAddr(mapping.PAddr.Add(offset)) + if actCSum != expCSum { + return btrfsvol.Mapping{}, false + } + } + return mapping, true +} + +func (em *ExtentMappings) addMapping(sum ShortSum, mapping btrfsvol.Mapping) { + interned := em.internedMappings[mapping] + if interned == nil { + interned = &mapping + em.internedMappings[mapping] = interned + } + + em.OutSum2mappings[sum] = append(em.OutSum2mappings[sum], interned) +} + +func (em *ExtentMappings) ScanOneDevice( + ctx context.Context, + devID btrfsvol.DeviceID, devName string, + regions []PhysicalRegion, +) error { + if em.internedMappings == nil { + em.internedMappings = make(map[btrfsvol.Mapping]*btrfsvol.Mapping) + } + if em.OutSum2mappings == nil { + em.OutSum2mappings = make(map[ShortSum][]*btrfsvol.Mapping) + } + + dlog.Infof(ctx, "... dev[%q] Scanning for extents...", devName) + + var totalMappings int + _ = WalkRegions(ctx, regions, btrfssum.BlockSize, + func(_, _ int64) {}, + func(paddr btrfsvol.PhysicalAddr) error { + qpaddr := btrfsvol.QualifiedPhysicalAddr{ + Dev: devID, + Addr: paddr, + } + sum, _ := em.InSums.SumForPAddr(qpaddr) + totalMappings += len(em.InReverseSums[sum]) + return nil + }, + ) + + lastProgress := -1 + considered := 0 + accepted := 0 + progress := func() { + pct := int(100 * 10000 * float64(considered) / float64(totalMappings)) + if pct != lastProgress || considered == totalMappings { + dlog.Infof(ctx, "... dev[%q] scanned %v%% (considered %v/%v pairings, accepted %v)", + devName, float64(pct)/10000.0, considered, totalMappings, accepted) + lastProgress = pct + } + } + return WalkRegions(ctx, regions, btrfssum.BlockSize, + func(_, _ int64) { + progress() + }, + func(paddr btrfsvol.PhysicalAddr) error { + qpaddr := btrfsvol.QualifiedPhysicalAddr{ + Dev: devID, + Addr: paddr, + } + sum, _ := em.InSums.SumForPAddr(qpaddr) + for i, laddr := range em.InReverseSums[sum] { + if i%100 == 0 { + if err := ctx.Err(); err != nil { + return err + } + } + mapping, ok := em.considerMapping(ctx, laddr, qpaddr) + considered++ + if !ok { + continue + } + em.addMapping(sum, mapping) + accepted++ + progress() + } + + return nil + }, + ) +} + +*/ diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/physicalsums.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/physicalsums.go new file mode 100644 index 0000000..0806a63 --- /dev/null +++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/physicalsums.go @@ -0,0 +1,89 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package rebuildmappings + +import ( + "context" + "sort" + + "golang.org/x/exp/constraints" + + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" + "git.lukeshu.com/btrfs-progs-ng/lib/btrfsprogs/btrfsinspect" + "git.lukeshu.com/btrfs-progs-ng/lib/maps" +) + +func ExtractPhysicalSums(scanResults btrfsinspect.ScanDevicesResult) map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr] { + ret := make(map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr], len(scanResults)) + for devID, devResults := range scanResults { + ret[devID] = devResults.Checksums + } + return ret +} + +type PhysicalRegion struct { + Beg, End btrfsvol.PhysicalAddr +} + +func ListUnmappedPhysicalRegions(fs *btrfs.FS) map[btrfsvol.DeviceID][]PhysicalRegion { + regions := make(map[btrfsvol.DeviceID][]PhysicalRegion) + pos := make(map[btrfsvol.DeviceID]btrfsvol.PhysicalAddr) + mappings := fs.LV.Mappings() + sort.Slice(mappings, func(i, j int) bool { + return mappings[i].PAddr.Cmp(mappings[j].PAddr) < 0 + }) + for _, mapping := range mappings { + if pos[mapping.PAddr.Dev] < mapping.PAddr.Addr { + regions[mapping.PAddr.Dev] = append(regions[mapping.PAddr.Dev], PhysicalRegion{ + Beg: pos[mapping.PAddr.Dev], + End: mapping.PAddr.Addr, + }) + } + if pos[mapping.PAddr.Dev] < mapping.PAddr.Addr.Add(mapping.Size) { + pos[mapping.PAddr.Dev] = mapping.PAddr.Addr.Add(mapping.Size) + } + } + for devID, dev := range fs.LV.PhysicalVolumes() { + devSize := dev.Size() + if pos[devID] < devSize { + regions[devID] = append(regions[devID], PhysicalRegion{ + Beg: pos[devID], + End: devSize, + }) + } + } + return regions +} + +func roundUp[T constraints.Integer](x, multiple T) T { + return ((x + multiple - 1) / multiple) * multiple +} + +func WalkUnmappedPhysicalRegions(ctx context.Context, + physicalSums map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr], + gaps map[btrfsvol.DeviceID][]PhysicalRegion, + fn func(btrfsvol.DeviceID, btrfssum.SumRun[btrfsvol.PhysicalAddr]) error, +) error { + for _, devID := range maps.SortedKeys(gaps) { + for _, gap := range gaps[devID] { + if err := ctx.Err(); err != nil { + return err + } + begAddr := roundUp(gap.Beg, btrfssum.BlockSize) + begOff := int(begAddr/btrfssum.BlockSize) * physicalSums[devID].ChecksumSize + endOff := int(gap.End/btrfssum.BlockSize) * physicalSums[devID].ChecksumSize + if err := fn(devID, btrfssum.SumRun[btrfsvol.PhysicalAddr]{ + ChecksumSize: physicalSums[devID].ChecksumSize, + Addr: begAddr, + Sums: physicalSums[devID].Sums[begOff:endOff], + }); err != nil { + return err + } + } + } + return nil +} diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go index 05aff72..09b2185 100644 --- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go +++ b/lib/btrfsprogs/btrfsinspect/rebuildmappings/rebuildmappings.go @@ -46,13 +46,14 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect numNodes += len(paddrs) } } - dlog.Infof(ctx, "plan: 1/5 process %d chunks", numChunks) - dlog.Infof(ctx, "plan: 2/5 process %d device extents", numDevExts) - dlog.Infof(ctx, "plan: 3/5 process %d nodes", numNodes) - dlog.Infof(ctx, "plan: 4/5 process %d block groups", numBlockGroups) - dlog.Infof(ctx, "plan: 5/5 process sums") + dlog.Infof(ctx, "plan: 1/6 process %d chunks", numChunks) + dlog.Infof(ctx, "plan: 2/6 process %d device extents", numDevExts) + dlog.Infof(ctx, "plan: 3/6 process %d nodes", numNodes) + dlog.Infof(ctx, "plan: 4/6 process %d block groups", numBlockGroups) + dlog.Infof(ctx, "plan: 5/6 process sums of block groups") + dlog.Infof(ctx, "plan: 6/6 process remaining sums") - dlog.Infof(ctx, "1/5: Processing %d chunks...", numChunks) + dlog.Infof(ctx, "1/6: Processing %d chunks...", numChunks) for _, devID := range devIDs { devResults := scanResults[devID] for _, chunk := range devResults.FoundChunks { @@ -65,7 +66,7 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect } dlog.Info(ctx, "... done processing chunks") - dlog.Infof(ctx, "2/5: Processing %d device extents...", numDevExts) + dlog.Infof(ctx, "2/6: Processing %d device extents...", numDevExts) for _, devID := range devIDs { devResults := scanResults[devID] for _, ext := range devResults.FoundDevExtents { @@ -80,7 +81,7 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect // too much. (Because nodes are numerous and small, while the // others are few and large; so it is likely that many of the // nodes will be subsumed by other things.) - dlog.Infof(ctx, "3/5: Processing %d nodes...", numNodes) + dlog.Infof(ctx, "3/6: Processing %d nodes...", numNodes) for _, devID := range devIDs { devResults := scanResults[devID] // Sort them so that progress numbers are predictable. @@ -104,15 +105,17 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect // Use block groups to add missing flags (and as a hint to // combine node entries). - dlog.Infof(ctx, "4/5: Processing %d block groups...", numBlockGroups) + dlog.Infof(ctx, "4/6: Processing %d block groups...", numBlockGroups) // First dedup them, because they change for allocations and // CoW means that they'll bounce around a lot, so you likely // have oodles of duplicates? - bgsOrdered, err := DedupBlockGroups(scanResults) + bgs, err := DedupBlockGroups(scanResults) if err != nil { return err } - for _, bg := range bgsOrdered { + dlog.Infof(ctx, "... de-duplicated to %d block groups", len(bgs)) + for _, bgLAddr := range maps.SortedKeys(bgs) { + bg := bgs[bgLAddr] otherLAddr, otherPAddr := fs.LV.ResolveAny(bg.LAddr, bg.Size) if otherLAddr < 0 || otherPAddr.Addr < 0 { dlog.Errorf(ctx, "... error: could not pair blockgroup laddr=%v (size=%v flags=%v) with a mapping", @@ -133,10 +136,43 @@ func RebuildMappings(ctx context.Context, fs *btrfs.FS, scanResults btrfsinspect } if err := fs.LV.AddMapping(mapping); err != nil { dlog.Errorf(ctx, "... error: adding flags from blockgroup: %v", err) + continue } + delete(bgs, bgLAddr) } dlog.Info(ctx, "... done processing block groups") - dlog.Infof(ctx, "5/5: Processing sums: TODO") + dlog.Infof(ctx, "5/6: Processing sums of %d block groups", len(bgs)) + physicalSums := ExtractPhysicalSums(scanResults) + logicalSums := ExtractLogicalSums(ctx, scanResults) + if err := matchBlockGroupSums(ctx, fs, bgs, physicalSums, logicalSums); err != nil { + return err + } + + dlog.Infof(ctx, "6/6: process remaining sums: TODO") + + dlog.Infof(ctx, "report:") + unmappedPhysicalRegions := ListUnmappedPhysicalRegions(fs) + var unmappedPhysical btrfsvol.AddrDelta + var numUnmappedPhysical int + for _, devRegions := range unmappedPhysicalRegions { + numUnmappedPhysical += len(devRegions) + for _, region := range devRegions { + unmappedPhysical += region.End.Sub(region.Beg) + } + } + dlog.Infof(ctx, "... %d KiB of unmapped physical space (across %d regions)", unmappedPhysical/1024, numUnmappedPhysical) + unmappedLogicalRegions := ListUnmappedLogicalRegions(fs, logicalSums) + var unmappedLogical btrfsvol.AddrDelta + for _, region := range unmappedLogicalRegions { + unmappedLogical += region.Size() + } + dlog.Infof(ctx, "... %d KiB of unmapped summed logical space (across %d regions)", unmappedLogical/1024, len(unmappedLogicalRegions)) + var unmappedBlockGroups btrfsvol.AddrDelta + for _, bg := range bgs { + unmappedBlockGroups += bg.Size + } + dlog.Infof(ctx, "... %d KiB of unmapped block groups (across %d groups)", unmappedBlockGroups/1024, len(bgs)) + return nil } diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/scan.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/scan.go deleted file mode 100644 index 69b783d..0000000 --- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/scan.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package rebuildmappings - -import ( - "context" - "errors" - - "github.com/datawire/dlib/dlog" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" - "git.lukeshu.com/btrfs-progs-ng/lib/containers" - "git.lukeshu.com/btrfs-progs-ng/lib/diskio" - "git.lukeshu.com/btrfs-progs-ng/lib/maps" -) - -func ScanForExtents(ctx context.Context, - fs *btrfs.FS, - blockgroups map[btrfsvol.LogicalAddr]BlockGroup, - physicalSums map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr], - logicalSums btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr], -) error { - dlog.Info(ctx, "Pairing up blockgroups and sums...") - bgSums := make(map[btrfsvol.LogicalAddr]btrfssum.SumRunWithGaps[btrfsvol.LogicalAddr]) - for i, bgLAddr := range maps.SortedKeys(blockgroups) { - blockgroup := blockgroups[bgLAddr] - runs := SumsForLogicalRegion(logicalSums, blockgroup.LAddr, blockgroup.Size) - bgSums[blockgroup.LAddr] = runs - dlog.Infof(ctx, "... (%v/%v) blockgroup[laddr=%v] has %v runs covering %v%%", - i+1, len(blockgroups), bgLAddr, len(runs.Runs), int(100*runs.PctFull())) - } - dlog.Info(ctx, "... done pairing") - - dlog.Info(ctx, "Searching for unmapped blockgroups in unmapped regions...") - gaps := ListUnmappedPhysicalRegions(fs) - bgMatches := make(map[btrfsvol.LogicalAddr][]btrfsvol.QualifiedPhysicalAddr) - for i, bgLAddr := range maps.SortedKeys(blockgroups) { - bgRun := bgSums[bgLAddr] - if len(bgRun.Runs) == 0 { - dlog.Errorf(ctx, "... (%v/%v) blockgroup[laddr=%v] can't be matched because it has 0 runs", - i+1, len(bgSums), bgLAddr) - continue - } - - if err := WalkUnmappedPhysicalRegions(ctx, physicalSums, gaps, func(devID btrfsvol.DeviceID, gap btrfssum.SumRun[btrfsvol.PhysicalAddr]) error { - matches, err := diskio.IndexAll[int64, btrfssum.ShortSum](gap, bgRun) - if err != nil { - return err - } - for _, match := range matches { - bgMatches[bgLAddr] = append(bgMatches[bgLAddr], btrfsvol.QualifiedPhysicalAddr{ - Dev: devID, - Addr: gap.Addr + (btrfsvol.PhysicalAddr(match) * btrfssum.BlockSize), - }) - } - return nil - }); err != nil { - return err - } - - lvl := dlog.LogLevelInfo - if len(bgMatches[bgLAddr]) == 0 { - lvl = dlog.LogLevelError - } - dlog.Logf(ctx, lvl, "... (%v/%v) blockgroup[laddr=%v] has %v matches based on %v%% coverage", - i+1, len(bgSums), bgLAddr, len(bgMatches[bgLAddr]), int(100*bgRun.PctFull())) - } - dlog.Info(ctx, "... done searching") - - dlog.Info(ctx, "Applying those mappings...") - for _, bgLAddr := range maps.SortedKeys(bgMatches) { - matches := bgMatches[bgLAddr] - if len(matches) != 1 { - continue - } - blockgroup := blockgroups[bgLAddr] - mapping := btrfsvol.Mapping{ - LAddr: blockgroup.LAddr, - PAddr: matches[0], - Size: blockgroup.Size, - SizeLocked: true, - Flags: containers.Optional[btrfsvol.BlockGroupFlags]{ - OK: true, - Val: blockgroup.Flags, - }, - } - if err := fs.LV.AddMapping(mapping); err != nil { - dlog.Error(ctx, err) - } - } - dlog.Info(ctx, "... done applying") - - dlog.Info(ctx, "Reverse-indexing remaining unmapped logical sums...") - sum2laddrs := make(map[btrfssum.ShortSum][]btrfsvol.LogicalAddr) - var numUnmappedBlocks int64 - if err := logicalSums.Walk(ctx, func(laddr btrfsvol.LogicalAddr, sum btrfssum.ShortSum) error { - var dat [btrfssum.BlockSize]byte - if _, err := fs.ReadAt(dat[:], laddr); err != nil { - if errors.Is(err, btrfsvol.ErrCouldNotMap) { - sum2laddrs[sum] = append(sum2laddrs[sum], laddr) - numUnmappedBlocks++ - return nil - } - return err - } - return nil - }); err != nil { - return err - } - dlog.Infof(ctx, "... done reverse-indexing; %v still unmapped logical sums", - numUnmappedBlocks) - - /* TODO - - dlog.Info(ctx, "Cross-referencing sums to re-construct mappings...") - newMappings := &ExtentMappings{ - InLV: &fs.LV, - InBlockGroups: blockgroups, - InSums: sums, - InReverseSums: sum2laddrs, - } - gaps := ListPhysicalGaps(fs) - for _, devID := range maps.SortedKeys(gaps) { - if err := newMappings.ScanOneDevice(ctx, - devID, devs[devID].Name(), - gaps[devID], - ); err != nil { - return err - } - } - dlog.Info(ctx, "... done cross-referencing") - - dlog.Info(ctx, "Applying those mappings...") - for laddr, mappings := range newMappings.OutSum2mappings { - if len(mappings) > 1 { - dlog.Errorf(ctx, "multiple possibilities for laddr=%v :", laddr) - for _, mapping := range mappings { - dlog.Errorf(ctx, " - %#v", *mapping) - } - continue - } - if err := fs.LV.AddMapping(*mappings[0]); err != nil { - dlog.Error(ctx, err) - } - } - dlog.Info(ctx, "... done applying") - - */ - - return nil -} - -/* - -type ExtentMappings struct { - // input - InLV *btrfsvol.LogicalVolume[*btrfs.Device] - InBlockGroups *BlockGroupTree - InSums AllSums - InReverseSums map[ShortSum][]btrfsvol.LogicalAddr - - // state - internedMappings map[btrfsvol.Mapping]*btrfsvol.Mapping - - // output - OutSum2mappings map[ShortSum][]*btrfsvol.Mapping -} - -func (em *ExtentMappings) considerMapping(ctx context.Context, laddr btrfsvol.LogicalAddr, paddr btrfsvol.QualifiedPhysicalAddr) (btrfsvol.Mapping, bool) { - blockgroup := LookupBlockGroup(em.InBlockGroups, laddr, btrfssum.BlockSize) - if blockgroup == nil { - return btrfsvol.Mapping{ - LAddr: laddr, - PAddr: paddr, - Size: btrfssum.BlockSize, - }, true - } - mapping := btrfsvol.Mapping{ - LAddr: blockgroup.LAddr, - PAddr: btrfsvol.QualifiedPhysicalAddr{ - Dev: paddr.Dev, - Addr: paddr.Addr.Add(laddr.Sub(blockgroup.LAddr)), - }, - Size: blockgroup.Size, - SizeLocked: true, - Flags: containers.Optional[btrfsvol.BlockGroupFlags]{ - OK: true, - Val: blockgroup.Flags, - }, - } - if !em.InLV.CouldAddMapping(mapping) { - return btrfsvol.Mapping{}, false - } - - for offset := btrfsvol.AddrDelta(0); offset <= mapping.Size; offset += btrfssum.BlockSize { - expCSum, ok := em.InSums.SumForLAddr(mapping.LAddr.Add(offset)) - if !ok { - continue - } - actCSum, _ := em.InSums.SumForPAddr(mapping.PAddr.Add(offset)) - if actCSum != expCSum { - return btrfsvol.Mapping{}, false - } - } - return mapping, true -} - -func (em *ExtentMappings) addMapping(sum ShortSum, mapping btrfsvol.Mapping) { - interned := em.internedMappings[mapping] - if interned == nil { - interned = &mapping - em.internedMappings[mapping] = interned - } - - em.OutSum2mappings[sum] = append(em.OutSum2mappings[sum], interned) -} - -func (em *ExtentMappings) ScanOneDevice( - ctx context.Context, - devID btrfsvol.DeviceID, devName string, - gaps []PhysicalGap, -) error { - if em.internedMappings == nil { - em.internedMappings = make(map[btrfsvol.Mapping]*btrfsvol.Mapping) - } - if em.OutSum2mappings == nil { - em.OutSum2mappings = make(map[ShortSum][]*btrfsvol.Mapping) - } - - dlog.Infof(ctx, "... dev[%q] Scanning for extents...", devName) - - var totalMappings int - _ = WalkGaps(ctx, gaps, btrfssum.BlockSize, - func(_, _ int64) {}, - func(paddr btrfsvol.PhysicalAddr) error { - qpaddr := btrfsvol.QualifiedPhysicalAddr{ - Dev: devID, - Addr: paddr, - } - sum, _ := em.InSums.SumForPAddr(qpaddr) - totalMappings += len(em.InReverseSums[sum]) - return nil - }, - ) - - lastProgress := -1 - considered := 0 - accepted := 0 - progress := func() { - pct := int(100 * 10000 * float64(considered) / float64(totalMappings)) - if pct != lastProgress || considered == totalMappings { - dlog.Infof(ctx, "... dev[%q] scanned %v%% (considered %v/%v pairings, accepted %v)", - devName, float64(pct)/10000.0, considered, totalMappings, accepted) - lastProgress = pct - } - } - return WalkGaps(ctx, gaps, btrfssum.BlockSize, - func(_, _ int64) { - progress() - }, - func(paddr btrfsvol.PhysicalAddr) error { - qpaddr := btrfsvol.QualifiedPhysicalAddr{ - Dev: devID, - Addr: paddr, - } - sum, _ := em.InSums.SumForPAddr(qpaddr) - for i, laddr := range em.InReverseSums[sum] { - if i%100 == 0 { - if err := ctx.Err(); err != nil { - return err - } - } - mapping, ok := em.considerMapping(ctx, laddr, qpaddr) - considered++ - if !ok { - continue - } - em.addMapping(sum, mapping) - accepted++ - progress() - } - - return nil - }, - ) -} - -*/ diff --git a/lib/btrfsprogs/btrfsinspect/rebuildmappings/unmappedregions.go b/lib/btrfsprogs/btrfsinspect/rebuildmappings/unmappedregions.go deleted file mode 100644 index 1bac127..0000000 --- a/lib/btrfsprogs/btrfsinspect/rebuildmappings/unmappedregions.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package rebuildmappings - -import ( - "context" - "sort" - - "golang.org/x/exp/constraints" - - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum" - "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" - "git.lukeshu.com/btrfs-progs-ng/lib/maps" -) - -type PhysicalRegion struct { - Beg, End btrfsvol.PhysicalAddr -} - -func ListUnmappedPhysicalRegions(fs *btrfs.FS) map[btrfsvol.DeviceID][]PhysicalRegion { - regions := make(map[btrfsvol.DeviceID][]PhysicalRegion) - pos := make(map[btrfsvol.DeviceID]btrfsvol.PhysicalAddr) - mappings := fs.LV.Mappings() - sort.Slice(mappings, func(i, j int) bool { - return mappings[i].PAddr.Cmp(mappings[j].PAddr) < 0 - }) - for _, mapping := range mappings { - if pos[mapping.PAddr.Dev] < mapping.PAddr.Addr { - regions[mapping.PAddr.Dev] = append(regions[mapping.PAddr.Dev], PhysicalRegion{ - Beg: pos[mapping.PAddr.Dev], - End: mapping.PAddr.Addr, - }) - } - if pos[mapping.PAddr.Dev] < mapping.PAddr.Addr.Add(mapping.Size) { - pos[mapping.PAddr.Dev] = mapping.PAddr.Addr.Add(mapping.Size) - } - } - for devID, dev := range fs.LV.PhysicalVolumes() { - devSize := dev.Size() - if pos[devID] < devSize { - regions[devID] = append(regions[devID], PhysicalRegion{ - Beg: pos[devID], - End: devSize, - }) - } - } - return regions -} - -func roundUp[T constraints.Integer](x, multiple T) T { - return ((x + multiple - 1) / multiple) * multiple -} - -func WalkUnmappedPhysicalRegions(ctx context.Context, - physicalSums map[btrfsvol.DeviceID]btrfssum.SumRun[btrfsvol.PhysicalAddr], - gaps map[btrfsvol.DeviceID][]PhysicalRegion, - fn func(btrfsvol.DeviceID, btrfssum.SumRun[btrfsvol.PhysicalAddr]) error, -) error { - for _, devID := range maps.SortedKeys(gaps) { - for _, gap := range gaps[devID] { - if err := ctx.Err(); err != nil { - return err - } - begAddr := roundUp(gap.Beg, btrfssum.BlockSize) - begOff := int(begAddr/btrfssum.BlockSize) * physicalSums[devID].ChecksumSize - endOff := int(gap.End/btrfssum.BlockSize) * physicalSums[devID].ChecksumSize - if err := fn(devID, btrfssum.SumRun[btrfsvol.PhysicalAddr]{ - ChecksumSize: physicalSums[devID].ChecksumSize, - Addr: begAddr, - Sums: physicalSums[devID].Sums[begOff:endOff], - }); err != nil { - return err - } - } - } - return nil -} -- cgit v1.2.3-2-g168b