Skip to content

Commit 5711284

Browse files
vinayakjeeturunc-bot[bot]
authored andcommitted
feat: add configurable vhost option for monitors
PR: #415 Signed-off-by: vinayakjeet <vinayakjeetog@gmail.com> Reviewed-by: Charalampos Mainas <cmainas@nubificus.co.uk> Approved-by: Charalampos Mainas <cmainas@nubificus.co.uk>
1 parent 9359db2 commit 5711284

File tree

7 files changed

+159
-11
lines changed

7 files changed

+159
-11
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ test: unittest e2etest
231231

232232
## unittest Run all unit tests
233233
.PHONY: unittest
234-
unittest: test_unikontainers test_metrics test_network
234+
unittest: test_unikontainers test_metrics test_network test_hypervisors
235235

236236
## e2etest Run all end-to-end tests
237237
.PHONY: e2etest
@@ -255,6 +255,12 @@ test_network:
255255
@GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./pkg/network -v
256256
@echo " "
257257

258+
## test_hypervisors Run unit tests for hypervisors package
259+
test_hypervisors:
260+
@echo "Unit testing in hypervisors"
261+
@GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./pkg/unikontainers/hypervisors -v
262+
@echo " "
263+
258264
## test_nerdctl Run all end-to-end tests with nerdctl
259265
.PHONY: test_nerdctl
260266
test_nerdctl:

pkg/unikontainers/hypervisors/qemu.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
type Qemu struct {
3131
binaryPath string
3232
binary string
33+
vhost bool
3334
}
3435

3536
func (q *Qemu) Stop(pid int) error {
@@ -94,6 +95,9 @@ func (q *Qemu) BuildExecCmd(args types.ExecArgs, ukernel types.Unikernel) ([]str
9495
netcli += args.Net.MAC
9596
netcli += " -net tap,script=no,downscript=no,ifname="
9697
netcli += args.Net.TapDev
98+
if q.vhost {
99+
netcli += ",vhost=on"
100+
}
97101
}
98102
cmdString += netcli
99103
} else {

pkg/unikontainers/hypervisors/vmm.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,25 +32,33 @@ var vmmLog = logrus.WithField("subsystem", "monitors")
3232

3333
type VMMFactory struct {
3434
binary string
35-
createFunc func(binary, binaryPath string) types.VMM
35+
createFunc func(binary, binaryPath string, vhost bool) types.VMM
3636
}
3737

3838
var vmmFactories = map[VmmType]VMMFactory{
3939
SptVmm: {
40-
binary: SptBinary,
41-
createFunc: func(binary, binaryPath string) types.VMM { return &SPT{binary: binary, binaryPath: binaryPath} },
40+
binary: SptBinary,
41+
createFunc: func(binary, binaryPath string, _ bool) types.VMM {
42+
return &SPT{binary: binary, binaryPath: binaryPath}
43+
},
4244
},
4345
HvtVmm: {
44-
binary: HvtBinary,
45-
createFunc: func(binary, binaryPath string) types.VMM { return &HVT{binary: binary, binaryPath: binaryPath} },
46+
binary: HvtBinary,
47+
createFunc: func(binary, binaryPath string, _ bool) types.VMM {
48+
return &HVT{binary: binary, binaryPath: binaryPath}
49+
},
4650
},
4751
QemuVmm: {
48-
binary: QemuBinary,
49-
createFunc: func(binary, binaryPath string) types.VMM { return &Qemu{binary: binary, binaryPath: binaryPath} },
52+
binary: QemuBinary,
53+
createFunc: func(binary, binaryPath string, vhost bool) types.VMM {
54+
return &Qemu{binary: binary, binaryPath: binaryPath, vhost: vhost}
55+
},
5056
},
5157
FirecrackerVmm: {
52-
binary: FirecrackerBinary,
53-
createFunc: func(binary, binaryPath string) types.VMM { return &Firecracker{binary: binary, binaryPath: binaryPath} },
58+
binary: FirecrackerBinary,
59+
createFunc: func(binary, binaryPath string, _ bool) types.VMM {
60+
return &Firecracker{binary: binary, binaryPath: binaryPath}
61+
},
5462
},
5563
}
5664

@@ -80,7 +88,7 @@ func NewVMM(vmmType VmmType, monitors map[string]types.MonitorConfig) (vmm types
8088
return nil, err
8189
}
8290

83-
return factory.createFunc(factory.binary, vmmPath), nil
91+
return factory.createFunc(factory.binary, vmmPath, monitors[string(vmmType)].Vhost), nil
8492
}
8593

8694
func getVMMPath(vmmType VmmType, binary string, monitors map[string]types.MonitorConfig) (string, error) {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (c) 2023-2026, Nubificus LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package hypervisors
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
)
22+
23+
func TestVMMFactoryQemuVhostFalse(t *testing.T) {
24+
t.Parallel()
25+
factory, exists := vmmFactories[QemuVmm]
26+
assert.True(t, exists, "qemu factory should exist")
27+
28+
vmm := factory.createFunc(QemuBinary, "/usr/bin/qemu-system-x86_64", false)
29+
qemu, ok := vmm.(*Qemu)
30+
assert.True(t, ok, "factory should return *Qemu")
31+
assert.False(t, qemu.vhost, "vhost should be false when passed as false")
32+
}
33+
34+
func TestVMMFactoryQemuVhostTrue(t *testing.T) {
35+
t.Parallel()
36+
factory, exists := vmmFactories[QemuVmm]
37+
assert.True(t, exists, "qemu factory should exist")
38+
39+
vmm := factory.createFunc(QemuBinary, "/usr/bin/qemu-system-x86_64", true)
40+
qemu, ok := vmm.(*Qemu)
41+
assert.True(t, ok, "factory should return *Qemu")
42+
assert.True(t, qemu.vhost, "vhost should be true when passed as true")
43+
}
44+
45+
func TestVMMFactoryNonQemuIgnoresVhost(t *testing.T) {
46+
t.Parallel()
47+
48+
// SPT factory should ignore vhost parameter
49+
factory, exists := vmmFactories[SptVmm]
50+
assert.True(t, exists, "spt factory should exist")
51+
52+
vmm := factory.createFunc(SptBinary, "/usr/bin/solo5-spt", true)
53+
_, ok := vmm.(*SPT)
54+
assert.True(t, ok, "factory should return *SPT")
55+
56+
// HVT factory should ignore vhost parameter
57+
factory, exists = vmmFactories[HvtVmm]
58+
assert.True(t, exists, "hvt factory should exist")
59+
60+
vmm = factory.createFunc(HvtBinary, "/usr/bin/solo5-hvt", true)
61+
_, ok = vmm.(*HVT)
62+
assert.True(t, ok, "factory should return *HVT")
63+
64+
// Firecracker factory should ignore vhost parameter
65+
factory, exists = vmmFactories[FirecrackerVmm]
66+
assert.True(t, exists, "firecracker factory should exist")
67+
68+
vmm = factory.createFunc(FirecrackerBinary, "/usr/bin/firecracker", true)
69+
_, ok = vmm.(*Firecracker)
70+
assert.True(t, ok, "factory should return *Firecracker")
71+
}

pkg/unikontainers/types/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,5 @@ type MonitorConfig struct {
131131
DefaultVCPUs uint `toml:"default_vcpus"`
132132
BinaryPath string `toml:"path,omitempty"` // Optional path to the hypervisor binary
133133
DataPath string `toml:"data_path,omitempty"` // Optional path to the hypervisor data files (e.g. qemu bios stuff)
134+
Vhost bool `toml:"vhost,omitempty"` // Optional: enable vhost for network performance optimization
134135
}

pkg/unikontainers/urunc_config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func (p *UruncConfig) Map() map[string]string {
125125
cfgMap[prefix+"default_vcpus"] = strconv.FormatUint(uint64(hvCfg.DefaultVCPUs), 10)
126126
cfgMap[prefix+"binary_path"] = hvCfg.BinaryPath
127127
cfgMap[prefix+"data_path"] = hvCfg.DataPath
128+
cfgMap[prefix+"vhost"] = strconv.FormatBool(hvCfg.Vhost)
128129
}
129130
for eb, ebCfg := range p.ExtraBins {
130131
prefix := "urunc_config.extra_binaries." + eb + "."
@@ -171,6 +172,13 @@ func UruncConfigFromMap(cfgMap map[string]string) *UruncConfig {
171172
hvCfg.BinaryPath = val
172173
case "data_path":
173174
hvCfg.DataPath = val
175+
case "vhost":
176+
boolVal, err := strconv.ParseBool(val)
177+
if err != nil {
178+
uniklog.Warnf("Invalid vhost value '%s' for monitor '%s': %v. Using default (false).", val, hv, err)
179+
} else {
180+
hvCfg.Vhost = boolVal
181+
}
174182
}
175183
cfg.Monitors[hv] = hvCfg
176184
}

pkg/unikontainers/urunc_config_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
testQemuVCPUsKey = "urunc_config.monitors.qemu.default_vcpus"
2828
testQemuBinaryKey = "urunc_config.monitors.qemu.binary_path"
2929
testQemuDataKey = "urunc_config.monitors.qemu.data_path"
30+
testQemuVhostKey = "urunc_config.monitors.qemu.vhost"
3031
testHvtMemoryKey = "urunc_config.monitors.hvt.default_memory_mb"
3132
testVirtiofsdPathKey = "urunc_config.extra_binaries.virtiofsd.path"
3233
testVirtiofsdOptsKey = "urunc_config.extra_binaries.virtiofsd.options"
@@ -56,6 +57,7 @@ func TestUruncConfigFromMap(t *testing.T) {
5657
testQemuVCPUsKey: "2",
5758
testQemuBinaryKey: testQemuBinaryPath,
5859
testQemuDataKey: testQemuDataPath,
60+
testQemuVhostKey: "true",
5961
}
6062

6163
config := UruncConfigFromMap(cfgMap)
@@ -67,6 +69,7 @@ func TestUruncConfigFromMap(t *testing.T) {
6769
assert.Equal(t, uint(2), qemuConfig.DefaultVCPUs)
6870
assert.Equal(t, testQemuBinaryPath, qemuConfig.BinaryPath)
6971
assert.Equal(t, testQemuDataPath, qemuConfig.DataPath)
72+
assert.True(t, qemuConfig.Vhost)
7073
})
7174

7275
t.Run("multiple monitors", func(t *testing.T) {
@@ -323,6 +326,34 @@ func TestUruncConfigFromMap(t *testing.T) {
323326
assert.Equal(t, testVirtiofsdDefOpts, vfsConfig.Options)
324327
})
325328

329+
t.Run("vhost false is parsed correctly", func(t *testing.T) {
330+
t.Parallel()
331+
cfgMap := map[string]string{
332+
testQemuMemoryKey: "512",
333+
testQemuVhostKey: "false",
334+
}
335+
336+
config := UruncConfigFromMap(cfgMap)
337+
338+
assert.NotNil(t, config)
339+
qemuConfig := config.Monitors["qemu"]
340+
assert.False(t, qemuConfig.Vhost)
341+
})
342+
343+
t.Run("invalid vhost value defaults to false", func(t *testing.T) {
344+
t.Parallel()
345+
cfgMap := map[string]string{
346+
testQemuMemoryKey: "512",
347+
testQemuVhostKey: "invalid",
348+
}
349+
350+
config := UruncConfigFromMap(cfgMap)
351+
352+
assert.NotNil(t, config)
353+
qemuConfig := config.Monitors["qemu"]
354+
assert.False(t, qemuConfig.Vhost, "invalid vhost value should default to false")
355+
})
356+
326357
}
327358

328359
func TestUruncConfigMap(t *testing.T) {
@@ -415,6 +446,24 @@ func TestUruncConfigMap(t *testing.T) {
415446
assert.NotNil(t, cfgMap)
416447
assert.Empty(t, cfgMap)
417448
})
449+
450+
t.Run("vhost true is serialized correctly", func(t *testing.T) {
451+
t.Parallel()
452+
config := &UruncConfig{
453+
Monitors: map[string]types.MonitorConfig{
454+
"qemu": {
455+
DefaultMemoryMB: 512,
456+
DefaultVCPUs: 2,
457+
Vhost: true,
458+
},
459+
},
460+
ExtraBins: map[string]types.ExtraBinConfig{},
461+
}
462+
463+
cfgMap := config.Map()
464+
465+
assert.Equal(t, "true", cfgMap[testQemuVhostKey])
466+
})
418467
}
419468

420469
func TestDefaultConfigs(t *testing.T) {
@@ -449,6 +498,7 @@ func TestDefaultConfigs(t *testing.T) {
449498
assert.Equal(t, uint(256), hvConfig.DefaultMemoryMB)
450499
assert.Equal(t, uint(1), hvConfig.DefaultVCPUs)
451500
assert.Equal(t, "", hvConfig.BinaryPath)
501+
assert.False(t, hvConfig.Vhost, "vhost should be false by default")
452502
}
453503
})
454504

0 commit comments

Comments
 (0)