talos/internal/pkg/kernel/kernel.go
Spencer Smith 473df84cf6 fix: move to per-platform console setup
This PR will make sure that each platform gets the console settings it
needs by setting them as extra flags in the makefile. This should ensure
that we have console logs flowing properly for each cloud.

Signed-off-by: Spencer Smith <robertspencersmith@gmail.com>
2019-09-10 07:50:34 -07:00

251 lines
5.2 KiB
Go

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package kernel
import (
"io/ioutil"
"strings"
"sync"
)
// NewDefaultCmdline returns a set of kernel parameters that serve as the base
// for all Talos installations.
// nolint: golint
func NewDefaultCmdline() *Cmdline {
cmdline := NewCmdline("")
cmdline.AppendDefaults()
return cmdline
}
// Key represents a key in a kernel parameter key-value pair.
type Key = string
// Parameter represents a value in a kernel parameter key-value pair.
type Parameter struct {
key Key
values []string
}
// NewParameter initializes and returns a Parameter.
func NewParameter(k string) *Parameter {
return &Parameter{
key: k,
values: []string{},
}
}
// Append appends a string to a value's internal representation.
func (v *Parameter) Append(s string) *Parameter {
v.values = append(v.values, s)
return v
}
// First attempts to return the first string of a value's internal
// representation.
func (v *Parameter) First() *string {
switch {
case v == nil:
return nil
case v.values == nil:
return nil
case len(v.values) > 0:
return &v.values[0]
default:
return nil
}
}
// Get attempts to get a string from a value's internal representation.
func (v *Parameter) Get(idx int) *string {
if v == nil {
return nil
}
if len(v.values) > idx {
return &v.values[idx]
}
return nil
}
// Contains returns a boolean indicating the existence of a value.
func (v *Parameter) Contains(s string) (ok bool) {
if v == nil {
return ok
}
for _, value := range v.values {
if ok = s == value; ok {
return ok
}
}
return ok
}
// Key returns the value's key.
func (v *Parameter) Key() string {
return v.key
}
// Parameters represents /proc/cmdline.
type Parameters []*Parameter
// String returns a string representation of all parameters.
func (p Parameters) String() string {
s := ""
for _, v := range p {
for _, val := range v.values {
if val == "" {
s += v.key + " "
} else {
s += v.key + "=" + val + " "
}
}
}
return strings.TrimRight(s, " ")
}
// Cmdline represents a set of kernel parameters.
type Cmdline struct {
sync.Mutex
Parameters
}
var instance *Cmdline
var once sync.Once
// Cmdline returns a representation of /proc/cmdline.
// nolint: golint
func ProcCmdline() *Cmdline {
once.Do(func() {
var err error
if instance, err = read(); err != nil {
panic(err)
}
})
return instance
}
// NewCmdline initializes and returns a representation of the cmdline values
// specified by `parameters`.
// nolint: golint
func NewCmdline(parameters string) *Cmdline {
parsed := parse(parameters)
c := &Cmdline{sync.Mutex{}, parsed}
return c
}
// AppendDefaults add the Talos default kernel commandline options to the existing set
func (c *Cmdline) AppendDefaults() {
c.Append("page_poison", "1")
c.Append("slab_nomerge", "")
c.Append("pti", "on")
// TODO(andrewrynhard): Add slub_debug=P. See https://github.com/talos-systems/talos/pull/157.
c.Append("consoleblank", "0")
// AWS recommends setting the nvme_core.io_timeout to the highest value possible.
// See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html.
c.Append("nvme_core.io_timeout", "4294967295")
c.Append("random.trust_cpu", "on")
// Disable rate limited printk
c.Append("printk.devkmsg", "on")
}
// Get gets a kernel parameter.
func (c *Cmdline) Get(k string) (value *Parameter) {
c.Lock()
defer c.Unlock()
for _, value := range c.Parameters {
if value.key == k {
return value
}
}
return nil
}
// Set sets a kernel parameter.
func (c *Cmdline) Set(k string, v *Parameter) {
c.Lock()
defer c.Unlock()
for i, value := range c.Parameters {
if value.key == k {
c.Parameters = append(c.Parameters[:i], append([]*Parameter{v}, c.Parameters[i:]...)...)
return
}
}
}
// Append appends a kernel parameter.
func (c *Cmdline) Append(k string, v string) {
c.Lock()
defer c.Unlock()
for _, value := range c.Parameters {
if value.key == k {
value.Append(v)
return
}
}
insert(&c.Parameters, k, v)
}
// AppendAll appends a set of kernel parameters.
func (c *Cmdline) AppendAll(args []string) error {
parameters := parse(strings.Join(args, " "))
c.Parameters = append(c.Parameters, parameters...)
return nil
}
// Bytes returns the byte slice representation of the cmdline struct.
func (c *Cmdline) Bytes() []byte {
return []byte(c.String())
}
func insert(values *Parameters, key, value string) {
for _, v := range *values {
if v.key == key {
v.Append(value)
return
}
}
*values = append(*values, &Parameter{key: key, values: []string{value}})
}
func parse(parameters string) (parsed Parameters) {
line := strings.TrimSuffix(parameters, "\n")
fields := strings.Fields(line)
parsed = make(Parameters, 0)
for _, arg := range fields {
kv := strings.SplitN(arg, "=", 2)
switch len(kv) {
case 1:
insert(&parsed, kv[0], "")
case 2:
insert(&parsed, kv[0], kv[1])
}
}
return parsed
}
func read() (c *Cmdline, err error) {
var parameters []byte
parameters, err = ioutil.ReadFile("/proc/cmdline")
if err != nil {
return nil, err
}
parsed := parse(string(parameters))
c = &Cmdline{sync.Mutex{}, parsed}
return c, nil
}