Skip to content
This repository was archived by the owner on Apr 3, 2018. It is now read-only.

Commit b20ba87

Browse files
author
Sebastien Boeuf
committed
pod: Improve pod create performances
In order to optimize pod creation, this patch does not wait for the VM after it has been started. Instead, it will wait for it during the "start" stage. This will allow our VM to be started while the caller could potentially do other things between create and start. Globally, this improves the boot time of our containers. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
1 parent e62b2ae commit b20ba87

5 files changed

Lines changed: 88 additions & 90 deletions

File tree

hypervisor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,8 @@ func RunningOnVMM(cpuInfoPath string) (bool, error) {
446446
type hypervisor interface {
447447
init(pod *Pod) error
448448
createPod(podConfig PodConfig) error
449-
startPod(startCh, stopCh chan struct{}) error
449+
startPod() error
450+
waitPod(timeout int) error
450451
stopPod() error
451452
pausePod() error
452453
resumePod() error

mock_hypervisor.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ func (m *mockHypervisor) createPod(podConfig PodConfig) error {
3636
return nil
3737
}
3838

39-
func (m *mockHypervisor) startPod(startCh, stopCh chan struct{}) error {
40-
var msg struct{}
41-
startCh <- msg
39+
func (m *mockHypervisor) startPod() error {
40+
return nil
41+
}
42+
43+
func (m *mockHypervisor) waitPod(timeout int) error {
4244
return nil
4345
}
4446

mock_hypervisor_test.go

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package virtcontainers
1919
import (
2020
"fmt"
2121
"testing"
22-
"time"
2322
)
2423

2524
func TestMockHypervisorInit(t *testing.T) {
@@ -36,8 +35,7 @@ func TestMockHypervisorInit(t *testing.T) {
3635
}
3736

3837
// wrong config
39-
err := m.init(pod)
40-
if err == nil {
38+
if err := m.init(pod); err == nil {
4139
t.Fatal()
4240
}
4341

@@ -48,8 +46,7 @@ func TestMockHypervisorInit(t *testing.T) {
4846
}
4947

5048
// right config
51-
err = m.init(pod)
52-
if err != nil {
49+
if err := m.init(pod); err != nil {
5350
t.Fatal(err)
5451
}
5552
}
@@ -59,42 +56,39 @@ func TestMockHypervisorCreatePod(t *testing.T) {
5956

6057
config := PodConfig{}
6158

62-
err := m.createPod(config)
63-
if err != nil {
59+
if err := m.createPod(config); err != nil {
6460
t.Fatal(err)
6561
}
6662
}
6763

6864
func TestMockHypervisorStartPod(t *testing.T) {
6965
var m *mockHypervisor
7066

71-
startCh := make(chan struct{})
72-
stopCh := make(chan struct{})
67+
if err := m.startPod(); err != nil {
68+
t.Fatal(err)
69+
}
70+
}
7371

74-
go m.startPod(startCh, stopCh)
72+
func TestMockHypervisorWaitPod(t *testing.T) {
73+
var m *mockHypervisor
7574

76-
select {
77-
case <-startCh:
78-
break
79-
case <-time.After(time.Second):
80-
t.Fatal("Timeout waiting for start notification")
75+
if err := m.waitPod(0); err != nil {
76+
t.Fatal(err)
8177
}
8278
}
8379

8480
func TestMockHypervisorStopPod(t *testing.T) {
8581
var m *mockHypervisor
8682

87-
err := m.stopPod()
88-
if err != nil {
83+
if err := m.stopPod(); err != nil {
8984
t.Fatal(err)
9085
}
9186
}
9287

9388
func TestMockHypervisorAddDevice(t *testing.T) {
9489
var m *mockHypervisor
9590

96-
err := m.addDevice(nil, imgDev)
97-
if err != nil {
91+
if err := m.addDevice(nil, imgDev); err != nil {
9892
t.Fatal(err)
9993
}
10094
}

pod.go

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"strings"
2424
"sync"
2525
"syscall"
26-
"time"
2726

2827
"github.com/sirupsen/logrus"
2928
)
@@ -39,6 +38,10 @@ const controlSocket = "ctrl.sock"
3938
// to understand if the VM is still alive or not.
4039
const monitorSocket = "monitor.sock"
4140

41+
// vmStartTimeout represents the time in seconds a pod can wait before
42+
// to consider the VM starting operation failed.
43+
const vmStartTimeout = 10
44+
4245
// stateString is a string representing a pod state.
4346
type stateString string
4447

@@ -748,34 +751,13 @@ func (p *Pod) startSetState() error {
748751
return nil
749752
}
750753

751-
// startVM starts the VM, ensuring it is started before it returns or issuing
752-
// an error in case of timeout. Then it connects to the agent inside the VM.
754+
// startVM starts the VM.
753755
func (p *Pod) startVM(netNsPath string) error {
754-
vmStartedCh := make(chan struct{})
755-
vmStoppedCh := make(chan struct{})
756-
const timeout = time.Duration(10) * time.Second
757-
758-
l := p.Logger()
759-
l.Info("Starting VM")
760-
761-
go func() {
762-
p.network.run(netNsPath, func() error {
763-
err := p.hypervisor.startPod(vmStartedCh, vmStoppedCh)
764-
return err
765-
})
766-
}()
767-
768-
// Wait for the pod started notification
769-
select {
770-
case <-vmStartedCh:
771-
break
772-
case <-time.After(timeout):
773-
return fmt.Errorf("Did not receive the pod started notification (timeout %ds)", timeout)
774-
}
775-
776-
l.Info("VM started")
756+
p.Logger().Info("Starting VM")
777757

778-
return nil
758+
return p.network.run(netNsPath, func() error {
759+
return p.hypervisor.startPod()
760+
})
779761
}
780762

781763
// startShims registers all containers to the proxy and starts one
@@ -835,6 +817,14 @@ func (p *Pod) start() error {
835817
return err
836818
}
837819

820+
l := p.Logger()
821+
822+
if err := p.hypervisor.waitPod(vmStartTimeout); err != nil {
823+
return err
824+
}
825+
826+
l.Info("VM started")
827+
838828
if _, _, err := p.proxy.connect(*p, false); err != nil {
839829
return err
840830
}
@@ -855,7 +845,7 @@ func (p *Pod) start() error {
855845
}
856846
}
857847

858-
p.Logger().Info("started")
848+
l.Info("started")
859849

860850
return nil
861851
}

qemu.go

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -573,40 +573,6 @@ func (q *qemu) init(pod *Pod) error {
573573
return nil
574574
}
575575

576-
func (q *qemu) qmpMonitor(connectedCh chan struct{}) {
577-
defer func(qemu *qemu) {
578-
if q.qmpMonitorCh.qmp != nil {
579-
q.qmpMonitorCh.qmp.Shutdown()
580-
}
581-
582-
q.qmpMonitorCh.wg.Done()
583-
}(q)
584-
585-
cfg := ciaoQemu.QMPConfig{Logger: newQMPLogger()}
586-
qmp, ver, err := ciaoQemu.QMPStart(q.qmpMonitorCh.ctx, q.qmpMonitorCh.path, cfg, q.qmpMonitorCh.disconnectCh)
587-
if err != nil {
588-
q.Logger().WithError(err).Error("Failed to connect to QEMU instance")
589-
return
590-
}
591-
592-
q.qmpMonitorCh.qmp = qmp
593-
594-
q.Logger().WithFields(logrus.Fields{
595-
"qmp-major-version": ver.Major,
596-
"qmp-minor-version": ver.Minor,
597-
"qmp-micro-version": ver.Micro,
598-
"qmp-capabilities": strings.Join(ver.Capabilities, ","),
599-
}).Infof("QMP details")
600-
601-
err = q.qmpMonitorCh.qmp.ExecuteQMPCapabilities(q.qmpMonitorCh.ctx)
602-
if err != nil {
603-
q.Logger().WithError(err).Error(qmpCapErrMsg)
604-
return
605-
}
606-
607-
close(connectedCh)
608-
}
609-
610576
func (q *qemu) setCPUResources(podConfig PodConfig) ciaoQemu.SMP {
611577
vcpus := q.config.DefaultVCPUs
612578
if podConfig.VMConfig.VCPUs > 0 {
@@ -775,16 +741,61 @@ func (q *qemu) createPod(podConfig PodConfig) error {
775741
}
776742

777743
// startPod will start the Pod's VM.
778-
func (q *qemu) startPod(startCh, stopCh chan struct{}) error {
744+
func (q *qemu) startPod() error {
779745
strErr, err := ciaoQemu.LaunchQemu(q.qemuConfig, newQMPLogger())
780746
if err != nil {
781747
return fmt.Errorf("%s", strErr)
782748
}
783749

784-
// Start the QMP monitoring thread
785-
q.qmpMonitorCh.disconnectCh = stopCh
786-
q.qmpMonitorCh.wg.Add(1)
787-
q.qmpMonitor(startCh)
750+
return nil
751+
}
752+
753+
// waitPod will wait for the Pod's VM to be up and running.
754+
func (q *qemu) waitPod(timeout int) error {
755+
defer func(qemu *qemu) {
756+
if q.qmpMonitorCh.qmp != nil {
757+
q.qmpMonitorCh.qmp.Shutdown()
758+
}
759+
}(q)
760+
761+
if timeout < 0 {
762+
return fmt.Errorf("Invalid timeout %ds", timeout)
763+
}
764+
765+
disconnectCh := make(chan struct{})
766+
cfg := ciaoQemu.QMPConfig{Logger: newQMPLogger()}
767+
768+
var qmp *ciaoQemu.QMP
769+
var ver *ciaoQemu.QMPVersion
770+
var err error
771+
772+
timeStart := time.Now()
773+
for {
774+
qmp, ver, err = ciaoQemu.QMPStart(q.qmpMonitorCh.ctx, q.qmpMonitorCh.path, cfg, disconnectCh)
775+
if err == nil {
776+
break
777+
}
778+
779+
if int(time.Now().Sub(timeStart).Seconds()) > timeout {
780+
return fmt.Errorf("Failed to connect to QEMU instance (timeout %ds): %v", timeout, err)
781+
}
782+
783+
time.Sleep(time.Duration(50) * time.Millisecond)
784+
}
785+
786+
q.qmpMonitorCh.qmp = qmp
787+
788+
q.Logger().WithFields(logrus.Fields{
789+
"qmp-major-version": ver.Major,
790+
"qmp-minor-version": ver.Minor,
791+
"qmp-micro-version": ver.Micro,
792+
"qmp-capabilities": strings.Join(ver.Capabilities, ","),
793+
}).Infof("QMP details")
794+
795+
if err = q.qmpMonitorCh.qmp.ExecuteQMPCapabilities(q.qmpMonitorCh.ctx); err != nil {
796+
q.Logger().WithError(err).Error(qmpCapErrMsg)
797+
return err
798+
}
788799

789800
return nil
790801
}

0 commit comments

Comments
 (0)