/* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package opts import ( "context" "errors" "fmt" "os" "path/filepath" "strings" "sync" "syscall" "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" "github.com/containerd/cgroups/v3" "golang.org/x/sys/unix" runtime "k8s.io/cri-api/pkg/apis/runtime/v1" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/oci" "github.com/containerd/log" ) // Linux dependent OCI spec opts. var ( swapControllerAvailability bool swapControllerAvailabilityOnce sync.Once ) // SwapControllerAvailable returns true if the swap controller is available func SwapControllerAvailable() bool { swapControllerAvailabilityOnce.Do(func() { const warn = "Failed to detect the availability of the swap controller, assuming not available" p := "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes" if cgroups.Mode() == cgroups.Unified { // memory.swap.max does not exist in the cgroup root, so we check /sys/fs/cgroup//memory.swap.max _, unified, err := cgroups.ParseCgroupFileUnified("/proc/self/cgroup") if err != nil { err = fmt.Errorf("failed to parse /proc/self/cgroup: %w", err) log.L.WithError(err).Warn(warn) return } p = filepath.Join("/sys/fs/cgroup", unified, "memory.swap.max") } if _, err := os.Stat(p); err != nil { if !errors.Is(err, os.ErrNotExist) { log.L.WithError(err).Warn(warn) } return } swapControllerAvailability = true }) return swapControllerAvailability } var ( supportsHugetlbOnce sync.Once supportsHugetlb bool ) func isHugetlbControllerPresent() bool { supportsHugetlbOnce.Do(func() { supportsHugetlb = false if IsCgroup2UnifiedMode() { supportsHugetlb = cgroupv2HasHugetlb() } else { supportsHugetlb = cgroupv1HasHugetlb() } }) return supportsHugetlb } var ( _cgroupv1HasHugetlbOnce sync.Once _cgroupv1HasHugetlb bool _cgroupv2HasHugetlbOnce sync.Once _cgroupv2HasHugetlb bool isUnifiedOnce sync.Once isUnified bool ) // cgroupv1HasHugetlb returns whether the hugetlb controller is present on // cgroup v1. func cgroupv1HasHugetlb() bool { _cgroupv1HasHugetlbOnce.Do(func() { if _, err := os.ReadDir("/sys/fs/cgroup/hugetlb"); err != nil { _cgroupv1HasHugetlb = false } else { _cgroupv1HasHugetlb = true } }) return _cgroupv1HasHugetlb } // cgroupv2HasHugetlb returns whether the hugetlb controller is present on // cgroup v2. func cgroupv2HasHugetlb() bool { _cgroupv2HasHugetlbOnce.Do(func() { controllers, err := os.ReadFile("/sys/fs/cgroup/cgroup.controllers") if err != nil { return } _cgroupv2HasHugetlb = strings.Contains(string(controllers), "hugetlb") }) return _cgroupv2HasHugetlb } // IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode. func IsCgroup2UnifiedMode() bool { isUnifiedOnce.Do(func() { var st syscall.Statfs_t if err := syscall.Statfs("/sys/fs/cgroup", &st); err != nil { panic("cannot statfs cgroup root") } isUnified = st.Type == unix.CGROUP2_SUPER_MAGIC }) return isUnified } // WithCDI updates OCI spec with CDI content func WithCDI(annotations map[string]string, CDIDevices []*runtime.CDIDevice) oci.SpecOpts { return func(ctx context.Context, client oci.Client, c *containers.Container, s *oci.Spec) error { seen := make(map[string]bool) // Add devices from CDIDevices CRI field var devices []string var err error for _, device := range CDIDevices { deviceName := device.Name if seen[deviceName] { log.G(ctx).Debugf("Skipping duplicated CDI device %s", deviceName) continue } devices = append(devices, deviceName) seen[deviceName] = true } log.G(ctx).Infof("Container %v: CDI devices from CRI Config.CDIDevices: %v", c.ID, devices) // Add devices from CDI annotations _, devsFromAnnotations, err := cdi.ParseAnnotations(annotations) if err != nil { return fmt.Errorf("failed to parse CDI device annotations: %w", err) } if devsFromAnnotations != nil { log.G(ctx).Infof("Container %v: CDI devices from annotations: %v", c.ID, devsFromAnnotations) for _, deviceName := range devsFromAnnotations { if seen[deviceName] { // TODO: change to Warning when passing CDI devices as annotations is deprecated log.G(ctx).Debugf("Skipping duplicated CDI device %s", deviceName) continue } devices = append(devices, deviceName) seen[deviceName] = true } // TODO: change to Warning when passing CDI devices as annotations is deprecated log.G(ctx).Debug("Passing CDI devices as annotations will be deprecated soon, please use CRI CDIDevices instead") } return oci.WithCDIDevices(devices...)(ctx, client, c, s) } }