diff --git a/qemu/qmp.go b/qemu/qmp.go index 43daed369d..97d549ecee 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -722,11 +722,7 @@ func (q *QMP) ExecuteQuit(ctx context.Context) error { return q.executeCommand(ctx, "quit", nil, nil) } -// ExecuteBlockdevAdd sends a blockdev-add to the QEMU instance. device is the -// path of the device to add, e.g., /dev/rdb0, and blockdevID is an identifier -// used to name the device. As this identifier will be passed directly to QMP, -// it must obey QMP's naming rules, e,g., it must start with a letter. -func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) error { +func (q *QMP) blockdevAddBaseArgs(device, blockdevID string) (map[string]interface{}, map[string]interface{}) { var args map[string]interface{} blockdevArgs := map[string]interface{}{ @@ -747,6 +743,39 @@ func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) } } + return args, blockdevArgs +} + +// ExecuteBlockdevAdd sends a blockdev-add to the QEMU instance. device is the +// path of the device to add, e.g., /dev/rdb0, and blockdevID is an identifier +// used to name the device. As this identifier will be passed directly to QMP, +// it must obey QMP's naming rules, e,g., it must start with a letter. +func (q *QMP) ExecuteBlockdevAdd(ctx context.Context, device, blockdevID string) error { + args, _ := q.blockdevAddBaseArgs(device, blockdevID) + + return q.executeCommand(ctx, "blockdev-add", args, nil) +} + +// ExecuteBlockdevAddWithCache has two more parameters direct and noFlush +// than ExecuteBlockdevAdd. +// They are cache-related options for block devices that are described in +// https://github.com/qemu/qemu/blob/master/qapi/block-core.json. +// direct denotes whether use of O_DIRECT (bypass the host page cache) +// is enabled. noFlush denotes whether flush requests for the device are +// ignored. +func (q *QMP) ExecuteBlockdevAddWithCache(ctx context.Context, device, blockdevID string, direct, noFlush bool) error { + args, blockdevArgs := q.blockdevAddBaseArgs(device, blockdevID) + + if q.version.Major < 2 || (q.version.Major == 2 && q.version.Minor < 9) { + return fmt.Errorf("versions of qemu (%d.%d) older than 2.9 do not support set cache-related options for block devices", + q.version.Major, q.version.Minor) + } + + blockdevArgs["cache"] = map[string]interface{}{ + "direct": direct, + "no-flush": noFlush, + } + return q.executeCommand(ctx, "blockdev-add", args, nil) } diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index a1a784dd6e..7d158d1b85 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -33,10 +33,10 @@ import ( const ( microStr = "50" - minorStr = "6" + minorStr = "9" majorStr = "2" micro = 50 - minor = 6 + minor = 9 major = 2 cap1 = "one" cap2 = "two" @@ -403,6 +403,30 @@ func TestQMPBlockdevAdd(t *testing.T) { <-disconnectedCh } +// Checks that the blockdev-add with cache options command is correctly sent. +// +// We start a QMPLoop, send the blockdev-add with cache options +// command and stop the loop. +// +// The blockdev-add with cache options command should be correctly sent and +// the QMP loop should exit gracefully. +func TestQMPBlockdevAddWithCache(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("blockdev-add", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + q.version = checkVersion(t, connectedCh) + err := q.ExecuteBlockdevAddWithCache(context.Background(), "/dev/rbd0", + fmt.Sprintf("drive_%s", volumeUUID), true, true) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + // Checks that the netdev_add command is correctly sent. // // We start a QMPLoop, send the netdev_add command and stop the loop. @@ -562,17 +586,17 @@ func TestQMPSCSIDeviceAdd(t *testing.T) { <-disconnectedCh } -// Checks that the x-blockdev-del command is correctly sent. +// Checks that the blockdev-del command is correctly sent. // -// We start a QMPLoop, send the x-blockdev-del command and stop the loop. +// We start a QMPLoop, send the blockdev-del command and stop the loop. // -// The x-blockdev-del command should be correctly sent and the QMP loop should +// The blockdev-del command should be correctly sent and the QMP loop should // exit gracefully. -func TestQMPXBlockdevDel(t *testing.T) { +func TestQMPBlockdevDel(t *testing.T) { connectedCh := make(chan *QMPVersion) disconnectedCh := make(chan struct{}) buf := newQMPTestCommandBuffer(t) - buf.AddCommand("x-blockdev-del", nil, "return", nil) + buf.AddCommand("blockdev-del", nil, "return", nil) cfg := QMPConfig{Logger: qmpTestLogger{}} q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) q.version = checkVersion(t, connectedCh)