Skip to content

Commit

Permalink
vm-builder: agetty init per architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
mikhail-sakhnov committed Oct 17, 2024
1 parent f871d05 commit 83795fd
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 6 deletions.
1 change: 0 additions & 1 deletion neonvm-controller/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import (
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
Expand Down
196 changes: 196 additions & 0 deletions pkg/neonvm/cpuscaling/sysfsscaling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package cpuscaling

import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)

// CPU directory path
const cpuPath = "/sys/devices/system/cpu/"

type CPUSysFsStateScaler struct {
}

func (c *CPUSysFsStateScaler) EnsureOnlineCPUs(X int) error {
cpus, err := getAllCPUs()
if err != nil {
return err
}

onlineCount, err := c.GetActiveCPUsCount()
if err != nil {
return err
}

if onlineCount < uint32(X) {
for _, cpu := range cpus {
if cpu == 0 {
// Skip CPU 0 as it is always online and can't be toggled
continue
}

online, err := isCPUOnline(cpu)
if err != nil {
return err
}

if !online {
// Mark CPU as online
err := setCPUOnline(cpu, true)
if err != nil {
return err
}
onlineCount++
}

// Stop when we reach the target count
if onlineCount == uint32(X) {
break
}
}
} else if onlineCount > uint32(X) {
// Remove CPUs if there are more than X online
for i := len(cpus) - 1; i >= 0; i-- {
cpu := cpus[i]
if cpu == 0 {
// Skip CPU 0 as it cannot be taken offline
continue
}

online, err := isCPUOnline(cpu)
if err != nil {
return err
}

if online {
// Mark CPU as offline
err := setCPUOnline(cpu, false)
if err != nil {
return err
}
onlineCount--
}

// Stop when we reach the target count
if onlineCount == uint32(X) {
break
}
}
}

if onlineCount != uint32(X) {
return fmt.Errorf("failed to ensure %d CPUs are online, current online CPUs: %d", X, onlineCount)
}

return nil
}

// GetActiveCPUsCount() returns the count of online CPUs.
func (c *CPUSysFsStateScaler) GetActiveCPUsCount() (uint32, error) {
cpus, err := getAllCPUs()
if err != nil {
return 0, err
}

var onlineCount uint32
for _, cpu := range cpus {
online, err := isCPUOnline(cpu)
if err != nil {
return 0, err
}
if online {
onlineCount++
}
}

return onlineCount, nil
}

// GetTotalCPUsCount returns the total number of CPUs (online + offline).
func (c *CPUSysFsStateScaler) GetTotalCPUsCount() (uint32, error) {
cpus, err := getAllCPUs()
if err != nil {
return 0, err
}

return uint32(len(cpus)), nil
}

// Helper functions

// getAllCPUs returns a list of all CPUs that are physically present in the system.
func getAllCPUs() ([]int, error) {
data, err := os.ReadFile(filepath.Join(cpuPath, "possible"))
if err != nil {
return nil, err
}

return parseCPURange(string(data))
}

// parseCPURange parses the CPU range string (e.g., "0-3") and returns a list of CPUs.
func parseCPURange(cpuRange string) ([]int, error) {
var cpus []int
cpuRange = strings.TrimSpace(cpuRange)
parts := strings.Split(cpuRange, "-")

if len(parts) == 1 {
// Single CPU case, e.g., "0"
cpu, err := strconv.Atoi(parts[0])
if err != nil {
return nil, err
}
cpus = append(cpus, cpu)
} else if len(parts) == 2 {
// Range case, e.g., "0-3"
start, err := strconv.Atoi(parts[0])
if err != nil {
return nil, err
}
end, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
for i := start; i <= end; i++ {
cpus = append(cpus, i)
}
}

return cpus, nil
}

// isCPUOnline checks if a given CPU is online.
func isCPUOnline(cpu int) (bool, error) {
data, err := os.ReadFile(filepath.Join(cpuPath, fmt.Sprintf("cpu%d/online", cpu)))
if os.IsNotExist(err) {
// If the file doesn't exist for CPU 0, assume it's online
if cpu == 0 {
return true, nil
}
return false, err
}
if err != nil {
return false, err
}

online := strings.TrimSpace(string(data))
return online == "1", nil
}

// setCPUOnline sets the given CPU as online (true) or offline (false).
func setCPUOnline(cpu int, online bool) error {
state := "0"
if online {
state = "1"
}

err := os.WriteFile(filepath.Join(cpuPath, fmt.Sprintf("cpu%d/online", cpu)), []byte(state), 0644)
if err != nil {
return fmt.Errorf("failed to set CPU %d online status: %w", cpu, err)
}

return nil
}
2 changes: 1 addition & 1 deletion vm-builder/files/Dockerfile.img
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ RUN set -e \
chmod +x /neonvm/bin/busybox \
&& /neonvm/bin/busybox --install -s /neonvm/bin

COPY helper.move-bins.sh /helper.move-bins.sh
COPY helper.move-bins.sh /helper.move-bins.sh

# add udevd and agetty (with shared libs)
RUN set -e \
Expand Down
1 change: 1 addition & 0 deletions vm-builder/files/agetty-init-amd64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ttyS0::respawn:/neonvm/bin/agetty --8bits --local-line --noissue --noclear --noreset --host console --login-program /neonvm/bin/login --login-pause --autologin root 115200 ttyS0 linux
1 change: 1 addition & 0 deletions vm-builder/files/agetty-init-arm64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ttyAMA0::respawn:/neonvm/bin/agetty --8bits --local-line --noissue --noclear --noreset --host console --login-program /neonvm/bin/login --login-pause --autologin root 115200 ttyAMA0 linux
2 changes: 1 addition & 1 deletion vm-builder/files/inittab
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
{{ range .InittabCommands }}
::{{.SysvInitAction}}:su -p {{.CommandUser}} -c {{.ShellEscapedCommand}}
{{ end }}
ttyAMA0::respawn:/neonvm/bin/agetty --8bits --local-line --noissue --noclear --noreset --host console --login-program /neonvm/bin/login --login-pause --autologin root 115200 ttyAMA0 linux
{{ .AgettyInitLine }}
::shutdown:/neonvm/bin/vmshutdown
42 changes: 39 additions & 3 deletions vm-builder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ var (
scriptVmStart string
//go:embed files/inittab
scriptInitTab string
//go:embed files/agetty-init-amd64
scriptAgettyInitAmd64 string
//go:embed files/agetty-init-arm64
scriptAgettyInitArm64 string
//go:embed files/vmacpi
scriptVmAcpi string
//go:embed files/vmshutdown
Expand All @@ -58,6 +62,11 @@ var (
configSshd string
)

const (
targetArchLinuxAmd64 = "linux/amd64"
targetArchLinuxArm64 = "linux/arm64"
)

var (
Version string
NeonvmDaemonImage string
Expand All @@ -72,7 +81,7 @@ var (
version = flag.Bool("version", false, `Print vm-builder version`)

daemonImageFlag = flag.String("daemon-image", "", `Specify the neonvm-daemon image: --daemon-image=neonvm-daemon:dev`)
targetArch = flag.String("target-arch", "linux/amd64", `Target architecture: --arch linux/amd64`)
targetArch = flag.String("target-arch", "", fmt.Sprintf("Target architecture: --arch %s | %s", targetArchLinuxAmd64, targetArchLinuxArm64))
)

func AddTemplatedFileToTar(tw *tar.Writer, tmplArgs any, filename string, tmplString string) error {
Expand Down Expand Up @@ -117,6 +126,7 @@ type TemplatesContext struct {
SpecBuild string
SpecMerge string
InittabCommands []inittabCommand
AgettyInitLine string
ShutdownHook string
}

Expand All @@ -133,13 +143,24 @@ func main() {
fmt.Println(Version)
os.Exit(0)
}

if len(*daemonImageFlag) == 0 && len(NeonvmDaemonImage) == 0 {
log.Println("neonvm-daemon image not set, needs to be explicitly passed in, or compiled with -ldflags '-X main.NeonvmDaemonImage=...'")
flag.PrintDefaults()
os.Exit(1)
}

if targetArch == nil || *targetArch == "" {
log.Println("Target architecture not set, see usage info:")
flag.PrintDefaults()
os.Exit(1)
}

if *targetArch != targetArchLinuxAmd64 && *targetArch != targetArchLinuxArm64 {
log.Fatalf("Unsupported target architecture: %q", *targetArch)
flag.PrintDefaults()
return
}

neonvmDaemonImage := NeonvmDaemonImage
if len(*daemonImageFlag) != 0 {
neonvmDaemonImage = *daemonImageFlag
Expand Down Expand Up @@ -290,7 +311,8 @@ func main() {
SpecBuild: "", // overridden below if spec != nil
SpecMerge: "", // overridden below if spec != nil
InittabCommands: nil, // overridden below if spec != nil
ShutdownHook: "", // overridden below if spec != nil
AgettyInitLine: getAgettyInitLine(*targetArch),
ShutdownHook: "", // overridden below if spec != nil
}

if len(imageSpec.Config.User) != 0 {
Expand Down Expand Up @@ -347,6 +369,8 @@ func main() {
{"vmstart", scriptVmStart},
{"vmshutdown", scriptVmShutdown},
{"inittab", scriptInitTab},
{"agetty-init-amd64", scriptAgettyInitAmd64},
{"agetty-init-arm64", scriptAgettyInitArm64},
{"vmacpi", scriptVmAcpi},
{"vminit", scriptVmInit},
{"vector.yaml", configVector},
Expand Down Expand Up @@ -543,3 +567,15 @@ func (f file) validate() []error {

return errs
}

func getAgettyInitLine(targetArch string) string {
switch targetArch {
case targetArchLinuxAmd64:
return scriptAgettyInitAmd64
case targetArchLinuxArm64:
return scriptAgettyInitArm64
default:
log.Fatalf("Unsupported target architecture: %q", targetArch)
return ""
}
}

0 comments on commit 83795fd

Please sign in to comment.