1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later
package rebuildnodes
import (
"context"
"fmt"
"reflect"
"sort"
"github.com/datawire/dlib/dlog"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfstree"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/containers"
"git.lukeshu.com/btrfs-progs-ng/lib/slices"
)
type RebuiltNode struct {
Errs containers.Set[string]
MinKey, MaxKey btrfsprim.Key
InTrees containers.Set[btrfsprim.ObjID]
btrfstree.Node
}
func (a RebuiltNode) Compat(b RebuiltNode) bool {
a.Node.Head.Generation = b.Node.Head.Generation
return reflect.DeepEqual(a.Node, b.Node)
}
func (a RebuiltNode) Merge(b RebuiltNode) (RebuiltNode, error) {
if !a.Compat(b) {
switch {
case a.Node.Head.Generation > b.Node.Head.Generation:
return a, nil
case a.Node.Head.Generation < b.Node.Head.Generation:
return b, nil
default:
return a, fmt.Errorf("mismatch: %v != %v", a, b)
}
}
// take the broadest region
if a.MinKey.Cmp(b.MinKey) > 0 { // if a.MinKey > b.MinKey {
a.MinKey = b.MinKey // take the min of the two
}
if a.MaxKey.Cmp(b.MaxKey) < 0 { // if a.MaxKey < b.MaxKey {
a.MaxKey = b.MaxKey // take the min of the two
}
// take the highest generation
a.Node.Head.Generation = slices.Max(a.Node.Head.Generation, b.Node.Head.Generation)
// take the union
a.InTrees.InsertFrom(b.InTrees)
a.Errs.InsertFrom(b.Errs)
return a, nil
}
func reInitBrokenNodes(ctx context.Context, fs _FS, badNodes []badNode) (map[btrfsvol.LogicalAddr]*RebuiltNode, error) {
dlog.Info(ctx, "Re-initializing bad nodes...")
sb, err := fs.Superblock()
if err != nil {
return nil, err
}
chunkTreeUUID, ok := getChunkTreeUUID(ctx, fs)
if !ok {
return nil, fmt.Errorf("could not look up chunk tree UUID")
}
sort.Slice(badNodes, func(i, j int) bool {
iGen := badNodes[i].Path.Node(-1).FromGeneration
jGen := badNodes[j].Path.Node(-1).FromGeneration
switch {
case iGen < jGen:
return true
case iGen > jGen:
return false
default:
iAddr := badNodes[i].Path.Node(-1).ToNodeAddr
jAddr := badNodes[j].Path.Node(-1).ToNodeAddr
return iAddr < jAddr
}
})
lastPct := -1
progress := func(done int) {
pct := int(100 * float64(done) / float64(len(badNodes)))
if pct != lastPct || done == len(badNodes) {
dlog.Infof(ctx, "... %v%% (%v/%v)",
pct, done, len(badNodes))
lastPct = pct
}
}
rebuiltNodes := make(map[btrfsvol.LogicalAddr]*RebuiltNode)
for i, badNode := range badNodes {
progress(i)
path := badNode.Path
min, max := spanOfTreePath(fs, path)
node := RebuiltNode{
Errs: containers.Set[string]{badNode.Err: struct{}{}},
MinKey: min,
MaxKey: max,
InTrees: containers.Set[btrfsprim.ObjID]{path.Node(-1).FromTree: struct{}{}},
Node: btrfstree.Node{
Size: sb.NodeSize,
ChecksumType: sb.ChecksumType,
Head: btrfstree.NodeHeader{
MetadataUUID: sb.EffectiveMetadataUUID(),
Addr: path.Node(-1).ToNodeAddr,
ChunkTreeUUID: chunkTreeUUID,
//Owner: TBD, // see RebuiltNode.InTrees
Generation: path.Node(-1).FromGeneration,
Level: path.Node(-1).ToNodeLevel,
},
},
}
if other, ok := rebuiltNodes[path.Node(-1).ToNodeAddr]; ok {
*other, err = other.Merge(node)
if err != nil {
dlog.Errorf(ctx, "... %v", err)
}
} else {
rebuiltNodes[path.Node(-1).ToNodeAddr] = &node
}
}
progress(len(badNodes))
dlog.Infof(ctx, "... initialized %d nodes", len(rebuiltNodes))
return rebuiltNodes, nil
}
|