github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/cgroups/fs2/psi.go (about)

     1  package fs2
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"golang.org/x/sys/unix"
    12  
    13  	"github.com/opencontainers/runc/libcontainer/cgroups"
    14  )
    15  
    16  func statPSI(dirPath string, file string) (*cgroups.PSIStats, error) {
    17  	f, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY)
    18  	if err != nil {
    19  		if errors.Is(err, os.ErrNotExist) {
    20  			// Kernel < 4.20, or CONFIG_PSI is not set,
    21  			// or PSI stats are turned off for the cgroup
    22  			// ("echo 0 > cgroup.pressure", kernel >= 6.1).
    23  			return nil, nil
    24  		}
    25  		return nil, err
    26  	}
    27  	defer f.Close()
    28  
    29  	var psistats cgroups.PSIStats
    30  	sc := bufio.NewScanner(f)
    31  	for sc.Scan() {
    32  		parts := strings.Fields(sc.Text())
    33  		var pv *cgroups.PSIData
    34  		switch parts[0] {
    35  		case "some":
    36  			pv = &psistats.Some
    37  		case "full":
    38  			pv = &psistats.Full
    39  		}
    40  		if pv != nil {
    41  			*pv, err = parsePSIData(parts[1:])
    42  			if err != nil {
    43  				return nil, &parseError{Path: dirPath, File: file, Err: err}
    44  			}
    45  		}
    46  	}
    47  	if err := sc.Err(); err != nil {
    48  		if errors.Is(err, unix.ENOTSUP) {
    49  			// Some kernels (e.g. CS9) may return ENOTSUP on read
    50  			// if psi=1 kernel cmdline parameter is required.
    51  			return nil, nil
    52  		}
    53  		return nil, &parseError{Path: dirPath, File: file, Err: err}
    54  	}
    55  	return &psistats, nil
    56  }
    57  
    58  func parsePSIData(psi []string) (cgroups.PSIData, error) {
    59  	data := cgroups.PSIData{}
    60  	for _, f := range psi {
    61  		kv := strings.SplitN(f, "=", 2)
    62  		if len(kv) != 2 {
    63  			return data, fmt.Errorf("invalid psi data: %q", f)
    64  		}
    65  		var pv *float64
    66  		switch kv[0] {
    67  		case "avg10":
    68  			pv = &data.Avg10
    69  		case "avg60":
    70  			pv = &data.Avg60
    71  		case "avg300":
    72  			pv = &data.Avg300
    73  		case "total":
    74  			v, err := strconv.ParseUint(kv[1], 10, 64)
    75  			if err != nil {
    76  				return data, fmt.Errorf("invalid %s PSI value: %w", kv[0], err)
    77  			}
    78  			data.Total = v
    79  		}
    80  		if pv != nil {
    81  			v, err := strconv.ParseFloat(kv[1], 64)
    82  			if err != nil {
    83  				return data, fmt.Errorf("invalid %s PSI value: %w", kv[0], err)
    84  			}
    85  			*pv = v
    86  		}
    87  	}
    88  	return data, nil
    89  }