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 <derek@mcgstyle.net>
This commit is contained in:
parent
f6df9f6632
commit
3bc4e69ba1
9
fs/du.go
9
fs/du.go
@ -1,5 +1,7 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// Usage of disk information
|
// Usage of disk information
|
||||||
type Usage struct {
|
type Usage struct {
|
||||||
Inodes int64
|
Inodes int64
|
||||||
@ -11,3 +13,10 @@ type Usage struct {
|
|||||||
func DiskUsage(roots ...string) (Usage, error) {
|
func DiskUsage(roots ...string) (Usage, error) {
|
||||||
return diskUsage(roots...)
|
return diskUsage(roots...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DiffUsage counts the numbers of inodes and disk usage in the
|
||||||
|
// diff between the 2 directories. The first path is intended
|
||||||
|
// as the base directory and the second as the changed directory.
|
||||||
|
func DiffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||||
|
return diffUsage(ctx, a, b)
|
||||||
|
}
|
||||||
|
@ -3,18 +3,20 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func diskUsage(roots ...string) (Usage, error) {
|
|
||||||
type inode struct {
|
type inode struct {
|
||||||
// TODO(stevvooe): Can probably reduce memory usage by not tracking
|
// TODO(stevvooe): Can probably reduce memory usage by not tracking
|
||||||
// device, but we can leave this right for now.
|
// device, but we can leave this right for now.
|
||||||
dev, ino uint64
|
dev, ino uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func diskUsage(roots ...string) (Usage, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
size int64
|
size int64
|
||||||
inodes = map[inode]struct{}{} // expensive!
|
inodes = map[inode]struct{}{} // expensive!
|
||||||
@ -45,3 +47,37 @@ func diskUsage(roots ...string) (Usage, error) {
|
|||||||
Size: size,
|
Size: size,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||||
|
var (
|
||||||
|
size int64
|
||||||
|
inodes = map[inode]struct{}{} // expensive!
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
||||||
|
stat := fi.Sys().(*syscall.Stat_t)
|
||||||
|
|
||||||
|
inoKey := inode{dev: uint64(stat.Dev), ino: uint64(stat.Ino)}
|
||||||
|
if _, ok := inodes[inoKey]; !ok {
|
||||||
|
inodes[inoKey] = struct{}{}
|
||||||
|
size += fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Usage{
|
||||||
|
Inodes: int64(len(inodes)),
|
||||||
|
Size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -31,3 +32,29 @@ func diskUsage(roots ...string) (Usage, error) {
|
|||||||
Size: size,
|
Size: size,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||||
|
var (
|
||||||
|
size int64
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
||||||
|
size += fi.Size()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
return Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Usage{
|
||||||
|
Size: size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/btrfs"
|
"github.com/containerd/btrfs"
|
||||||
|
"github.com/containerd/containerd/fs"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
@ -128,18 +129,43 @@ func (b *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpath
|
|||||||
|
|
||||||
// Usage retrieves the disk usage of the top-level snapshot.
|
// Usage retrieves the disk usage of the top-level snapshot.
|
||||||
func (b *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
func (b *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
||||||
panic("not implemented")
|
return b.usage(ctx, key)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(stevvooe): Btrfs has a quota model where data can be exclusive to a
|
func (b *snapshotter) usage(ctx context.Context, key string) (snapshots.Usage, error) {
|
||||||
// snapshot or shared among other resources. We may find that this is the
|
ctx, t, err := b.ms.TransactionContext(ctx, false)
|
||||||
// correct value to reoprt but the stability of the implementation is under
|
if err != nil {
|
||||||
// question.
|
return snapshots.Usage{}, err
|
||||||
//
|
}
|
||||||
// In general, this has impact on the model we choose for reporting usage.
|
id, info, usage, err := storage.GetInfo(ctx, key)
|
||||||
// Ideally, the value should allow aggregration. For overlay, this is
|
var parentID string
|
||||||
// simple since we can scan the diff directory to get a unique value. This
|
if err == nil && info.Kind == snapshots.KindActive && info.Parent != "" {
|
||||||
// breaks down when start looking the behavior when data is shared between
|
parentID, _, _, err = storage.GetInfo(ctx, info.Parent)
|
||||||
// snapshots, such as that for btrfs.
|
|
||||||
|
}
|
||||||
|
t.Rollback() // transaction no longer needed at this point.
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Kind == snapshots.KindActive {
|
||||||
|
var du fs.Usage
|
||||||
|
p := filepath.Join(b.root, "active", id)
|
||||||
|
if parentID != "" {
|
||||||
|
du, err = fs.DiffUsage(ctx, filepath.Join(b.root, "snapshots", parentID), p)
|
||||||
|
} else {
|
||||||
|
du, err = fs.DiskUsage(p)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// TODO(stevvooe): Consider not reporting an error in this case.
|
||||||
|
return snapshots.Usage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
usage = snapshots.Usage(du)
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the committed snapshots.
|
// Walk the committed snapshots.
|
||||||
@ -238,6 +264,11 @@ func (b *snapshotter) mounts(dir string, s storage.Snapshot) ([]mount.Mount, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (err error) {
|
func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) (err error) {
|
||||||
|
usage, err := b.usage(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to compute usage")
|
||||||
|
}
|
||||||
|
|
||||||
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
ctx, t, err := b.ms.TransactionContext(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -250,7 +281,7 @@ func (b *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
id, err := storage.CommitActive(ctx, key, name, snapshots.Usage{}, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
|
id, err := storage.CommitActive(ctx, key, name, usage, opts...) // TODO(stevvooe): Resolve a usage value for btrfs
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to commit")
|
return errors.Wrap(err, "failed to commit")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user