-
Notifications
You must be signed in to change notification settings - Fork 11
/
usage.go
176 lines (156 loc) · 4.27 KB
/
usage.go
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package btrfs
import (
"os"
"sort"
"syscall"
)
func cmpChunkBlockGroup(f1, f2 blockGroup) int {
var mask blockGroup
if (f1 & _BTRFS_BLOCK_GROUP_TYPE_MASK) ==
(f2 & _BTRFS_BLOCK_GROUP_TYPE_MASK) {
mask = _BTRFS_BLOCK_GROUP_PROFILE_MASK
} else if f2&blockGroupSystem != 0 {
return -1
} else if f1&blockGroupSystem != 0 {
return +1
} else {
mask = _BTRFS_BLOCK_GROUP_TYPE_MASK
}
if (f1 & mask) > (f2 & mask) {
return +1
} else if (f1 & mask) < (f2 & mask) {
return -1
} else {
return 0
}
}
type spaceInfoByBlockGroup []spaceInfo
func (a spaceInfoByBlockGroup) Len() int { return len(a) }
func (a spaceInfoByBlockGroup) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a spaceInfoByBlockGroup) Less(i, j int) bool {
return cmpChunkBlockGroup(blockGroup(a[i].Flags), blockGroup(a[j].Flags)) < 0
}
type UsageInfo struct {
Total uint64
TotalUnused uint64
TotalUsed uint64
TotalChunks uint64
FreeEstimated uint64
FreeMin uint64
LogicalDataChunks uint64
RawDataChunks uint64
RawDataUsed uint64
LogicalMetaChunks uint64
RawMetaChunks uint64
RawMetaUsed uint64
SystemUsed uint64
SystemChunks uint64
DataRatio float64
MetadataRatio float64
GlobalReserve uint64
GlobalReserveUsed uint64
}
const minUnallocatedThreshold = 16 * 1024 * 1024
func spaceUsage(f *os.File) (UsageInfo, error) {
info, err := iocFsInfo(f)
if err != nil {
return UsageInfo{}, err
}
var u UsageInfo
for i := uint64(0); i <= info.max_id; i++ {
dev, err := iocDevInfo(f, i, UUID{})
if err == syscall.ENODEV {
continue
} else if err != nil {
return UsageInfo{}, err
}
u.Total += dev.total_bytes
}
spaces, err := iocSpaceInfo(f)
if err != nil {
return UsageInfo{}, err
}
sort.Sort(spaceInfoByBlockGroup(spaces))
var (
maxDataRatio int = 1
mixed bool
)
for _, s := range spaces {
ratio := 1
bg := s.Flags.BlockGroup()
switch {
case bg&blockGroupRaid0 != 0:
ratio = 1
case bg&blockGroupRaid1 != 0:
ratio = 2
case bg&blockGroupRaid5 != 0:
ratio = 0
case bg&blockGroupRaid6 != 0:
ratio = 0
case bg&blockGroupDup != 0:
ratio = 2
case bg&blockGroupRaid10 != 0:
ratio = 2
}
if ratio > maxDataRatio {
maxDataRatio = ratio
}
if bg&spaceInfoGlobalRsv != 0 {
u.GlobalReserve = s.TotalBytes
u.GlobalReserveUsed = s.UsedBytes
}
if bg&(blockGroupData|blockGroupMetadata) == (blockGroupData | blockGroupMetadata) {
mixed = true
}
if bg&blockGroupData != 0 {
u.RawDataUsed += s.UsedBytes * uint64(ratio)
u.RawDataChunks += s.TotalBytes * uint64(ratio)
u.LogicalDataChunks += s.TotalBytes
}
if bg&blockGroupMetadata != 0 {
u.RawMetaUsed += s.UsedBytes * uint64(ratio)
u.RawMetaChunks += s.TotalBytes * uint64(ratio)
u.LogicalMetaChunks += s.TotalBytes
}
if bg&blockGroupSystem != 0 {
u.SystemUsed += s.UsedBytes * uint64(ratio)
u.SystemChunks += s.TotalBytes * uint64(ratio)
}
}
u.TotalChunks = u.RawDataChunks + u.SystemChunks
u.TotalUsed = u.RawDataUsed + u.SystemUsed
if !mixed {
u.TotalChunks += u.RawMetaChunks
u.TotalUsed += u.RawMetaUsed
}
u.TotalUnused = u.Total - u.TotalChunks
u.DataRatio = float64(u.RawDataChunks) / float64(u.LogicalDataChunks)
if mixed {
u.MetadataRatio = u.DataRatio
} else {
u.MetadataRatio = float64(u.RawMetaChunks) / float64(u.LogicalMetaChunks)
}
// We're able to fill at least DATA for the unused space
//
// With mixed raid levels, this gives a rough estimate but more
// accurate than just counting the logical free space
// (l_data_chunks - l_data_used)
//
// In non-mixed case there's no difference.
u.FreeEstimated = uint64(float64(u.RawDataChunks-u.RawDataUsed) / u.DataRatio)
// For mixed-bg the metadata are left out in calculations thus global
// reserve would be lost. Part of it could be permanently allocated,
// we have to subtract the used bytes so we don't go under zero free.
if mixed {
u.FreeEstimated -= u.GlobalReserve - u.GlobalReserveUsed
}
u.FreeMin = u.FreeEstimated
// Chop unallocatable space
// FIXME: must be applied per device
if u.TotalUnused >= minUnallocatedThreshold {
u.FreeEstimated += uint64(float64(u.TotalUnused) / u.DataRatio)
// Match the calculation of 'df', use the highest raid ratio
u.FreeMin += u.TotalUnused / uint64(maxDataRatio)
}
return u, nil
}