migration: Add error_desc for file channel errors
Currently, there is no information about error if outgoing migration was failed because of file channel errors. Example (QMP session): -> { "execute": "migrate", "arguments": { "uri": "exec:head -c 1" }} <- { "return": {} } ... -> { "execute": "query-migrate" } <- { "return": { "status": "failed" }} // There is not error's description And even in the QEMU's output there is nothing. This patch 1) Adds errp for the most of QEMUFileOps 2) Adds qemu_file_get_error_obj/qemu_file_set_error_obj 3) And finally using of qemu_file_get_error_obj in migration.c And now, the status for the mentioned fail will be: -> { "execute": "query-migrate" } <- { "return": { "status": "failed", "error-desc": "Unable to write to command: Broken pipe" }} Signed-off-by: Yury Kotov <yury-kotov@yandex-team.ru> Message-Id: <20190422103420.15686-1-yury-kotov@yandex-team.ru> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
This commit is contained in:
parent
f28ed74fd1
commit
3d661c8ab1
@ -2963,6 +2963,7 @@ static MigThrError migration_detect_error(MigrationState *s)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int state = s->state;
|
int state = s->state;
|
||||||
|
Error *local_error = NULL;
|
||||||
|
|
||||||
if (state == MIGRATION_STATUS_CANCELLING ||
|
if (state == MIGRATION_STATUS_CANCELLING ||
|
||||||
state == MIGRATION_STATUS_CANCELLED) {
|
state == MIGRATION_STATUS_CANCELLED) {
|
||||||
@ -2971,13 +2972,18 @@ static MigThrError migration_detect_error(MigrationState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Try to detect any file errors */
|
/* Try to detect any file errors */
|
||||||
ret = qemu_file_get_error(s->to_dst_file);
|
ret = qemu_file_get_error_obj(s->to_dst_file, &local_error);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
/* Everything is fine */
|
/* Everything is fine */
|
||||||
|
assert(!local_error);
|
||||||
return MIG_THR_ERR_NONE;
|
return MIG_THR_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (local_error) {
|
||||||
|
migrate_set_error(s, local_error);
|
||||||
|
error_free(local_error);
|
||||||
|
}
|
||||||
|
|
||||||
if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) {
|
if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) {
|
||||||
/*
|
/*
|
||||||
* For postcopy, we allow the network to be down for a
|
* For postcopy, we allow the network to be down for a
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
static ssize_t channel_writev_buffer(void *opaque,
|
static ssize_t channel_writev_buffer(void *opaque,
|
||||||
struct iovec *iov,
|
struct iovec *iov,
|
||||||
int iovcnt,
|
int iovcnt,
|
||||||
int64_t pos)
|
int64_t pos,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
||||||
ssize_t done = 0;
|
ssize_t done = 0;
|
||||||
@ -47,7 +48,7 @@ static ssize_t channel_writev_buffer(void *opaque,
|
|||||||
|
|
||||||
while (nlocal_iov > 0) {
|
while (nlocal_iov > 0) {
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL);
|
len = qio_channel_writev(ioc, local_iov, nlocal_iov, errp);
|
||||||
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
if (len == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
if (qemu_in_coroutine()) {
|
if (qemu_in_coroutine()) {
|
||||||
qio_channel_yield(ioc, G_IO_OUT);
|
qio_channel_yield(ioc, G_IO_OUT);
|
||||||
@ -57,7 +58,6 @@ static ssize_t channel_writev_buffer(void *opaque,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
/* XXX handle Error objects */
|
|
||||||
done = -EIO;
|
done = -EIO;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -75,13 +75,14 @@ static ssize_t channel_writev_buffer(void *opaque,
|
|||||||
static ssize_t channel_get_buffer(void *opaque,
|
static ssize_t channel_get_buffer(void *opaque,
|
||||||
uint8_t *buf,
|
uint8_t *buf,
|
||||||
int64_t pos,
|
int64_t pos,
|
||||||
size_t size)
|
size_t size,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
ret = qio_channel_read(ioc, (char *)buf, size, NULL);
|
ret = qio_channel_read(ioc, (char *)buf, size, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
if (ret == QIO_CHANNEL_ERR_BLOCK) {
|
||||||
if (qemu_in_coroutine()) {
|
if (qemu_in_coroutine()) {
|
||||||
@ -90,7 +91,6 @@ static ssize_t channel_get_buffer(void *opaque,
|
|||||||
qio_channel_wait(ioc, G_IO_IN);
|
qio_channel_wait(ioc, G_IO_IN);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* XXX handle Error * object */
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,18 +100,20 @@ static ssize_t channel_get_buffer(void *opaque,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int channel_close(void *opaque)
|
static int channel_close(void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
||||||
qio_channel_close(ioc, NULL);
|
ret = qio_channel_close(ioc, errp);
|
||||||
object_unref(OBJECT(ioc));
|
object_unref(OBJECT(ioc));
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int channel_shutdown(void *opaque,
|
static int channel_shutdown(void *opaque,
|
||||||
bool rd,
|
bool rd,
|
||||||
bool wr)
|
bool wr,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
||||||
|
|
||||||
@ -125,8 +127,7 @@ static int channel_shutdown(void *opaque,
|
|||||||
} else {
|
} else {
|
||||||
mode = QIO_CHANNEL_SHUTDOWN_WRITE;
|
mode = QIO_CHANNEL_SHUTDOWN_WRITE;
|
||||||
}
|
}
|
||||||
if (qio_channel_shutdown(ioc, mode, NULL) < 0) {
|
if (qio_channel_shutdown(ioc, mode, errp) < 0) {
|
||||||
/* XXX handler Error * object */
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,11 +136,12 @@ static int channel_shutdown(void *opaque,
|
|||||||
|
|
||||||
|
|
||||||
static int channel_set_blocking(void *opaque,
|
static int channel_set_blocking(void *opaque,
|
||||||
bool enabled)
|
bool enabled,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
QIOChannel *ioc = QIO_CHANNEL(opaque);
|
||||||
|
|
||||||
if (qio_channel_set_blocking(ioc, enabled, NULL) < 0) {
|
if (qio_channel_set_blocking(ioc, enabled, errp) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "migration.h"
|
#include "migration.h"
|
||||||
#include "qemu-file.h"
|
#include "qemu-file.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
#define IO_BUF_SIZE 32768
|
#define IO_BUF_SIZE 32768
|
||||||
#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
|
#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
|
||||||
@ -51,6 +52,7 @@ struct QEMUFile {
|
|||||||
unsigned int iovcnt;
|
unsigned int iovcnt;
|
||||||
|
|
||||||
int last_error;
|
int last_error;
|
||||||
|
Error *last_error_obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -62,7 +64,7 @@ int qemu_file_shutdown(QEMUFile *f)
|
|||||||
if (!f->ops->shut_down) {
|
if (!f->ops->shut_down) {
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
return f->ops->shut_down(f->opaque, true, true);
|
return f->ops->shut_down(f->opaque, true, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -106,6 +108,36 @@ void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks)
|
|||||||
f->hooks = hooks;
|
f->hooks = hooks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get last error for stream f with optional Error*
|
||||||
|
*
|
||||||
|
* Return negative error value if there has been an error on previous
|
||||||
|
* operations, return 0 if no error happened.
|
||||||
|
* Optional, it returns Error* in errp, but it may be NULL even if return value
|
||||||
|
* is not 0.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
int qemu_file_get_error_obj(QEMUFile *f, Error **errp)
|
||||||
|
{
|
||||||
|
if (errp) {
|
||||||
|
*errp = f->last_error_obj ? error_copy(f->last_error_obj) : NULL;
|
||||||
|
}
|
||||||
|
return f->last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the last error for stream f with optional Error*
|
||||||
|
*/
|
||||||
|
void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err)
|
||||||
|
{
|
||||||
|
if (f->last_error == 0 && ret) {
|
||||||
|
f->last_error = ret;
|
||||||
|
error_propagate(&f->last_error_obj, err);
|
||||||
|
} else if (err) {
|
||||||
|
error_report_err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get last error for stream f
|
* Get last error for stream f
|
||||||
*
|
*
|
||||||
@ -115,14 +147,15 @@ void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks)
|
|||||||
*/
|
*/
|
||||||
int qemu_file_get_error(QEMUFile *f)
|
int qemu_file_get_error(QEMUFile *f)
|
||||||
{
|
{
|
||||||
return f->last_error;
|
return qemu_file_get_error_obj(f, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the last error for stream f
|
||||||
|
*/
|
||||||
void qemu_file_set_error(QEMUFile *f, int ret)
|
void qemu_file_set_error(QEMUFile *f, int ret)
|
||||||
{
|
{
|
||||||
if (f->last_error == 0) {
|
qemu_file_set_error_obj(f, ret, NULL);
|
||||||
f->last_error = ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qemu_file_is_writable(QEMUFile *f)
|
bool qemu_file_is_writable(QEMUFile *f)
|
||||||
@ -176,6 +209,7 @@ void qemu_fflush(QEMUFile *f)
|
|||||||
{
|
{
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
ssize_t expect = 0;
|
ssize_t expect = 0;
|
||||||
|
Error *local_error = NULL;
|
||||||
|
|
||||||
if (!qemu_file_is_writable(f)) {
|
if (!qemu_file_is_writable(f)) {
|
||||||
return;
|
return;
|
||||||
@ -183,7 +217,8 @@ void qemu_fflush(QEMUFile *f)
|
|||||||
|
|
||||||
if (f->iovcnt > 0) {
|
if (f->iovcnt > 0) {
|
||||||
expect = iov_size(f->iov, f->iovcnt);
|
expect = iov_size(f->iov, f->iovcnt);
|
||||||
ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
|
ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos,
|
||||||
|
&local_error);
|
||||||
|
|
||||||
qemu_iovec_release_ram(f);
|
qemu_iovec_release_ram(f);
|
||||||
}
|
}
|
||||||
@ -195,7 +230,7 @@ void qemu_fflush(QEMUFile *f)
|
|||||||
* data set we requested, so sanity check that.
|
* data set we requested, so sanity check that.
|
||||||
*/
|
*/
|
||||||
if (ret != expect) {
|
if (ret != expect) {
|
||||||
qemu_file_set_error(f, ret < 0 ? ret : -EIO);
|
qemu_file_set_error_obj(f, ret < 0 ? ret : -EIO, local_error);
|
||||||
}
|
}
|
||||||
f->buf_index = 0;
|
f->buf_index = 0;
|
||||||
f->iovcnt = 0;
|
f->iovcnt = 0;
|
||||||
@ -283,6 +318,7 @@ static ssize_t qemu_fill_buffer(QEMUFile *f)
|
|||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
int pending;
|
int pending;
|
||||||
|
Error *local_error = NULL;
|
||||||
|
|
||||||
assert(!qemu_file_is_writable(f));
|
assert(!qemu_file_is_writable(f));
|
||||||
|
|
||||||
@ -294,14 +330,16 @@ static ssize_t qemu_fill_buffer(QEMUFile *f)
|
|||||||
f->buf_size = pending;
|
f->buf_size = pending;
|
||||||
|
|
||||||
len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
|
len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
|
||||||
IO_BUF_SIZE - pending);
|
IO_BUF_SIZE - pending, &local_error);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
f->buf_size += len;
|
f->buf_size += len;
|
||||||
f->pos += len;
|
f->pos += len;
|
||||||
} else if (len == 0) {
|
} else if (len == 0) {
|
||||||
qemu_file_set_error(f, -EIO);
|
qemu_file_set_error_obj(f, -EIO, local_error);
|
||||||
} else if (len != -EAGAIN) {
|
} else if (len != -EAGAIN) {
|
||||||
qemu_file_set_error(f, len);
|
qemu_file_set_error_obj(f, len, local_error);
|
||||||
|
} else {
|
||||||
|
error_free(local_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@ -327,7 +365,7 @@ int qemu_fclose(QEMUFile *f)
|
|||||||
ret = qemu_file_get_error(f);
|
ret = qemu_file_get_error(f);
|
||||||
|
|
||||||
if (f->ops->close) {
|
if (f->ops->close) {
|
||||||
int ret2 = f->ops->close(f->opaque);
|
int ret2 = f->ops->close(f->opaque, NULL);
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
ret = ret2;
|
ret = ret2;
|
||||||
}
|
}
|
||||||
@ -338,6 +376,7 @@ int qemu_fclose(QEMUFile *f)
|
|||||||
if (f->last_error) {
|
if (f->last_error) {
|
||||||
ret = f->last_error;
|
ret = f->last_error;
|
||||||
}
|
}
|
||||||
|
error_free(f->last_error_obj);
|
||||||
g_free(f);
|
g_free(f);
|
||||||
trace_qemu_file_fclose();
|
trace_qemu_file_fclose();
|
||||||
return ret;
|
return ret;
|
||||||
@ -783,6 +822,6 @@ void qemu_put_counted_string(QEMUFile *f, const char *str)
|
|||||||
void qemu_file_set_blocking(QEMUFile *f, bool block)
|
void qemu_file_set_blocking(QEMUFile *f, bool block)
|
||||||
{
|
{
|
||||||
if (f->ops->set_blocking) {
|
if (f->ops->set_blocking) {
|
||||||
f->ops->set_blocking(f->opaque, block);
|
f->ops->set_blocking(f->opaque, block, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
* bytes actually read should be returned.
|
* bytes actually read should be returned.
|
||||||
*/
|
*/
|
||||||
typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
|
typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
|
||||||
int64_t pos, size_t size);
|
int64_t pos, size_t size,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
/* Close a file
|
/* Close a file
|
||||||
*
|
*
|
||||||
@ -41,7 +42,7 @@ typedef ssize_t (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
|
|||||||
* The meaning of return value on success depends on the specific back-end being
|
* The meaning of return value on success depends on the specific back-end being
|
||||||
* used.
|
* used.
|
||||||
*/
|
*/
|
||||||
typedef int (QEMUFileCloseFunc)(void *opaque);
|
typedef int (QEMUFileCloseFunc)(void *opaque, Error **errp);
|
||||||
|
|
||||||
/* Called to return the OS file descriptor associated to the QEMUFile.
|
/* Called to return the OS file descriptor associated to the QEMUFile.
|
||||||
*/
|
*/
|
||||||
@ -49,14 +50,15 @@ typedef int (QEMUFileGetFD)(void *opaque);
|
|||||||
|
|
||||||
/* Called to change the blocking mode of the file
|
/* Called to change the blocking mode of the file
|
||||||
*/
|
*/
|
||||||
typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled);
|
typedef int (QEMUFileSetBlocking)(void *opaque, bool enabled, Error **errp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function writes an iovec to file. The handler must write all
|
* This function writes an iovec to file. The handler must write all
|
||||||
* of the data or return a negative errno value.
|
* of the data or return a negative errno value.
|
||||||
*/
|
*/
|
||||||
typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov,
|
typedef ssize_t (QEMUFileWritevBufferFunc)(void *opaque, struct iovec *iov,
|
||||||
int iovcnt, int64_t pos);
|
int iovcnt, int64_t pos,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function provides hooks around different
|
* This function provides hooks around different
|
||||||
@ -97,7 +99,8 @@ typedef QEMUFile *(QEMURetPathFunc)(void *opaque);
|
|||||||
* Existing blocking reads/writes must be woken
|
* Existing blocking reads/writes must be woken
|
||||||
* Returns 0 on success, -err on error
|
* Returns 0 on success, -err on error
|
||||||
*/
|
*/
|
||||||
typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr);
|
typedef int (QEMUFileShutdownFunc)(void *opaque, bool rd, bool wr,
|
||||||
|
Error **errp);
|
||||||
|
|
||||||
typedef struct QEMUFileOps {
|
typedef struct QEMUFileOps {
|
||||||
QEMUFileGetBufferFunc *get_buffer;
|
QEMUFileGetBufferFunc *get_buffer;
|
||||||
@ -149,6 +152,8 @@ void qemu_update_position(QEMUFile *f, size_t size);
|
|||||||
void qemu_file_reset_rate_limit(QEMUFile *f);
|
void qemu_file_reset_rate_limit(QEMUFile *f);
|
||||||
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
|
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
|
||||||
int64_t qemu_file_get_rate_limit(QEMUFile *f);
|
int64_t qemu_file_get_rate_limit(QEMUFile *f);
|
||||||
|
int qemu_file_get_error_obj(QEMUFile *f, Error **errp);
|
||||||
|
void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err);
|
||||||
void qemu_file_set_error(QEMUFile *f, int ret);
|
void qemu_file_set_error(QEMUFile *f, int ret);
|
||||||
int qemu_file_shutdown(QEMUFile *f);
|
int qemu_file_shutdown(QEMUFile *f);
|
||||||
QEMUFile *qemu_file_get_return_path(QEMUFile *f);
|
QEMUFile *qemu_file_get_return_path(QEMUFile *f);
|
||||||
|
@ -124,7 +124,7 @@ static struct mig_cmd_args {
|
|||||||
/* savevm/loadvm support */
|
/* savevm/loadvm support */
|
||||||
|
|
||||||
static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
|
static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
|
||||||
int64_t pos)
|
int64_t pos, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
@ -139,12 +139,12 @@ static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
|
static ssize_t block_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
|
||||||
size_t size)
|
size_t size, Error **errp)
|
||||||
{
|
{
|
||||||
return bdrv_load_vmstate(opaque, buf, pos, size);
|
return bdrv_load_vmstate(opaque, buf, pos, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bdrv_fclose(void *opaque)
|
static int bdrv_fclose(void *opaque, Error **errp)
|
||||||
{
|
{
|
||||||
return bdrv_flush(opaque);
|
return bdrv_flush(opaque);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user