Skip to content

Commit 3bc4e69

Browse files
committed
Implement btrfs usage
Implements btrfs usage using a double walking diff and counting the result. Walking gives the most accurate count and includes inode usage. Signed-off-by: Derek McGowan <[email protected]>
1 parent f6df9f6 commit 3bc4e69

4 files changed

Lines changed: 121 additions & 18 deletions

File tree

fs/du.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package fs
22

3+
import "context"
4+
35
// Usage of disk information
46
type Usage struct {
57
Inodes int64
@@ -11,3 +13,10 @@ type Usage struct {
1113
func DiskUsage(roots ...string) (Usage, error) {
1214
return diskUsage(roots...)
1315
}
16+
17+
// DiffUsage counts the numbers of inodes and disk usage in the
18+
// diff between the 2 directories. The first path is intended
19+
// as the base directory and the second as the changed directory.
20+
func DiffUsage(ctx context.Context, a, b string) (Usage, error) {
21+
return diffUsage(ctx, a, b)
22+
}

fs/du_unix.go

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
package fs
44

55
import (
6+
"context"
67
"os"
78
"path/filepath"
89
"syscall"
910
)
1011

12+
type inode struct {
13+
// TODO(stevvooe): Can probably reduce memory usage by not tracking
14+
// device, but we can leave this right for now.
15+
dev, ino uint64
16+
}
17+
1118
func diskUsage(roots ...string) (Usage, error) {
12-
type inode struct {
13-
// TODO(stevvooe): Can probably reduce memory usage by not tracking
14-
// device, but we can leave this right for now.
15-
dev, ino uint64
16-
}
1719

1820
var (
1921
size int64
@@ -45,3 +47,37 @@ func diskUsage(roots ...string) (Usage, error) {
4547
Size: size,
4648
}, nil
4749
}
50+
51+
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
52+
var (
53+
size int64
54+
inodes = map[inode]struct{}{} // expensive!
55+
)
56+
57+
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
58+
if err != nil {
59+
return err
60+
}
61+
62+
if kind == ChangeKindAdd || kind == ChangeKindModify {
63+
stat := fi.Sys().(*syscall.Stat_t)
64+
65+
inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)}
66+
if _, ok := inodes[inoKey]; !ok {
67+
inodes[inoKey] = struct{}{}
68+
size += fi.Size()
69+
}
70+
71+
return nil
72+
73+
}
74+
return nil
75+
}); err != nil {
76+
return Usage{}, err
77+
}
78+
79+
return Usage{
80+
Inodes: int64(len(inodes)),
81+
Size: size,
82+
}, nil
83+
}

fs/du_windows.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package fs
44

55
import (
6+
"context"
67
"os"
78
"path/filepath"
89
)
@@ -31,3 +32,29 @@ func diskUsage(roots ...string) (Usage, error) {
3132
Size: size,
3233
}, nil
3334
}
35+
36+
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
37+
var (
38+
size int64
39+
)
40+
41+
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
42+
if err != nil {
43+
return err
44+
}
45+
46+
if kind == ChangeKindAdd || kind == ChangeKindModify {
47+
size += fi.Size()
48+
49+
return nil
50+
51+
}
52+
return nil
53+
}); err != nil {
54+
return Usage{}, err
55+
}
56+
57+
return Usage{
58+
Size: size,
59+
}, nil
60+
}

snapshots/btrfs/btrfs.go

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111

1212
"github.com/containerd/btrfs"
13+
"github.com/containerd/containerd/fs"
1314
"github.com/containerd/containerd/log"
1415
"github.com/containerd/containerd/mount"
1516
"github.com/containerd/containerd/platforms"
@@ -128,18 +129,43 @@ func (b *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpath
128129

129130
// Usage retrieves the disk usage of the top-level snapshot.
130131
func (b *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
131-
panic("not implemented")
132-
133-
// TODO(stevvooe): Btrfs has a quota model where data can be exclusive to a
134-
// snapshot or shared among other resources. We may find that this is the
135-
// correct value to reoprt but the stability of the implementation is under
136-
// question.
137-
//
138-
// In general, this has impact on the model we choose for reporting usage.
139-
// Ideally, the value should allow aggregration. For overlay, this is
140-
// simple since we can scan the diff directory to get a unique value. This
141-
// breaks down when start looking the behavior when data is shared between
142-
// snapshots, such as that for btrfs.
132+
return b.usage(ctx, key)
133+
}
134+
135+
func (b *snapshotter) usage(ctx context.Context, key string) (snapshots.Usage, error) {
136+
ctx, t, err := b.ms.TransactionContext(ctx, false)
137+
if err != nil {
138+
return snapshots.Usage{}, err
139+
}
140+
id, info, usage, err := storage.GetInfo(ctx, key)
141+
var parentID string
142+
if err == nil && info.Kind == snapshots.KindActive && info.Parent != "" {
143+
parentID, _, _, err = storage.GetInfo(ctx, info.Parent)
144+
145+
}
146+
t.Rollback() // transaction no longer needed at this point.
147+
148+
if err != nil {
149+
return snapshots.Usage{}, err
150+
}
151+
152+
if info.Kind == snapshots.KindActive {
153+
var du fs.Usage
154+
p := filepath.Join(b.root, "active", id)
155+
if parentID != "" {
156+
du, err = fs.DiffUsage(ctx, filepath.Join(b.root, "snapshots", parentID), p)
157+
} else {
158+
du, err = fs.DiskUsage(p)
159+
}
160+
if err != nil {
161+
// TODO(stevvooe): Consider not reporting an error in this case.
162+
return snapshots.Usage{}, err
163+
}
164+
165+
usage = snapshots.Usage(du)
166+
}
167+
168+
return usage, nil
143169
}
144170

145171
// Walk the committed snapshots.
@@ -238,6 +264,11 @@ func (b *snapshotter) mounts(dir string, s storage.Snapshot) ([]mount.Mount, err
238264
}
239265

240266
func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (err error) {
267+
usage, err := b.usage(ctx, key)
268+
if err != nil {
269+
return errors.Wrap(err, "failed to compute usage")
270+
}
271+
241272
ctx, t, err := b.ms.TransactionContext(ctx, true)
242273
if err != nil {
243274
return err
@@ -250,7 +281,7 @@ func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
250281
}
251282
}()
252283

253-
id, err := storage.CommitActive(ctx, key, name, snapshots.Usage{}, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
284+
id, err := storage.CommitActive(ctx, key, name, usage, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
254285
if err != nil {
255286
return errors.Wrap(err, "failed to commit")
256287
}

0 commit comments

Comments
 (0)