From f700a97beef89be2d759a1616a7364e92bc8ec77 Mon Sep 17 00:00:00 2001 From: Julio Montes Date: Fri, 13 Jul 2018 12:05:19 -0500 Subject: [PATCH 1/3] qemu/qmp: implement function to hotplug vsock-pci Implement function to hotplug vsocks, vsocks are needed to communicate processes are running inside the VM with processes are running on the host. Signed-off-by: Julio Montes --- qemu/qemu.go | 3 +++ qemu/qmp.go | 10 ++++++++++ qemu/qmp_test.go | 17 +++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/qemu/qemu.go b/qemu/qemu.go index 41381d80ee..e911364846 100644 --- a/qemu/qemu.go +++ b/qemu/qemu.go @@ -82,6 +82,9 @@ const ( // VirtioSerialPort is the serial port device driver. VirtioSerialPort = "virtserialport" + + // VHostVSockPCI is the vhost vsock pci driver. + VHostVSockPCI = "vhost-vsock-pci" ) // ObjectType is a string representing a qemu object type. diff --git a/qemu/qmp.go b/qemu/qmp.go index 37334e99e2..32a59ef04b 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -881,3 +881,13 @@ func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string return err } + +// ExecutePCIVSockAdd adds a vhost-vsock-pci bus +func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID string) error { + args := map[string]interface{}{ + "driver": VHostVSockPCI, + "id": id, + "guest-cid": guestCID, + } + return q.executeCommand(ctx, "device_add", args, nil) +} diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index f5b472d741..c87822ae8e 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -945,3 +945,20 @@ func TestExecHotplugMemory(t *testing.T) { q.Shutdown() <-disconnectedCh } + +// Checks vsock-pci hotplug +func TestExecutePCIVSockAdd(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("device_add", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + err := q.ExecutePCIVSockAdd(context.Background(), "vsock-pci0", "3") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} From 3830b4419f4ddee5de05dc89e6d934e345837e76 Mon Sep 17 00:00:00 2001 From: Julio Montes Date: Tue, 24 Jul 2018 13:37:28 -0500 Subject: [PATCH 2/3] qemu: add vhostfd and disable-modern to vhost-vsock-pci `vhostfd` is the vhost file descriptor that holds the socket context ID `disable-modern` prevents qemu from relying on fast MMIO Signed-off-by: Julio Montes --- qemu/qemu.go | 20 ++++++++++++++++++-- qemu/qemu_test.go | 14 +++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/qemu/qemu.go b/qemu/qemu.go index e911364846..4f7a383ee1 100644 --- a/qemu/qemu.go +++ b/qemu/qemu.go @@ -965,6 +965,12 @@ type VSOCKDevice struct { ID string ContextID uint32 + + // VHostFD vhost file descriptor that holds the ContextID + VHostFD *os.File + + // DisableModern prevents qemu from relying on fast MMIO. + DisableModern bool } const ( @@ -989,12 +995,22 @@ func (vsock VSOCKDevice) Valid() bool { // QemuParams returns the qemu parameters built out of the VSOCK device. func (vsock VSOCKDevice) QemuParams(config *Config) []string { + var deviceParams []string var qemuParams []string - deviceParam := fmt.Sprintf("%s,id=%s,%s=%d", VhostVSOCKPCI, vsock.ID, VSOCKGuestCID, vsock.ContextID) + deviceParams = append(deviceParams, fmt.Sprintf("%s", VhostVSOCKPCI)) + if vsock.DisableModern { + deviceParams = append(deviceParams, ",disable-modern=true") + } + if vsock.VHostFD != nil { + qemuFDs := config.appendFDs([]*os.File{vsock.VHostFD}) + deviceParams = append(deviceParams, fmt.Sprintf(",vhostfd=%d", qemuFDs[0])) + } + deviceParams = append(deviceParams, fmt.Sprintf(",id=%s", vsock.ID)) + deviceParams = append(deviceParams, fmt.Sprintf(",%s=%d", VSOCKGuestCID, vsock.ContextID)) qemuParams = append(qemuParams, "-device") - qemuParams = append(qemuParams, deviceParam) + qemuParams = append(qemuParams, strings.Join(deviceParams, "")) return qemuParams } diff --git a/qemu/qemu_test.go b/qemu/qemu_test.go index 14637910d0..be9657b0eb 100644 --- a/qemu/qemu_test.go +++ b/qemu/qemu_test.go @@ -329,12 +329,14 @@ func TestAppendDeviceVFIO(t *testing.T) { testAppend(vfioDevice, deviceVFIOString, t) } -var deviceVSOCKString = "-device vhost-vsock-pci,id=vhost-vsock-pci0,guest-cid=4" +var deviceVSOCKString = "-device vhost-vsock-pci,disable-modern=true,id=vhost-vsock-pci0,guest-cid=4" func TestAppendVSOCK(t *testing.T) { vsockDevice := VSOCKDevice{ - ID: "vhost-vsock-pci0", - ContextID: 4, + ID: "vhost-vsock-pci0", + ContextID: 4, + VHostFD: nil, + DisableModern: true, } testAppend(vsockDevice, deviceVSOCKString, t) @@ -342,8 +344,10 @@ func TestAppendVSOCK(t *testing.T) { func TestVSOCKValid(t *testing.T) { vsockDevice := VSOCKDevice{ - ID: "vhost-vsock-pci0", - ContextID: MinimalGuestCID - 1, + ID: "vhost-vsock-pci0", + ContextID: MinimalGuestCID - 1, + VHostFD: nil, + DisableModern: true, } if vsockDevice.Valid() { From 12dfa872936de1f8cd003238711a044b37be9b59 Mon Sep 17 00:00:00 2001 From: Ruidong Cao Date: Tue, 24 Jul 2018 17:06:22 +0800 Subject: [PATCH 3/3] qemu/qmp: implement function for hotplug network Implement function to hotplug and delete a network device to QEMU Signed-off-by: Ruidong Cao --- qemu/qmp.go | 45 ++++++++++++++++++++++++++++++++++++ qemu/qmp_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/qemu/qmp.go b/qemu/qmp.go index 37334e99e2..c4e9f7356a 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -727,6 +727,51 @@ func (q *QMP) ExecuteBlockdevDel(ctx context.Context, blockdevID string) error { return q.executeCommand(ctx, "x-blockdev-del", args, nil) } +// ExecuteNetdevAdd adds a Net device to a QEMU instance +// using the netdev_add command. netdevID is the id of the device to add. +// Must be valid QMP identifier. +func (q *QMP) ExecuteNetdevAdd(ctx context.Context, netdevType, netdevID, ifname, downscript, script string, queues int) error { + args := map[string]interface{}{ + "type": netdevType, + "id": netdevID, + "ifname": ifname, + "downscript": downscript, + "script": script, + } + if queues > 1 { + args["queues"] = queues + } + + return q.executeCommand(ctx, "netdev_add", args, nil) +} + +// ExecuteNetdevDel deletes a Net device from a QEMU instance +// using the netdev_del command. netdevID is the id of the device to delete. +func (q *QMP) ExecuteNetdevDel(ctx context.Context, netdevID string) error { + args := map[string]interface{}{ + "id": netdevID, + } + return q.executeCommand(ctx, "netdev_del", args, nil) +} + +// ExecuteNetPCIDeviceAdd adds a Net PCI device to a QEMU instance +// using the device_add command. devID is the id of the device to add. +// Must be valid QMP identifier. netdevID is the id of nic added by previous netdev_add. +func (q *QMP) ExecuteNetPCIDeviceAdd(ctx context.Context, netdevID, devID, macAddr, addr, bus string) error { + args := map[string]interface{}{ + "id": devID, + "driver": VirtioNetPCI, + "netdev": netdevID, + "mac": macAddr, + "addr": addr, + } + + if bus != "" { + args["bus"] = bus + } + return q.executeCommand(ctx, "device_add", args, nil) +} + // ExecuteDeviceDel deletes guest portion of a QEMU device by sending a // device_del command. devId is the identifier of the device to delete. // Typically it would match the devID parameter passed to an earlier call diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index f5b472d741..72ef08ddfa 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -364,6 +364,66 @@ func TestQMPBlockdevAdd(t *testing.T) { <-disconnectedCh } +// Checks that the netdev_add command is correctly sent. +// +// We start a QMPLoop, send the netdev_add command and stop the loop. +// +// The netdev_add command should be correctly sent and the QMP loop should +// exit gracefully. +func TestQMPNetdevAdd(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("netdev_add", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + q.version = checkVersion(t, connectedCh) + err := q.ExecuteNetdevAdd(context.Background(), "tap", "br0", "tap0", "no", "no", 8) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + +// Checks that the netdev_del command is correctly sent. +// +// We start a QMPLoop, send the netdev_del command and stop the loop. +// +// The netdev_del command should be correctly sent and the QMP loop should +// exit gracefully. +func TestQMPNetdevDel(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("netdev_del", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + q.version = checkVersion(t, connectedCh) + err := q.ExecuteNetdevDel(context.Background(), "br0") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + +func TestQMPNetPCIDeviceAdd(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("device_add", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + err := q.ExecuteNetPCIDeviceAdd(context.Background(), "br0", "virtio-0", "02:42:ac:11:00:02", "0x7", "") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + // Checks that the device_add command is correctly sent. // // We start a QMPLoop, send the device_add command and stop the loop.