178 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   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/containerd/cgroups/v3"
 | 
						|
	"golang.org/x/sys/unix"
 | 
						|
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
 | 
						|
	"tags.cncf.io/container-device-interface/pkg/cdi"
 | 
						|
 | 
						|
	"github.com/containerd/containerd/v2/core/containers"
 | 
						|
	"github.com/containerd/containerd/v2/pkg/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/<SELF>/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)
 | 
						|
	}
 | 
						|
}
 |