github.com/intel/goresctrl@v0.5.0/cmd/sst-ctl/main.go (about)

     1  /*
     2  Copyright 2021 Intel Corporation
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"os"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  
    27  	goresctrlpath "github.com/intel/goresctrl/pkg/path"
    28  	"github.com/intel/goresctrl/pkg/sst"
    29  	"github.com/intel/goresctrl/pkg/utils"
    30  )
    31  
    32  var (
    33  	// Global command line flags
    34  	packageIds string
    35  )
    36  
    37  type subCmd func([]string) error
    38  
    39  var subCmds = map[string]subCmd{
    40  	"info": subCmdInfo,
    41  	"bf":   subCmdBF,
    42  	"cp":   subCmdCP,
    43  }
    44  
    45  func main() {
    46  	cmds := make([]string, 0, len(subCmds))
    47  	for c := range subCmds {
    48  		cmds = append(cmds, c)
    49  	}
    50  	sort.Strings(cmds)
    51  	allCmds := strings.Join(cmds, ", ")
    52  
    53  	if len(os.Args) < 2 {
    54  		fmt.Printf("missing sub-command, must be one of: %s\n", allCmds)
    55  		os.Exit(1)
    56  	}
    57  
    58  	// Run sub-command
    59  	cmd, ok := subCmds[os.Args[1]]
    60  	if !ok {
    61  		fmt.Printf("unknown sub-command %q, must be of: %s\n", os.Args[1], allCmds)
    62  		os.Exit(1)
    63  	}
    64  
    65  	if err := cmd(os.Args[2:]); err != nil {
    66  		fmt.Printf("ERROR: sub-command %q failed: %v\n", os.Args[1], err)
    67  		os.Exit(1)
    68  	}
    69  }
    70  
    71  func addGlobalFlags(flagset *flag.FlagSet) {
    72  	flagset.StringVar(&packageIds, "package", "", "One or more physical package id")
    73  	flagset.Func("prefix", "set mount prefix for system directories", func(s string) error {
    74  		goresctrlpath.SetPrefix(s)
    75  		return nil
    76  	})
    77  }
    78  
    79  func printPackageInfo(pkgId ...int) error {
    80  	info, err := sst.GetPackageInfo(pkgId...)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	fmt.Println(utils.DumpJSON(info))
    86  
    87  	return nil
    88  }
    89  
    90  // TODO: Move this functionality into utils.NewIdSetFromString()
    91  func str2slice(str string) []int {
    92  	var s []int
    93  
    94  	for _, str := range strings.Split(str, ",") {
    95  		if str == "" {
    96  			continue
    97  		}
    98  
    99  		id, err := strconv.ParseUint(str, 10, 0)
   100  		if err != nil {
   101  			fmt.Printf("invalid value '%s': %v", str, err)
   102  			continue
   103  		}
   104  
   105  		s = append(s, int(id))
   106  	}
   107  
   108  	return s
   109  }
   110  
   111  func subCmdInfo(args []string) error {
   112  	// Parse command line args
   113  	flags := flag.NewFlagSet("info", flag.ExitOnError)
   114  	addGlobalFlags(flags)
   115  	if err := flags.Parse(args); err != nil {
   116  		return err
   117  	}
   118  
   119  	return printPackageInfo(str2slice(packageIds)...)
   120  }
   121  
   122  func enableBF(pkgId ...int) error {
   123  	if len(pkgId) == 0 {
   124  		fmt.Printf("Enabling BF for all packages\n")
   125  	} else {
   126  		fmt.Printf("Enabling BF for package(s) %v\n", pkgId)
   127  	}
   128  
   129  	err := sst.EnableBF(pkgId...)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	return printPackageInfo(pkgId...)
   135  }
   136  
   137  func disableBF(pkgId ...int) error {
   138  	if len(pkgId) == 0 {
   139  		fmt.Printf("Disabling BF for all packages\n")
   140  	} else {
   141  		fmt.Printf("Disabling BF for package(s) %v\n", pkgId)
   142  	}
   143  
   144  	err := sst.DisableBF(pkgId...)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	return printPackageInfo(pkgId...)
   150  }
   151  
   152  func subCmdBF(args []string) error {
   153  	var enable, disable bool
   154  
   155  	flags := flag.NewFlagSet("bf", flag.ExitOnError)
   156  	flags.BoolVar(&enable, "enable", false, "enable feature")
   157  	flags.BoolVar(&disable, "disable", false, "disable feature")
   158  	addGlobalFlags(flags)
   159  
   160  	if err := flags.Parse(args); err != nil {
   161  		return err
   162  	}
   163  
   164  	if (!enable && !disable) || (enable && disable) {
   165  		fmt.Printf("Please provide either -enable or -disable flag\n")
   166  		return nil
   167  	}
   168  
   169  	var err error
   170  
   171  	pkgs := str2slice(packageIds)
   172  
   173  	if enable {
   174  		err = enableBF(pkgs...)
   175  	} else {
   176  		err = disableBF(pkgs...)
   177  	}
   178  
   179  	return err
   180  }
   181  
   182  func getPackage(packageStr string, cpus utils.IDSet) (map[int]*sst.SstPackageInfo, *sst.SstPackageInfo, []int, error) {
   183  	var infomap map[int]*sst.SstPackageInfo
   184  	var info *sst.SstPackageInfo
   185  	var packageId int
   186  	var err error
   187  
   188  	// If user has specified a package, then all the CPUs must belong to it.
   189  	pkgs := str2slice(packageStr)
   190  	if len(pkgs) > 1 {
   191  		fmt.Printf("Only one package can be configured at a time (you have %d)\n", len(pkgs))
   192  		return nil, nil, nil, fmt.Errorf("Provide one package value only")
   193  	}
   194  
   195  	if len(pkgs) == 0 {
   196  		// User has not specified a package, figure it out from the
   197  		// first CPU in the list.
   198  		infomap, err = sst.GetPackageInfo()
   199  		if err != nil {
   200  			return nil, nil, nil, err
   201  		}
   202  
   203  		for packageId, info = range infomap {
   204  			if sst.CheckPackageCpus(info, cpus) {
   205  				pkgs = append(pkgs, packageId)
   206  				break
   207  			}
   208  		}
   209  	} else {
   210  		// User has specified one package, make sure all the CPUs belong to it.
   211  		infomap, err = sst.GetPackageInfo(pkgs...)
   212  		if err != nil {
   213  			return nil, nil, nil, err
   214  		}
   215  
   216  		for packageId, info = range infomap {
   217  			if !sst.CheckPackageCpus(info, cpus) {
   218  				fmt.Printf("All the CPUs %v must belong to one specific package\n", cpus)
   219  				return nil, nil, nil, fmt.Errorf("Not all CPUs belong to package %d", packageId)
   220  			}
   221  
   222  			pkgs = append(pkgs, packageId)
   223  			break
   224  		}
   225  	}
   226  
   227  	return infomap, info, pkgs, nil
   228  }
   229  
   230  // TODO: Instead of all CP parameters groupped together, separate them like this.
   231  //    sst-ctl cp enable
   232  //    sst-ctl cp disable
   233  //    sst-ctl cp configure -clos=CLOS...
   234  //    sst-ctl cp assign...
   235  
   236  func subCmdCP(args []string) error {
   237  	var enable, disable, reset bool
   238  
   239  	// Clos setup variables
   240  	var epp, minFreq, maxFreq, desiredFreq, proportionalPriority, clos int
   241  
   242  	// CPU to Clos mapping variables
   243  	var cpuStr string
   244  	var cpus utils.IDSet
   245  
   246  	var packageId int
   247  	var pkgs []int
   248  	var info *sst.SstPackageInfo
   249  	var infomap map[int]*sst.SstPackageInfo
   250  	var err error
   251  	var priority int
   252  
   253  	defaultMaxFreq := 0xff
   254  
   255  	flags := flag.NewFlagSet("cp", flag.ExitOnError)
   256  	flags.BoolVar(&enable, "enable", false, "Enable feature")
   257  	flags.BoolVar(&disable, "disable", false, "Disable feature")
   258  	flags.BoolVar(&reset, "reset", false, "Reset CP to a known state")
   259  	flags.IntVar(&clos, "clos", -1, "Class of service (0 - 3)")
   260  	flags.IntVar(&epp, "epp", 0, "Energy Performance Preference value, Lower value favors performance, and higher value favors power. The value can be between 0 and 15. The default value is 0.")
   261  	flags.IntVar(&minFreq, "min", 0, "Clos minimum frequency MHz")
   262  	flags.IntVar(&maxFreq, "max", defaultMaxFreq, "Clos maximum frequency MHz")
   263  	flags.IntVar(&desiredFreq, "desired", 0, "Clos desired frequency MHz")
   264  	flags.IntVar(&proportionalPriority, "proportional", 0, "Clos proportional priority weight. Used if CP priority mode is 0 (Proportional)")
   265  	flags.IntVar(&priority, "priority", 1, "CP priority mode. 0 is Proportional, 1 is Ordered.")
   266  	flags.StringVar(&cpuStr, "cpus", "", "List of CPUs assigned to the Clos.")
   267  
   268  	addGlobalFlags(flags)
   269  
   270  	flags.Usage = func() {
   271  		flags.PrintDefaults()
   272  
   273  		fmt.Fprintf(os.Stderr, "\nExample usage:\n\n")
   274  		fmt.Fprintf(os.Stderr, "First reset CP to default:\n\t%s cp -reset\n\n", os.Args[0])
   275  		fmt.Fprintf(os.Stderr, "Then set the CLOS values:\n\t%s cp -clos 1 -desired 280 -epp 1 -max 30 -min 21 -priority 1 -package 0\n\n", os.Args[0])
   276  		fmt.Fprintf(os.Stderr, "Then bind CPUs to a CLOS:\n\t%s cp -clos 1 -cpus 1,3,5,6\n\n", os.Args[0])
   277  		fmt.Fprintf(os.Stderr, "Finally enable CP:\n\t%s cp -enable -package 0\n\n", os.Args[0])
   278  	}
   279  
   280  	if err := flags.Parse(args); err != nil {
   281  		return err
   282  	}
   283  
   284  	if reset {
   285  		err := sst.ResetCPConfig()
   286  		_ = printPackageInfo()
   287  		return err
   288  	}
   289  
   290  	if enable && disable {
   291  		return fmt.Errorf("Please provide either -enable or -disable flag")
   292  	}
   293  
   294  	// If user specifies a list of CPUs, then he/she wants to assign those
   295  	// CPUs to a specific CLOS. If the -cpus option is not set, then user
   296  	// wants to configure the actual CLOS values. Both operations cannot be
   297  	// done at the same time.
   298  
   299  	// If user specifies a list of CPUs, then the package option is ignored.
   300  	// Verify that all the CPUs belong to one specific package.
   301  	if cpuStr != "" {
   302  		cpus = utils.NewIDSet(str2slice(cpuStr)...)
   303  
   304  		infomap, info, pkgs, err = getPackage(packageIds, cpus)
   305  		if err != nil {
   306  			return fmt.Errorf("Cannot get CPUs %v package: %w", cpus, err)
   307  		}
   308  
   309  		if len(pkgs) == 0 {
   310  			return fmt.Errorf("All the CPUs %v must belong to one specific package", cpus)
   311  		}
   312  
   313  		if clos < 0 {
   314  			return fmt.Errorf("Clos not set, use -clos option")
   315  		}
   316  
   317  		cpu2Clos := make(sst.ClosCPUSet, 1)
   318  		cpu2Clos[clos] = cpus
   319  
   320  		if err := sst.ConfigureCP(info, priority, &cpu2Clos); err != nil {
   321  			return err
   322  		}
   323  
   324  	} else if clos >= 0 {
   325  		pkgs = str2slice(packageIds)
   326  		if len(pkgs) == 0 {
   327  			return fmt.Errorf("No packages set, invalid value %q", packageIds)
   328  		}
   329  
   330  		closinfo := sst.SstClosInfo{
   331  			EPP:                  epp,
   332  			ProportionalPriority: proportionalPriority,
   333  			MinFreq:              minFreq,
   334  			MaxFreq:              maxFreq,
   335  			DesiredFreq:          desiredFreq,
   336  		}
   337  
   338  		infomap, err = sst.GetPackageInfo(pkgs...)
   339  		if err != nil {
   340  			return fmt.Errorf("Cannot get package info: %w", err)
   341  		}
   342  
   343  		for _, info = range infomap {
   344  			if err := sst.ClosSetup(info, clos, &closinfo); err != nil {
   345  				return fmt.Errorf("Cannot set Clos: %w", err)
   346  			}
   347  		}
   348  	} else {
   349  		if (!enable && !disable) && clos < 0 {
   350  			return fmt.Errorf("Clos not set, use -clos option")
   351  		}
   352  
   353  		// Print information if user just wants to enable / disable CP
   354  		infomap, _ = sst.GetPackageInfo(pkgs...)
   355  	}
   356  
   357  	if enable || disable {
   358  		for packageId, info = range infomap {
   359  			if enable {
   360  				fmt.Printf("Enabling CP for package %d\n", packageId)
   361  
   362  				err = sst.EnableCP(info)
   363  				if err != nil {
   364  					return err
   365  				}
   366  			} else if disable {
   367  				fmt.Printf("Disabling CP for package %d\n", packageId)
   368  
   369  				err = sst.DisableCP(info)
   370  				if err != nil {
   371  					return err
   372  				}
   373  			}
   374  		}
   375  	}
   376  
   377  	for packageId = range infomap {
   378  		// If we add a CPU to Clos, punit might add another CPU to same Clos.
   379  		// Make sure we have re-read the package info before printing it
   380  		_ = printPackageInfo(packageId)
   381  	}
   382  
   383  	return nil
   384  }