Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cmd/containerd/command/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/containerd/containerd/services/server"
srvconfig "github.com/containerd/containerd/services/server/config"
"github.com/containerd/containerd/sys"
"github.com/containerd/containerd/sys/blkiorun"
"github.com/containerd/containerd/version"
)

Expand Down Expand Up @@ -132,6 +133,16 @@ can be used and modified as necessary as a custom configuration.`
return err
}

// Initialize block IO weight control (Linux only, no-op on other platforms)
blkioConfig := config.Cgroup.Blkio
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Weight field is typed as int in BlkioConfig but is being cast to uint16 here. This could cause issues if a negative value is provided in the configuration. Consider validating that blkioConfig.Weight is non-negative before casting, or change the BlkioConfig.Weight field type to uint16 to prevent negative values at the configuration level.

Suggested change
blkioConfig := config.Cgroup.Blkio
blkioConfig := config.Cgroup.Blkio
if blkioConfig.Weight < 0 {
return fmt.Errorf("invalid blkio weight %d: must be non-negative: %w", blkioConfig.Weight, errdefs.ErrInvalidArgument)
}

Copilot uses AI. Check for mistakes.
if err := blkiorun.Init(
blkiorun.Config{Weight: uint16(blkioConfig.Weight)},
blkioConfig.SlicePath,
blkioConfig.SliceName,
); err != nil {
log.G(ctx).WithError(err).Warn("failed to initialize blkio control")
}

if config.GRPC.Address == "" {
return fmt.Errorf("grpc address cannot be empty: %w", errdefs.ErrInvalidArgument)
}
Expand Down
9 changes: 7 additions & 2 deletions pkg/unpack/unpacker.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/containerd/containerd/pkg/cleanup"
"github.com/containerd/containerd/pkg/kmutex"
"github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/sys/blkiorun"
"github.com/containerd/containerd/tracing"
)

Expand Down Expand Up @@ -374,7 +375,9 @@ func (u *Unpacker) unpack(
case <-fetchC[i-fetchOffset]:
}

diff, err := a.Apply(ctx, desc, mounts, unpack.ApplyOpts...)
diff, err := blkiorun.Local(func() (ocispec.Descriptor, error) {
return a.Apply(ctx, desc, mounts, unpack.ApplyOpts...)
})
if err != nil {
cleanup.Do(ctx, abort)
return fmt.Errorf("failed to extract layer %s: %w", diffIDs[i], err)
Expand Down Expand Up @@ -474,7 +477,9 @@ func (u *Unpacker) fetch(ctx context.Context, h images.Handler, layers []ocispec
return err
}

_, err = h.Handle(ctx2, desc)
_, err = blkiorun.Local(func() ([]ocispec.Descriptor, error) {
return h.Handle(ctx2, desc)
})

unlock()
u.release()
Expand Down
9 changes: 9 additions & 0 deletions pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
"github.com/containerd/containerd/remotes/docker/schema1" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
"github.com/containerd/containerd/sys/blkiorun"
"github.com/containerd/containerd/tracing"
)

Expand All @@ -53,6 +54,14 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
}
}

// Run with configured IO weight (if enabled)
return blkiorun.Go(func() (Image, error) {
return c.pull(ctx, ref, pullCtx, span)
})
}

// pull is the internal implementation of Pull that performs the actual work.
func (c *Client) pull(ctx context.Context, ref string, pullCtx *RemoteContext, span *tracing.Span) (_ Image, retErr error) {
if pullCtx.PlatformMatcher == nil {
if len(pullCtx.Platforms) > 1 {
return nil, errors.New("cannot pull multiplatform image locally, try Fetch")
Expand Down
17 changes: 17 additions & 0 deletions services/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,23 @@ type MetricsConfig struct {
// CgroupConfig provides cgroup configuration
type CgroupConfig struct {
Path string `toml:"path"`
// Blkio configures block IO control for containerd operations (cgroups v2)
Blkio BlkioConfig `toml:"blkio"`
}

// BlkioConfig provides block IO configuration for cgroups v2
type BlkioConfig struct {
// Weight is the IO weight value (10-1000) for image pull, unpack, and commit operations.
// Set to 0 to disable IO weight control. Default is 0 (disabled).
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states "Set to 0 to disable IO weight control" but doesn't mention that values outside the range 10-1000 will also result in the feature being disabled. Consider clarifying this behavior in the documentation or adding a note that invalid values will disable the feature with a warning.

Suggested change
// Set to 0 to disable IO weight control. Default is 0 (disabled).
// Set to 0 to disable IO weight control. Default is 0 (disabled). Any non-zero value
// outside the valid range (10-1000) will be treated as invalid, causing IO weight
// control to be disabled and a warning to be emitted.

Copilot uses AI. Check for mistakes.
Weight int `toml:"weight"`
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Weight field is defined as int which allows negative values. Since the valid range is 10-1000 (or 0 to disable), negative values don't make semantic sense. Consider changing the type to uint16 to match the internal Config type and prevent invalid negative values from being specified in configuration files.

Suggested change
Weight int `toml:"weight"`
Weight uint16 `toml:"weight"`

Copilot uses AI. Check for mistakes.
// SlicePath is the path to an existing cgroup for IO weight control.
// If specified, this cgroup will be used directly (must already exist with io controller enabled).
// If empty, a transient systemd slice will be created via D-Bus using SliceName.
SlicePath string `toml:"slice_path"`
// SliceName is the systemd slice name for IO weight control (e.g., "containerdio.slice").
// Only used when SlicePath is empty. The slice will be created as a transient unit via systemd D-Bus.
// Leave empty to use default: "containerdio.slice"
SliceName string `toml:"slice_name"`
}

// ProxyPlugin provides a proxy plugin configuration
Expand Down
9 changes: 8 additions & 1 deletion services/snapshots/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/containerd/containerd/services"
"github.com/containerd/containerd/services/warning"
"github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/sys/blkiorun"
"github.com/containerd/log"
)

Expand Down Expand Up @@ -165,7 +166,13 @@ func (s *service) Commit(ctx context.Context, cr *snapshotsapi.CommitSnapshotReq
if cr.Labels != nil {
opts = append(opts, snapshots.WithLabels(cr.Labels))
}
if err := sn.Commit(ctx, cr.Name, cr.Key, opts...); err != nil {

// Run with configured IO weight (if enabled)
_, err = blkiorun.Go(func() (struct{}, error) {
return struct{}{}, sn.Commit(ctx, cr.Name, cr.Key, opts...)
})

if err != nil {
return nil, errdefs.ToGRPC(err)
Comment on lines +171 to 176
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error from blkiorun.Go is assigned to err, but this shadows the original err variable. If the Commit operation succeeds but there's an error in the blkiorun.Go wrapper itself (which shouldn't happen since it's returning a struct{} and the actual error), this could lead to confusion. Consider using a different variable name or ensuring the error handling is clear.

Suggested change
_, err = blkiorun.Go(func() (struct{}, error) {
return struct{}{}, sn.Commit(ctx, cr.Name, cr.Key, opts...)
})
if err != nil {
return nil, errdefs.ToGRPC(err)
_, commitErr := blkiorun.Go(func() (struct{}, error) {
return struct{}{}, sn.Commit(ctx, cr.Name, cr.Key, opts...)
})
if commitErr != nil {
return nil, errdefs.ToGRPC(commitErr)

Copilot uses AI. Check for mistakes.
}

Expand Down
11 changes: 7 additions & 4 deletions snapshots/devbox/devbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/snapshots/overlay/overlayutils"
"github.com/containerd/containerd/sys/blkiorun"
"github.com/containerd/continuity/fs"
"github.com/containerd/errdefs"
"github.com/containerd/log"
Expand Down Expand Up @@ -381,10 +382,12 @@ func (o *Snapshotter) RemoveDir(ctx context.Context, dir string) {
return
}
} else {
if err1 := os.RemoveAll(dir); err1 != nil {
log.G(ctx).WithError(err1).WithField("path", dir).Warn("failed to remove directory")
return
}
blkiorun.Go(func() (struct{}, error) {
if err1 := os.RemoveAll(dir); err1 != nil {
log.G(ctx).WithError(err1).WithField("path", dir).Warn("failed to remove directory")
}
return struct{}{}, nil
})
}
}

Expand Down
12 changes: 8 additions & 4 deletions snapshots/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/snapshots/overlay/overlayutils"
"github.com/containerd/containerd/snapshots/storage"
"github.com/containerd/containerd/sys/blkiorun"
"github.com/containerd/continuity/fs"
"github.com/containerd/log"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -352,11 +353,14 @@ func (o *snapshotter) Cleanup(ctx context.Context) error {
return err
}

for _, dir := range cleanup {
if err := os.RemoveAll(dir); err != nil {
log.G(ctx).WithError(err).WithField("path", dir).Warn("failed to remove directory")
blkiorun.Go(func() (struct{}, error) {
for _, dir := range cleanup {
if err := os.RemoveAll(dir); err != nil {
log.G(ctx).WithError(err).WithField("path", dir).Warn("failed to remove directory")
}
}
}
return struct{}{}, nil
})

return nil
}
Expand Down
Loading