mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-29 03:04:24 +01:00
As github.com/containerd/cgroups doesn't support scope units which are essential in some cases lets create the cgroups manually and load it trough the cgroups api This is currently done only when there's single sandbox cgroup (sandbox_cgroup_only=true), otherwise we set it as static cgroup path as it used to be (until a proper soultion for overhead cgroup under systemd will be suggested) Fixes: #2868 Signed-off-by: Snir Sheriber <ssheribe@redhat.com>
240 lines
5.1 KiB
Go
240 lines
5.1 KiB
Go
package ebpf
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/cilium/ebpf/internal"
|
|
)
|
|
|
|
// MapInfo describes a map.
|
|
type MapInfo struct {
|
|
Type MapType
|
|
id MapID
|
|
KeySize uint32
|
|
ValueSize uint32
|
|
MaxEntries uint32
|
|
Flags uint32
|
|
// Name as supplied by user space at load time.
|
|
Name string
|
|
}
|
|
|
|
func newMapInfoFromFd(fd *internal.FD) (*MapInfo, error) {
|
|
info, err := bpfGetMapInfoByFD(fd)
|
|
if errors.Is(err, syscall.EINVAL) {
|
|
return newMapInfoFromProc(fd)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &MapInfo{
|
|
MapType(info.map_type),
|
|
MapID(info.id),
|
|
info.key_size,
|
|
info.value_size,
|
|
info.max_entries,
|
|
info.map_flags,
|
|
// name is available from 4.15.
|
|
internal.CString(info.name[:]),
|
|
}, nil
|
|
}
|
|
|
|
func newMapInfoFromProc(fd *internal.FD) (*MapInfo, error) {
|
|
var mi MapInfo
|
|
err := scanFdInfo(fd, map[string]interface{}{
|
|
"map_type": &mi.Type,
|
|
"key_size": &mi.KeySize,
|
|
"value_size": &mi.ValueSize,
|
|
"max_entries": &mi.MaxEntries,
|
|
"map_flags": &mi.Flags,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &mi, nil
|
|
}
|
|
|
|
// ID returns the map ID.
|
|
//
|
|
// Available from 4.13.
|
|
//
|
|
// The bool return value indicates whether this optional field is available.
|
|
func (mi *MapInfo) ID() (MapID, bool) {
|
|
return mi.id, mi.id > 0
|
|
}
|
|
|
|
// programStats holds statistics of a program.
|
|
type programStats struct {
|
|
// Total accumulated runtime of the program ins ns.
|
|
runtime time.Duration
|
|
// Total number of times the program was called.
|
|
runCount uint64
|
|
}
|
|
|
|
// ProgramInfo describes a program.
|
|
type ProgramInfo struct {
|
|
Type ProgramType
|
|
id ProgramID
|
|
// Truncated hash of the BPF bytecode.
|
|
Tag string
|
|
// Name as supplied by user space at load time.
|
|
Name string
|
|
|
|
stats *programStats
|
|
}
|
|
|
|
func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
|
|
info, err := bpfGetProgInfoByFD(fd)
|
|
if errors.Is(err, syscall.EINVAL) {
|
|
return newProgramInfoFromProc(fd)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ProgramInfo{
|
|
Type: ProgramType(info.prog_type),
|
|
id: ProgramID(info.id),
|
|
// tag is available if the kernel supports BPF_PROG_GET_INFO_BY_FD.
|
|
Tag: hex.EncodeToString(info.tag[:]),
|
|
// name is available from 4.15.
|
|
Name: internal.CString(info.name[:]),
|
|
stats: &programStats{
|
|
runtime: time.Duration(info.run_time_ns),
|
|
runCount: info.run_cnt,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func newProgramInfoFromProc(fd *internal.FD) (*ProgramInfo, error) {
|
|
var info ProgramInfo
|
|
err := scanFdInfo(fd, map[string]interface{}{
|
|
"prog_type": &info.Type,
|
|
"prog_tag": &info.Tag,
|
|
})
|
|
if errors.Is(err, errMissingFields) {
|
|
return nil, &internal.UnsupportedFeatureError{
|
|
Name: "reading program info from /proc/self/fdinfo",
|
|
MinimumVersion: internal.Version{4, 10, 0},
|
|
}
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &info, nil
|
|
}
|
|
|
|
// ID returns the program ID.
|
|
//
|
|
// Available from 4.13.
|
|
//
|
|
// The bool return value indicates whether this optional field is available.
|
|
func (pi *ProgramInfo) ID() (ProgramID, bool) {
|
|
return pi.id, pi.id > 0
|
|
}
|
|
|
|
// RunCount returns the total number of times the program was called.
|
|
//
|
|
// Can return 0 if the collection of statistics is not enabled. See EnableStats().
|
|
// The bool return value indicates whether this optional field is available.
|
|
func (pi *ProgramInfo) RunCount() (uint64, bool) {
|
|
if pi.stats != nil {
|
|
return pi.stats.runCount, true
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
// Runtime returns the total accumulated runtime of the program.
|
|
//
|
|
// Can return 0 if the collection of statistics is not enabled. See EnableStats().
|
|
// The bool return value indicates whether this optional field is available.
|
|
func (pi *ProgramInfo) Runtime() (time.Duration, bool) {
|
|
if pi.stats != nil {
|
|
return pi.stats.runtime, true
|
|
}
|
|
return time.Duration(0), false
|
|
}
|
|
|
|
func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
|
|
raw, err := fd.Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", raw))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fh.Close()
|
|
|
|
if err := scanFdInfoReader(fh, fields); err != nil {
|
|
return fmt.Errorf("%s: %w", fh.Name(), err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var errMissingFields = errors.New("missing fields")
|
|
|
|
func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
|
|
var (
|
|
scanner = bufio.NewScanner(r)
|
|
scanned int
|
|
)
|
|
|
|
for scanner.Scan() {
|
|
parts := strings.SplitN(scanner.Text(), "\t", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
|
|
name := strings.TrimSuffix(parts[0], ":")
|
|
field, ok := fields[string(name)]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 {
|
|
return fmt.Errorf("can't parse field %s: %v", name, err)
|
|
}
|
|
|
|
scanned++
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if scanned != len(fields) {
|
|
return errMissingFields
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// EnableStats starts the measuring of the runtime
|
|
// and run counts of eBPF programs.
|
|
//
|
|
// Collecting statistics can have an impact on the performance.
|
|
//
|
|
// Requires at least 5.8.
|
|
func EnableStats(which uint32) (io.Closer, error) {
|
|
attr := internal.BPFEnableStatsAttr{
|
|
StatsType: which,
|
|
}
|
|
|
|
fd, err := internal.BPFEnableStats(&attr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fd, nil
|
|
}
|