File: main.go

package info (click to toggle)
golang-github-intel-goresctrl 0.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bookworm-proposed-updates, forky, sid, trixie, trixie-proposed-updates
  • size: 2,064 kB
  • sloc: makefile: 19; sh: 15
file content (379 lines) | stat: -rw-r--r-- 9,730 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/*
Copyright 2021 Intel Corporation

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 main

import (
	"flag"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"

	"github.com/intel/goresctrl/pkg/sst"
	"github.com/intel/goresctrl/pkg/utils"
)

var (
	// Global command line flags
	packageIds string
)

type subCmd func([]string) error

var subCmds = map[string]subCmd{
	"info": subCmdInfo,
	"bf":   subCmdBF,
	"cp":   subCmdCP,
}

func main() {
	cmds := make([]string, 0, len(subCmds))
	for c := range subCmds {
		cmds = append(cmds, c)
	}
	sort.Strings(cmds)
	allCmds := strings.Join(cmds, ", ")

	if len(os.Args) < 2 {
		fmt.Printf("missing sub-command, must be one of: %s\n", allCmds)
		os.Exit(1)
	}

	// Run sub-command
	cmd, ok := subCmds[os.Args[1]]
	if !ok {
		fmt.Printf("unknown sub-command %q, must be of: %s\n", os.Args[1], allCmds)
		os.Exit(1)
	}

	if err := cmd(os.Args[2:]); err != nil {
		fmt.Printf("ERROR: sub-command %q failed: %v\n", os.Args[1], err)
		os.Exit(1)
	}
}

func addGlobalFlags(flagset *flag.FlagSet) {
	flagset.StringVar(&packageIds, "package", "", "One or more physical package id")
}

func printPackageInfo(pkgId ...int) error {
	info, err := sst.GetPackageInfo(pkgId...)
	if err != nil {
		return err
	}

	fmt.Println(utils.DumpJSON(info))

	return nil
}

// TODO: Move this functionality into utils.NewIdSetFromString()
func str2slice(str string) []int {
	var s []int

	for _, str := range strings.Split(str, ",") {
		if str == "" {
			continue
		}

		id, err := strconv.ParseUint(str, 10, 0)
		if err != nil {
			fmt.Printf("invalid value '%s': %v", str, err)
			continue
		}

		s = append(s, int(id))
	}

	return s
}

func subCmdInfo(args []string) error {
	// Parse command line args
	flags := flag.NewFlagSet("info", flag.ExitOnError)
	addGlobalFlags(flags)
	if err := flags.Parse(args); err != nil {
		return err
	}

	return printPackageInfo(str2slice(packageIds)...)
}

func enableBF(pkgId ...int) error {
	if len(pkgId) == 0 {
		fmt.Printf("Enabling BF for all packages\n")
	} else {
		fmt.Printf("Enabling BF for package(s) %v\n", pkgId)
	}

	err := sst.EnableBF(pkgId...)
	if err != nil {
		return err
	}

	return printPackageInfo(pkgId...)
}

func disableBF(pkgId ...int) error {
	if len(pkgId) == 0 {
		fmt.Printf("Disabling BF for all packages\n")
	} else {
		fmt.Printf("Disabling BF for package(s) %v\n", pkgId)
	}

	err := sst.DisableBF(pkgId...)
	if err != nil {
		return err
	}

	return printPackageInfo(pkgId...)
}

func subCmdBF(args []string) error {
	var enable, disable bool

	flags := flag.NewFlagSet("bf", flag.ExitOnError)
	flags.BoolVar(&enable, "enable", false, "enable feature")
	flags.BoolVar(&disable, "disable", false, "disable feature")
	addGlobalFlags(flags)

	if err := flags.Parse(args); err != nil {
		return err
	}

	if (!enable && !disable) || (enable && disable) {
		fmt.Printf("Please provide either -enable or -disable flag\n")
		return nil
	}

	var err error

	pkgs := str2slice(packageIds)

	if enable {
		err = enableBF(pkgs...)
	} else {
		err = disableBF(pkgs...)
	}

	return err
}

func getPackage(packageStr string, cpus utils.IDSet) (map[int]*sst.SstPackageInfo, *sst.SstPackageInfo, []int, error) {
	var infomap map[int]*sst.SstPackageInfo
	var info *sst.SstPackageInfo
	var packageId int
	var err error

	// If user has specified a package, then all the CPUs must belong to it.
	pkgs := str2slice(packageStr)
	if len(pkgs) > 1 {
		fmt.Printf("Only one package can be configured at a time (you have %d)\n", len(pkgs))
		return nil, nil, nil, fmt.Errorf("Provide one package value only")
	}

	if len(pkgs) == 0 {
		// User has not specified a package, figure it out from the
		// first CPU in the list.
		infomap, err = sst.GetPackageInfo()
		if err != nil {
			return nil, nil, nil, err
		}

		for packageId, info = range infomap {
			if sst.CheckPackageCpus(info, cpus) {
				pkgs = append(pkgs, packageId)
				break
			}
		}
	} else {
		// User has specified one package, make sure all the CPUs belong to it.
		infomap, err = sst.GetPackageInfo(pkgs...)
		if err != nil {
			return nil, nil, nil, err
		}

		for packageId, info = range infomap {
			if !sst.CheckPackageCpus(info, cpus) {
				fmt.Printf("All the CPUs %v must belong to one specific package\n", cpus)
				return nil, nil, nil, fmt.Errorf("Not all CPUs belong to package %d", packageId)
			}

			pkgs = append(pkgs, packageId)
			break
		}
	}

	return infomap, info, pkgs, nil
}

// TODO: Instead of all CP parameters groupped together, separate them like this.
//    sst-ctl cp enable
//    sst-ctl cp disable
//    sst-ctl cp configure -clos=CLOS...
//    sst-ctl cp assign...

func subCmdCP(args []string) error {
	var enable, disable, reset bool

	// Clos setup variables
	var epp, minFreq, maxFreq, desiredFreq, proportionalPriority, clos int

	// CPU to Clos mapping variables
	var cpuStr string
	var cpus utils.IDSet

	var packageId int
	var pkgs []int
	var info *sst.SstPackageInfo
	var infomap map[int]*sst.SstPackageInfo
	var err error
	var priority int

	defaultMaxFreq := 0xff

	flags := flag.NewFlagSet("cp", flag.ExitOnError)
	flags.BoolVar(&enable, "enable", false, "Enable feature")
	flags.BoolVar(&disable, "disable", false, "Disable feature")
	flags.BoolVar(&reset, "reset", false, "Reset CP to a known state")
	flags.IntVar(&clos, "clos", -1, "Class of service (0 - 3)")
	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.")
	flags.IntVar(&minFreq, "min", 0, "Clos minimum frequency MHz")
	flags.IntVar(&maxFreq, "max", defaultMaxFreq, "Clos maximum frequency MHz")
	flags.IntVar(&desiredFreq, "desired", 0, "Clos desired frequency MHz")
	flags.IntVar(&proportionalPriority, "proportional", 0, "Clos proportional priority weight. Used if CP priority mode is 0 (Proportional)")
	flags.IntVar(&priority, "priority", 1, "CP priority mode. 0 is Proportional, 1 is Ordered.")
	flags.StringVar(&cpuStr, "cpus", "", "List of CPUs assigned to the Clos.")

	addGlobalFlags(flags)

	flags.Usage = func() {
		flags.PrintDefaults()

		fmt.Fprintf(os.Stderr, "\nExample usage:\n\n")
		fmt.Fprintf(os.Stderr, "First reset CP to default:\n\t%s cp -reset\n\n", os.Args[0])
		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])
		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])
		fmt.Fprintf(os.Stderr, "Finally enable CP:\n\t%s cp -enable -package 0\n\n", os.Args[0])
	}

	if err := flags.Parse(args); err != nil {
		return err
	}

	if reset {
		err := sst.ResetCPConfig()
		_ = printPackageInfo()
		return err
	}

	if enable && disable {
		return fmt.Errorf("Please provide either -enable or -disable flag")
	}

	// If user specifies a list of CPUs, then he/she wants to assign those
	// CPUs to a specific CLOS. If the -cpus option is not set, then user
	// wants to configure the actual CLOS values. Both operations cannot be
	// done at the same time.

	// If user specifies a list of CPUs, then the package option is ignored.
	// Verify that all the CPUs belong to one specific package.
	if cpuStr != "" {
		cpus = utils.NewIDSet(str2slice(cpuStr)...)

		infomap, info, pkgs, err = getPackage(packageIds, cpus)
		if err != nil {
			return fmt.Errorf("Cannot get CPUs %v package: %w", cpus, err)
		}

		if len(pkgs) == 0 {
			return fmt.Errorf("All the CPUs %v must belong to one specific package", cpus)
		}

		if clos < 0 {
			return fmt.Errorf("Clos not set, use -clos option")
		}

		cpu2Clos := make(sst.ClosCPUSet, 1)
		cpu2Clos[clos] = cpus

		if err := sst.ConfigureCP(info, priority, &cpu2Clos); err != nil {
			return err
		}

	} else if clos >= 0 {
		pkgs = str2slice(packageIds)
		if len(pkgs) == 0 {
			return fmt.Errorf("No packages set, invalid value %q", packageIds)
		}

		closinfo := sst.SstClosInfo{
			EPP:                  epp,
			ProportionalPriority: proportionalPriority,
			MinFreq:              minFreq,
			MaxFreq:              maxFreq,
			DesiredFreq:          desiredFreq,
		}

		infomap, err = sst.GetPackageInfo(pkgs...)
		if err != nil {
			return fmt.Errorf("Cannot get package info: %w", err)
		}

		for _, info = range infomap {
			if err := sst.ClosSetup(info, clos, &closinfo); err != nil {
				return fmt.Errorf("Cannot set Clos: %w", err)
			}
		}
	} else {
		if (!enable && !disable) && clos < 0 {
			return fmt.Errorf("Clos not set, use -clos option")
		}

		// Print information if user just wants to enable / disable CP
		infomap, _ = sst.GetPackageInfo(pkgs...)
	}

	if enable || disable {
		for packageId, info = range infomap {
			if enable {
				fmt.Printf("Enabling CP for package %d\n", packageId)

				err = sst.EnableCP(info)
				if err != nil {
					return err
				}
			} else if disable {
				fmt.Printf("Disabling CP for package %d\n", packageId)

				err = sst.DisableCP(info)
				if err != nil {
					return err
				}
			}
		}
	}

	for packageId = range infomap {
		// If we add a CPU to Clos, punit might add another CPU to same Clos.
		// Make sure we have re-read the package info before printing it
		_ = printPackageInfo(packageId)
	}

	return nil
}