diff --git a/qemu/qmp.go b/qemu/qmp.go index 473ce4afaa..9d27dbeefb 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -946,6 +946,17 @@ func (q *QMP) ExecuteBlockdevDel(ctx context.Context, blockdevID string) error { return q.executeCommand(ctx, "x-blockdev-del", args, nil) } +// ExecuteChardevDel deletes a char device by sending a chardev-remove command. +// chardevID is the id of the char device to be deleted. Typically, this will +// match the id passed to ExecuteCharDevUnixSocketAdd. It must be a valid QMP id. +func (q *QMP) ExecuteChardevDel(ctx context.Context, chardevID string) error { + args := map[string]interface{}{ + "id": chardevID, + } + + return q.executeCommand(ctx, "chardev-remove", 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. @@ -1124,6 +1135,27 @@ func (q *QMP) ExecutePCIDeviceAdd(ctx context.Context, blockdevID, devID, driver return q.executeCommand(ctx, "device_add", args, nil) } +// ExecutePCIVhostUserDevAdd adds a vhost-user device to a QEMU instance using the device_add command. +// This function can be used to hot plug vhost-user devices on PCI(E) bridges. +// It receives the bus and the device address on its parent bus. bus is optional. +// devID is the id of the device to add.Must be valid QMP identifier. chardevID +// is the QMP identifier of character device using a unix socket as backend. +// driver is the name of vhost-user driver, like vhost-user-blk-pci. +func (q *QMP) ExecutePCIVhostUserDevAdd(ctx context.Context, driver, devID, chardevID, addr, bus string) error { + args := map[string]interface{}{ + "driver": driver, + "id": devID, + "chardev": chardevID, + "addr": addr, + } + + if bus != "" { + args["bus"] = bus + } + + return q.executeCommand(ctx, "device_add", args, nil) +} + // ExecuteVFIODeviceAdd adds a VFIO device to a QEMU instance // using the device_add command. devID is the id of the device to add. // Must be valid QMP identifier. bdf is the PCI bus-device-function diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index c5722765ab..71f97bcadd 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -638,6 +638,28 @@ func TestQMPBlockdevDel(t *testing.T) { <-disconnectedCh } +// Checks that the chardev-remove command is correctly sent. +// +// We start a QMPLoop, send the chardev-remove command and stop the loop. +// +// The chardev-remove command should be correctly sent and the QMP loop should +// exit gracefully. +func TestQMPChardevDel(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("chardev-remove", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + q.version = checkVersion(t, connectedCh) + err := q.ExecuteChardevDel(context.Background(), "chardev-0") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + // Checks that the device_del command is correctly sent. // // We start a QMPLoop, send the device_del command and wait for it to complete. @@ -1307,6 +1329,26 @@ func TestExecutePCIVSockAdd(t *testing.T) { <-disconnectedCh } +// Checks vhost-user-pci hotplug +func TestExecutePCIVhostUserDevAdd(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) + driver := "vhost-user-blk-pci" + devID := "vhost-user-blk0" + chardevID := "vhost-user-blk-char0" + err := q.ExecutePCIVhostUserDevAdd(context.Background(), driver, devID, chardevID, "1", "1") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + // Checks getfd func TestExecuteGetFdD(t *testing.T) { connectedCh := make(chan *QMPVersion)