Skip to content

Commit 83795fd

Browse files
vm-builder: agetty init per architecture
1 parent f871d05 commit 83795fd

File tree

7 files changed

+239
-6
lines changed

7 files changed

+239
-6
lines changed

neonvm-controller/cmd/main.go

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ import (
4242
"k8s.io/apimachinery/pkg/types"
4343
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
4444
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
45-
4645
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
4746
// to ensure that exec-entrypoint and run can make use of them.
4847
_ "k8s.io/client-go/plugin/pkg/client/auth"

pkg/neonvm/cpuscaling/sysfsscaling.go

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package cpuscaling
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
// CPU directory path
12+
const cpuPath = "/sys/devices/system/cpu/"
13+
14+
type CPUSysFsStateScaler struct {
15+
}
16+
17+
func (c *CPUSysFsStateScaler) EnsureOnlineCPUs(X int) error {
18+
cpus, err := getAllCPUs()
19+
if err != nil {
20+
return err
21+
}
22+
23+
onlineCount, err := c.GetActiveCPUsCount()
24+
if err != nil {
25+
return err
26+
}
27+
28+
if onlineCount < uint32(X) {
29+
for _, cpu := range cpus {
30+
if cpu == 0 {
31+
// Skip CPU 0 as it is always online and can't be toggled
32+
continue
33+
}
34+
35+
online, err := isCPUOnline(cpu)
36+
if err != nil {
37+
return err
38+
}
39+
40+
if !online {
41+
// Mark CPU as online
42+
err := setCPUOnline(cpu, true)
43+
if err != nil {
44+
return err
45+
}
46+
onlineCount++
47+
}
48+
49+
// Stop when we reach the target count
50+
if onlineCount == uint32(X) {
51+
break
52+
}
53+
}
54+
} else if onlineCount > uint32(X) {
55+
// Remove CPUs if there are more than X online
56+
for i := len(cpus) - 1; i >= 0; i-- {
57+
cpu := cpus[i]
58+
if cpu == 0 {
59+
// Skip CPU 0 as it cannot be taken offline
60+
continue
61+
}
62+
63+
online, err := isCPUOnline(cpu)
64+
if err != nil {
65+
return err
66+
}
67+
68+
if online {
69+
// Mark CPU as offline
70+
err := setCPUOnline(cpu, false)
71+
if err != nil {
72+
return err
73+
}
74+
onlineCount--
75+
}
76+
77+
// Stop when we reach the target count
78+
if onlineCount == uint32(X) {
79+
break
80+
}
81+
}
82+
}
83+
84+
if onlineCount != uint32(X) {
85+
return fmt.Errorf("failed to ensure %d CPUs are online, current online CPUs: %d", X, onlineCount)
86+
}
87+
88+
return nil
89+
}
90+
91+
// GetActiveCPUsCount() returns the count of online CPUs.
92+
func (c *CPUSysFsStateScaler) GetActiveCPUsCount() (uint32, error) {
93+
cpus, err := getAllCPUs()
94+
if err != nil {
95+
return 0, err
96+
}
97+
98+
var onlineCount uint32
99+
for _, cpu := range cpus {
100+
online, err := isCPUOnline(cpu)
101+
if err != nil {
102+
return 0, err
103+
}
104+
if online {
105+
onlineCount++
106+
}
107+
}
108+
109+
return onlineCount, nil
110+
}
111+
112+
// GetTotalCPUsCount returns the total number of CPUs (online + offline).
113+
func (c *CPUSysFsStateScaler) GetTotalCPUsCount() (uint32, error) {
114+
cpus, err := getAllCPUs()
115+
if err != nil {
116+
return 0, err
117+
}
118+
119+
return uint32(len(cpus)), nil
120+
}
121+
122+
// Helper functions
123+
124+
// getAllCPUs returns a list of all CPUs that are physically present in the system.
125+
func getAllCPUs() ([]int, error) {
126+
data, err := os.ReadFile(filepath.Join(cpuPath, "possible"))
127+
if err != nil {
128+
return nil, err
129+
}
130+
131+
return parseCPURange(string(data))
132+
}
133+
134+
// parseCPURange parses the CPU range string (e.g., "0-3") and returns a list of CPUs.
135+
func parseCPURange(cpuRange string) ([]int, error) {
136+
var cpus []int
137+
cpuRange = strings.TrimSpace(cpuRange)
138+
parts := strings.Split(cpuRange, "-")
139+
140+
if len(parts) == 1 {
141+
// Single CPU case, e.g., "0"
142+
cpu, err := strconv.Atoi(parts[0])
143+
if err != nil {
144+
return nil, err
145+
}
146+
cpus = append(cpus, cpu)
147+
} else if len(parts) == 2 {
148+
// Range case, e.g., "0-3"
149+
start, err := strconv.Atoi(parts[0])
150+
if err != nil {
151+
return nil, err
152+
}
153+
end, err := strconv.Atoi(parts[1])
154+
if err != nil {
155+
return nil, err
156+
}
157+
for i := start; i <= end; i++ {
158+
cpus = append(cpus, i)
159+
}
160+
}
161+
162+
return cpus, nil
163+
}
164+
165+
// isCPUOnline checks if a given CPU is online.
166+
func isCPUOnline(cpu int) (bool, error) {
167+
data, err := os.ReadFile(filepath.Join(cpuPath, fmt.Sprintf("cpu%d/online", cpu)))
168+
if os.IsNotExist(err) {
169+
// If the file doesn't exist for CPU 0, assume it's online
170+
if cpu == 0 {
171+
return true, nil
172+
}
173+
return false, err
174+
}
175+
if err != nil {
176+
return false, err
177+
}
178+
179+
online := strings.TrimSpace(string(data))
180+
return online == "1", nil
181+
}
182+
183+
// setCPUOnline sets the given CPU as online (true) or offline (false).
184+
func setCPUOnline(cpu int, online bool) error {
185+
state := "0"
186+
if online {
187+
state = "1"
188+
}
189+
190+
err := os.WriteFile(filepath.Join(cpuPath, fmt.Sprintf("cpu%d/online", cpu)), []byte(state), 0644)
191+
if err != nil {
192+
return fmt.Errorf("failed to set CPU %d online status: %w", cpu, err)
193+
}
194+
195+
return nil
196+
}

vm-builder/files/Dockerfile.img

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ RUN set -e \
2121
chmod +x /neonvm/bin/busybox \
2222
&& /neonvm/bin/busybox --install -s /neonvm/bin
2323

24-
COPY helper.move-bins.sh /helper.move-bins.sh
24+
COPY helper.move-bins.sh /helper.move-bins.sh
2525

2626
# add udevd and agetty (with shared libs)
2727
RUN set -e \

vm-builder/files/agetty-init-amd64

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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

vm-builder/files/agetty-init-arm64

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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

vm-builder/files/inittab

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
{{ range .InittabCommands }}
1313
::{{.SysvInitAction}}:su -p {{.CommandUser}} -c {{.ShellEscapedCommand}}
1414
{{ end }}
15-
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
15+
{{ .AgettyInitLine }}
1616
::shutdown:/neonvm/bin/vmshutdown

vm-builder/main.go

+39-3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ var (
3838
scriptVmStart string
3939
//go:embed files/inittab
4040
scriptInitTab string
41+
//go:embed files/agetty-init-amd64
42+
scriptAgettyInitAmd64 string
43+
//go:embed files/agetty-init-arm64
44+
scriptAgettyInitArm64 string
4145
//go:embed files/vmacpi
4246
scriptVmAcpi string
4347
//go:embed files/vmshutdown
@@ -58,6 +62,11 @@ var (
5862
configSshd string
5963
)
6064

65+
const (
66+
targetArchLinuxAmd64 = "linux/amd64"
67+
targetArchLinuxArm64 = "linux/arm64"
68+
)
69+
6170
var (
6271
Version string
6372
NeonvmDaemonImage string
@@ -72,7 +81,7 @@ var (
7281
version = flag.Bool("version", false, `Print vm-builder version`)
7382

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

7887
func AddTemplatedFileToTar(tw *tar.Writer, tmplArgs any, filename string, tmplString string) error {
@@ -117,6 +126,7 @@ type TemplatesContext struct {
117126
SpecBuild string
118127
SpecMerge string
119128
InittabCommands []inittabCommand
129+
AgettyInitLine string
120130
ShutdownHook string
121131
}
122132

@@ -133,13 +143,24 @@ func main() {
133143
fmt.Println(Version)
134144
os.Exit(0)
135145
}
136-
137146
if len(*daemonImageFlag) == 0 && len(NeonvmDaemonImage) == 0 {
138147
log.Println("neonvm-daemon image not set, needs to be explicitly passed in, or compiled with -ldflags '-X main.NeonvmDaemonImage=...'")
139148
flag.PrintDefaults()
140149
os.Exit(1)
141150
}
142151

152+
if targetArch == nil || *targetArch == "" {
153+
log.Println("Target architecture not set, see usage info:")
154+
flag.PrintDefaults()
155+
os.Exit(1)
156+
}
157+
158+
if *targetArch != targetArchLinuxAmd64 && *targetArch != targetArchLinuxArm64 {
159+
log.Fatalf("Unsupported target architecture: %q", *targetArch)
160+
flag.PrintDefaults()
161+
return
162+
}
163+
143164
neonvmDaemonImage := NeonvmDaemonImage
144165
if len(*daemonImageFlag) != 0 {
145166
neonvmDaemonImage = *daemonImageFlag
@@ -290,7 +311,8 @@ func main() {
290311
SpecBuild: "", // overridden below if spec != nil
291312
SpecMerge: "", // overridden below if spec != nil
292313
InittabCommands: nil, // overridden below if spec != nil
293-
ShutdownHook: "", // overridden below if spec != nil
314+
AgettyInitLine: getAgettyInitLine(*targetArch),
315+
ShutdownHook: "", // overridden below if spec != nil
294316
}
295317

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

544568
return errs
545569
}
570+
571+
func getAgettyInitLine(targetArch string) string {
572+
switch targetArch {
573+
case targetArchLinuxAmd64:
574+
return scriptAgettyInitAmd64
575+
case targetArchLinuxArm64:
576+
return scriptAgettyInitArm64
577+
default:
578+
log.Fatalf("Unsupported target architecture: %q", targetArch)
579+
return ""
580+
}
581+
}

0 commit comments

Comments
 (0)