-
Notifications
You must be signed in to change notification settings - Fork 13
Add basic kernel tuning functionality #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package cmd | ||
|
|
||
| import ( | ||
| "bufio" | ||
| "encoding/json" | ||
| "flag" | ||
| "fmt" | ||
|
|
@@ -31,9 +32,18 @@ const ( | |
| etcPivotFile = "/etc/pivot/image-pullspec" | ||
| runPivotRebootFile = "/run/pivot/reboot-needed" | ||
| // Pull secret. Written by the machine-config-operator | ||
| kubeletAuthFile = "/var/lib/kubelet/config.json" | ||
| kubeletAuthFile = "/var/lib/kubelet/config.json" | ||
| // File containing kernel arg changes for tuning | ||
| kernelTuningFile = "/etc/pivot/kernel-args" | ||
| cmdLineFile = "/proc/cmdline" | ||
| ) | ||
|
|
||
| // TODO: fill out the whitelist | ||
| // tuneableArgsWhitelist contains allowed keys for tunable arguments | ||
| var tuneableArgsWhitelist = map[string]bool{ | ||
| "nosmt": true, | ||
| } | ||
|
|
||
| // RootCmd houses the cobra config for the main command | ||
| var RootCmd = &cobra.Command{ | ||
| Use: "pivot [FLAGS] [IMAGE_PULLSPEC]", | ||
|
|
@@ -51,6 +61,133 @@ func init() { | |
| pflag.CommandLine.AddGoFlagSet(flag.CommandLine) | ||
| } | ||
|
|
||
| // isArgTuneable returns if the argument provided is allowed to be modified | ||
| func isArgTunable(arg string) bool { | ||
| return tuneableArgsWhitelist[arg] | ||
| } | ||
|
|
||
| // isArgInUse checks to see if the argument is already in use by the system currently | ||
| func isArgInUse(arg, cmdLinePath string) (bool, error) { | ||
| if cmdLinePath == "" { | ||
| cmdLinePath = cmdLineFile | ||
| } | ||
| content, err := ioutil.ReadFile(cmdLinePath) | ||
| if err != nil { | ||
| return false, err | ||
| } | ||
|
|
||
| checkable := string(content) | ||
| if strings.Contains(checkable, arg) { | ||
| return true, nil | ||
| } | ||
| return false, nil | ||
| } | ||
|
|
||
| // parseTuningFile parses the kernel argument tuning file | ||
| func parseTuningFile(tuningFilePath, cmdLinePath string) ([]types.TuneArgument, []types.TuneArgument, error) { | ||
| addArguments := []types.TuneArgument{} | ||
| deleteArguments := []types.TuneArgument{} | ||
| if tuningFilePath == "" { | ||
| tuningFilePath = kernelTuningFile | ||
| } | ||
| if cmdLinePath == "" { | ||
| cmdLinePath = cmdLineFile | ||
| } | ||
| // Return fast if the file does not exist | ||
| if _, err := os.Stat(tuningFilePath); os.IsNotExist(err) { | ||
| glog.V(2).Infof("no kernel tuning needed as %s does not exist", tuningFilePath) | ||
| // This isn't an error. Return out. | ||
| return addArguments, deleteArguments, err | ||
| } | ||
| // Read and parse the file | ||
| file, err := os.Open(tuningFilePath) | ||
| // Clean up | ||
| defer file.Close() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if open fails for any reason (like a not found of the tuningFilePath), this will panic, this statement should go after the error check below
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| if err != nil { | ||
| // If we have an issue reading return an error | ||
| glog.Infof("Unable to open %s for reading: %v", tuningFilePath, err) | ||
| return addArguments, deleteArguments, err | ||
| } | ||
|
|
||
| // Parse the tuning lines | ||
| scanner := bufio.NewScanner(file) | ||
| for scanner.Scan() { | ||
| line := scanner.Text() | ||
| if strings.HasPrefix(line, "ADD ") { | ||
| // NOTE: Today only specific bare kernel arguments are allowed so | ||
| // there is not a need to split on =. | ||
| key := strings.TrimSpace(line[len("ADD "):]) | ||
| if isArgTunable(key) { | ||
| // Find out if the argument is in use | ||
| inUse, err := isArgInUse(key, cmdLinePath) | ||
| if err != nil { | ||
| return addArguments, deleteArguments, err | ||
| } | ||
| if !inUse { | ||
| addArguments = append(addArguments, types.TuneArgument{Key: key, Bare: true}) | ||
| } else { | ||
| glog.Infof(`skipping "%s" as it is already in use`, key) | ||
| } | ||
| } else { | ||
| glog.Infof("%s not a whitelisted kernel argument", key) | ||
ashcrow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } else if strings.HasPrefix(line, "DELETE ") { | ||
| // NOTE: Today only specific bare kernel arguments are allowed so | ||
| // there is not a need to split on =. | ||
| key := strings.TrimSpace(line[len("DELETE "):]) | ||
| if isArgTunable(key) { | ||
| inUse, err := isArgInUse(key, cmdLinePath) | ||
| if err != nil { | ||
| return addArguments, deleteArguments, err | ||
| } | ||
| if inUse { | ||
| deleteArguments = append(deleteArguments, types.TuneArgument{Key: key, Bare: true}) | ||
| } else { | ||
| glog.Infof(`skipping "%s" as it is not present in the current argument list`, key) | ||
| } | ||
| } else { | ||
| glog.Infof("%s not a whitelisted kernel argument", key) | ||
| } | ||
| } else { | ||
| glog.V(2).Infof(`skipping malformed line in %s: "%s"`, tuningFilePath, line) | ||
| } | ||
| } | ||
| return addArguments, deleteArguments, nil | ||
| } | ||
|
|
||
| // updateTuningArgs executes additions and removals of kernel tuning arguments | ||
| func updateTuningArgs(tuningFilePath, cmdLinePath string) (bool, error) { | ||
| if cmdLinePath == "" { | ||
| cmdLinePath = cmdLineFile | ||
| } | ||
| changed := false | ||
| additions, deletions, err := parseTuningFile(tuningFilePath, cmdLinePath) | ||
| if err != nil { | ||
| return changed, err | ||
| } | ||
|
|
||
| // Execute additions | ||
| for _, toAdd := range additions { | ||
| if toAdd.Bare { | ||
| changed = true | ||
| utils.Run("rpm-ostree", "kargs", fmt.Sprintf("--append=%s", toAdd.Key)) | ||
ashcrow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| // TODO: currently not supported | ||
| } | ||
| } | ||
| // Execute deletions | ||
| for _, toDelete := range deletions { | ||
| if toDelete.Bare { | ||
| changed = true | ||
| utils.Run("rpm-ostree", "kargs", fmt.Sprintf("--delete=%s", toDelete.Key)) | ||
| } else { | ||
| // TODO: currently not supported | ||
| } | ||
| } | ||
| return changed, nil | ||
| } | ||
|
|
||
| // podmanRemove kills and removes a container | ||
| func podmanRemove(cid string) { | ||
| utils.RunIgnoreErr("podman", "kill", cid) | ||
|
|
@@ -241,6 +378,21 @@ func Execute(cmd *cobra.Command, args []string) { | |
| utils.RunIgnoreErr("podman", "rmi", imgid) | ||
| } | ||
|
|
||
| // Check to see if we need to tune kernel arguments | ||
| tuningChanged, err := updateTuningArgs(kernelTuningFile, cmdLineFile) | ||
| if err != nil { | ||
| glog.Infof("unable to parse tuning file %s: %s", kernelTuningFile, err) | ||
| } | ||
| // If tuning changes but the oscontainer didn't we still denote we changed | ||
| // for the reboot | ||
| if tuningChanged { | ||
| changed = true | ||
| if err != nil { | ||
| glog.Infof(`Unable to remove kernel tuning file %s: "%s"`, kernelTuningFile, err) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| if !changed { | ||
| glog.Info("Already at target pivot; exiting...") | ||
| if exit_77 { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package types | ||
|
|
||
| // TuneArgument represents a single tuning argument | ||
| type TuneArgument struct { | ||
| Key string `json:"key"` // The name of the argument (or argument itself if Bare) | ||
| Value string `json:"value"` // The value of the argument | ||
| Bare bool `json:"bare"` // If the kernel argument is a bare argument (no value expected) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think right now this is OK, but in the future we're clearly going to need to actually parse the cmdline properly because this will e.g. succeed if some other user-editable string in the cmdline contains
nosmtlikerd.lvm.lv=janosmtbecause their name is "Janos Mountain" or something.