From 84b212f1b85711e969eee035884de8787aebffd8 Mon Sep 17 00:00:00 2001 From: Julio Montes Date: Fri, 20 Jul 2018 15:58:31 -0500 Subject: [PATCH 1/2] qemu: add vhostfd and disable-modern to vsock hotplug `vhostfd` is used to specify the vhost-vsock device fd, and it holds the context ID previously opened. `disable-modern` is to disable the use of "modern" devices, by using virtio 0.9 instead of virtio 1.0. Particularly, this is useful when running the VM in a nested environment. Signed-off-by: Julio Montes --- qemu/qmp.go | 8 +++++++- qemu/qmp_test.go | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/qemu/qmp.go b/qemu/qmp.go index 32a59ef04..61788e0fa 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -883,11 +883,17 @@ func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string } // ExecutePCIVSockAdd adds a vhost-vsock-pci bus -func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID string) error { +func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID, vhostfd string, disableModern bool) error { args := map[string]interface{}{ "driver": VHostVSockPCI, "id": id, "guest-cid": guestCID, + "vhostfd": vhostfd, } + + if disableModern { + args["disable-modern"] = disableModern + } + return q.executeCommand(ctx, "device_add", args, nil) } diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index c87822ae8..5b238648d 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -955,7 +955,7 @@ func TestExecutePCIVSockAdd(t *testing.T) { cfg := QMPConfig{Logger: qmpTestLogger{}} q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) checkVersion(t, connectedCh) - err := q.ExecutePCIVSockAdd(context.Background(), "vsock-pci0", "3") + err := q.ExecutePCIVSockAdd(context.Background(), "vsock-pci0", "3", "1", true) if err != nil { t.Fatalf("Unexpected error %v", err) } From 03f1a1c3a8a3b10a389c99aa1b37b6b772463420 Mon Sep 17 00:00:00 2001 From: Julio Montes Date: Mon, 30 Jul 2018 15:07:11 -0500 Subject: [PATCH 2/2] qemu/qmp: implement getfd `getfd` receives a file descriptor via SCM rights and assign it a name, this command is useful to send file descriptors from the host, and then hot plug devices that needs file descriptors like vhost-vsock-pci devices. Signed-off-by: Julio Montes --- qemu/qmp.go | 28 ++++++++++++++++++++++++---- qemu/qmp_test.go | 18 ++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/qemu/qmp.go b/qemu/qmp.go index 61788e0fa..3913e9cf7 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -24,6 +24,8 @@ import ( "fmt" "io" "net" + "os" + "syscall" "time" "context" @@ -118,6 +120,7 @@ type qmpCommand struct { args map[string]interface{} filter *qmpEventFilter resultReceived bool + oob []byte } // QMP is a structure that contains the internal state used by startQMPLoop and @@ -302,7 +305,12 @@ func (q *QMP) writeNextQMPCommand(cmdQueue *list.List) { } q.cfg.Logger.Infof("%s", string(encodedCmd)) encodedCmd = append(encodedCmd, '\n') - _, err = q.conn.Write(encodedCmd) + if unixConn, ok := q.conn.(*net.UnixConn); ok && len(cmd.oob) > 0 { + _, _, err = unixConn.WriteMsgUnix(encodedCmd, cmd.oob, nil) + } else { + _, err = q.conn.Write(encodedCmd) + } + if err != nil { cmd.res <- qmpResult{ err: fmt.Errorf("Unable to write command to qmp socket %v", err), @@ -485,7 +493,7 @@ func startQMPLoop(conn io.ReadWriteCloser, cfg QMPConfig, } func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args map[string]interface{}, - filter *qmpEventFilter) (interface{}, error) { + oob []byte, filter *qmpEventFilter) (interface{}, error) { var err error var response interface{} resCh := make(chan qmpResult) @@ -498,6 +506,7 @@ func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args name: name, args: args, filter: filter, + oob: oob, }: } @@ -519,7 +528,7 @@ func (q *QMP) executeCommandWithResponse(ctx context.Context, name string, args func (q *QMP) executeCommand(ctx context.Context, name string, args map[string]interface{}, filter *qmpEventFilter) error { - _, err := q.executeCommandWithResponse(ctx, name, args, filter) + _, err := q.executeCommandWithResponse(ctx, name, args, nil, filter) return err } @@ -809,7 +818,7 @@ func (q *QMP) ExecuteCPUDeviceAdd(ctx context.Context, driver, cpuID, socketID, // ExecuteQueryHotpluggableCPUs returns a slice with the list of hotpluggable CPUs func (q *QMP) ExecuteQueryHotpluggableCPUs(ctx context.Context) ([]HotpluggableCPU, error) { - response, err := q.executeCommandWithResponse(ctx, "query-hotpluggable-cpus", nil, nil) + response, err := q.executeCommandWithResponse(ctx, "query-hotpluggable-cpus", nil, nil, nil) if err != nil { return nil, err } @@ -897,3 +906,14 @@ func (q *QMP) ExecutePCIVSockAdd(ctx context.Context, id, guestCID, vhostfd stri return q.executeCommand(ctx, "device_add", args, nil) } + +// ExecuteGetFD sends a file descriptor via SCM rights and assigns it a name +func (q *QMP) ExecuteGetFD(ctx context.Context, fdname string, fd *os.File) error { + oob := syscall.UnixRights(int(fd.Fd())) + args := map[string]interface{}{ + "fdname": fdname, + } + + _, err := q.executeCommandWithResponse(ctx, "getfd", args, oob, nil) + return err +} diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index 5b238648d..415387924 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "log" + "os" "reflect" "sync" "testing" @@ -962,3 +963,20 @@ func TestExecutePCIVSockAdd(t *testing.T) { q.Shutdown() <-disconnectedCh } + +// Checks getfd +func TestExecuteGetFdD(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("getfd", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + err := q.ExecuteGetFD(context.Background(), "foo", os.NewFile(0, "foo")) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +}