[[RESEND] 0/8] migration/dirtyrate: Introduce APIs for getting domain memory dirty rate

Sometimes domain's memory dirty rate is expected by user in order to decide whether it's proper to be migrated out or not. We have already completed the QEMU part of the capability: https://patchew.org/QEMU/1600237327-33618-1-git-send-email-zhengchuan@huawei... And this serial of patches introduce the corresponding LIBVIRT part -- DomainGetDirtyRateInfo API and corresponding virsh api -- "getdirtyrate". instructions: bash# virsh getdirtyrate --help NAME getdirtyrate - Get a vm's memory dirty rate SYNOPSIS getdirtyrate <domain> [--seconds <number>] [--calculate] [--query] DESCRIPTION Get memory dirty rate of a domain in order to decide whether it's proper to be migrated out or not. OPTIONS [--domain] <string> domain name, id or uuid --seconds <number> calculate memory dirty rate within specified seconds, a valid range of values is [1, 60], and would default to 1s. --calculate calculate dirty rate only, can be used together with --query, either or both is expected, otherwise would default to both. --query query dirty rate only, can be used together with --calculate, either or both is expected, otherwise would default to both. example: bash# virsh getdirtyrate --calculate --query --domain vm0 --seconds 1 status: measured startTime: 820148 calcTime: 1 s dirtyRate: 6 MB/s Hao Wang (8): migration/dirtyrate: Introduce virDomainDirtyRateInfo structure migration/dirtyrate: Implement qemuMonitorJSONExtractDirtyRateInfo migration/dirtyrate: Introduce dirty_rate async job migration/dirtyrate: Implement qemuDomainQueryDirtyRate migration/dirtyrate: Implement qemuDomainCalculateDirtyRate migration/dirtyrate: Introduce virDomainDirtyRateFlags migration/dirtyrate: Introduce DomainGetDirtyRateInfo API migration/dirtyrate: Introduce getdirtyrate virsh api include/libvirt/libvirt-domain.h | 65 ++++++++++++++++++ src/driver-hypervisor.h | 7 ++ src/libvirt-domain.c | 46 +++++++++++++ src/libvirt_public.syms | 5 ++ src/qemu/qemu_domainjob.c | 3 + src/qemu/qemu_domainjob.h | 1 + src/qemu/qemu_driver.c | 76 +++++++++++++++++++++ src/qemu/qemu_migration.c | 63 +++++++++++++++++ src/qemu/qemu_migration.h | 10 +++ src/qemu/qemu_monitor.c | 24 +++++++ src/qemu/qemu_monitor.h | 8 +++ src/qemu/qemu_monitor_json.c | 97 ++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ src/qemu/qemu_process.c | 3 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 21 +++++- tools/virsh-domain.c | 113 +++++++++++++++++++++++++++++++ 17 files changed, 550 insertions(+), 1 deletion(-) -- 2.23.0

Introduce virDomainDirtyRateInfo structure used for domain's memory dirty rate query. Signed-off-by: Hao Wang <wanghao232@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- include/libvirt/libvirt-domain.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 77f9116675..c7e22d4af1 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5012,4 +5012,28 @@ int virDomainBackupBegin(virDomainPtr domain, char *virDomainBackupGetXMLDesc(virDomainPtr domain, unsigned int flags); +/** + * virDomainDirtyRateInfo: + * + * a virDomainDirtyRateInfo is a structure filled by virDomainGetDirtyRate() and + * extracting dirty rate infomation for a given active Domain. + */ + +typedef struct _virDomainDirtyRateInfo virDomainDirtyRateInfo; + +struct _virDomainDirtyRateInfo { + int status; /* the status of dirtyrate calculation */ + long long dirtyRate; /* the dirtyrate in MB/s */ + long long startTime; /* the start time of dirtyrate calculation */ + long long calcTime; /* the period of dirtyrate calculation */ +}; + +/** + * virDomainDirtyRateInfoPtr: + * + * a virDomainDirtyRateInfoPtr is a pointer to a virDomainDirtyRateInfo structure. + */ + +typedef virDomainDirtyRateInfo *virDomainDirtyRateInfoPtr; + #endif /* LIBVIRT_DOMAIN_H */ -- 2.23.0

Implement qemuMonitorJSONExtractDirtyRateInfo to deal with the return from qmp "query-dirty-rate", and store them in virDomainDirtyRateInfo. Signed-off-by: Hao Wang <wanghao232@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- include/libvirt/libvirt-domain.h | 16 +++++++++++ src/qemu/qemu_monitor_json.c | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index c7e22d4af1..9bf4f8a8cf 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5012,6 +5012,22 @@ int virDomainBackupBegin(virDomainPtr domain, char *virDomainBackupGetXMLDesc(virDomainPtr domain, unsigned int flags); +/** + * virDomainDirtyRateStatus: + * + * Details on the cause of a dirtyrate calculation status. + */ + +typedef enum { + VIR_DOMAIN_DIRTYRATE_UNSTARTED = 0, /* the dirtyrate calculation has not been started */ + VIR_DOMAIN_DIRTYRATE_MEASURING = 1, /* the dirtyrate calculation is measuring */ + VIR_DOMAIN_DIRTYRATE_MEASURED = 2, /* the dirtyrate calculation is completed */ + +# ifdef VIR_ENUM_SENTINELS + VIR_DOMAIN_DIRTYRATE_LAST +# endif +} virDomainDirtyRateStatus; + /** * virDomainDirtyRateInfo: * diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 26ac499fc5..2e920553ae 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -9393,3 +9393,51 @@ qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon, return virJSONValueGetBoolean(virJSONValueObjectGet(reply, "return"), migratable); } + + +VIR_ENUM_DECL(qemuDomainDirtyRateStatus); +VIR_ENUM_IMPL(qemuDomainDirtyRateStatus, + VIR_DOMAIN_DIRTYRATE_LAST, + "unstarted", + "measuring", + "measured"); + +static int +qemuMonitorJSONExtractDirtyRateInfo(virJSONValuePtr data, + virDomainDirtyRateInfoPtr info) +{ + const char *status; + int statusID; + + if (!(status = virJSONValueObjectGetString(data, "status"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-dirty-rate reply was missing 'status' data")); + return -1; + } + + if ((statusID = qemuDomainDirtyRateStatusTypeFromString(status)) < 0) { + return -1; + } + info->status = statusID; + + if ((info->status == VIR_DOMAIN_DIRTYRATE_MEASURED) && + (virJSONValueObjectGetNumberLong(data, "dirty-rate", &(info->dirtyRate)) < 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-dirty-rate reply was missing 'dirty-rate' data")); + return -1; + } + + if (virJSONValueObjectGetNumberLong(data, "start-time", &(info->startTime)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-dirty-rate reply was missing 'start-time' data")); + return -1; + } + + if (virJSONValueObjectGetNumberLong(data, "calc-time", &(info->calcTime)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-dirty-rate reply was missing 'calc-time' data")); + return -1; + } + + return 0; +} -- 2.23.0

Introduce dirty_rate async job for qemuDomainGetDirtyRateInfo. Signed-off-by: Hao Wang <wanghao232@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- include/libvirt/libvirt-domain.h | 1 + src/qemu/qemu_domainjob.c | 3 +++ src/qemu/qemu_domainjob.h | 1 + src/qemu/qemu_driver.c | 5 +++++ src/qemu/qemu_migration.c | 2 ++ src/qemu/qemu_process.c | 3 +++ tools/virsh-domain.c | 1 + 7 files changed, 16 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 9bf4f8a8cf..c7c64b9317 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3308,6 +3308,7 @@ typedef enum { VIR_DOMAIN_JOB_OPERATION_SNAPSHOT_REVERT = 7, VIR_DOMAIN_JOB_OPERATION_DUMP = 8, VIR_DOMAIN_JOB_OPERATION_BACKUP = 9, + VIR_DOMAIN_JOB_OPERATION_DIRTYRATE = 10, # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_JOB_OPERATION_LAST diff --git a/src/qemu/qemu_domainjob.c b/src/qemu/qemu_domainjob.c index 3c2c6b9179..d20cc09d49 100644 --- a/src/qemu/qemu_domainjob.c +++ b/src/qemu/qemu_domainjob.c @@ -61,6 +61,7 @@ VIR_ENUM_IMPL(qemuDomainAsyncJob, "snapshot", "start", "backup", + "dirty rate" ); const char * @@ -78,6 +79,7 @@ qemuDomainAsyncJobPhaseToString(qemuDomainAsyncJob job, case QEMU_ASYNC_JOB_START: case QEMU_ASYNC_JOB_NONE: case QEMU_ASYNC_JOB_BACKUP: + case QEMU_ASYNC_JOB_DIRTYRATE: G_GNUC_FALLTHROUGH; case QEMU_ASYNC_JOB_LAST: break; @@ -104,6 +106,7 @@ qemuDomainAsyncJobPhaseFromString(qemuDomainAsyncJob job, case QEMU_ASYNC_JOB_START: case QEMU_ASYNC_JOB_NONE: case QEMU_ASYNC_JOB_BACKUP: + case QEMU_ASYNC_JOB_DIRTYRATE: G_GNUC_FALLTHROUGH; case QEMU_ASYNC_JOB_LAST: break; diff --git a/src/qemu/qemu_domainjob.h b/src/qemu/qemu_domainjob.h index 79f0127252..4f820aaf37 100644 --- a/src/qemu/qemu_domainjob.h +++ b/src/qemu/qemu_domainjob.h @@ -74,6 +74,7 @@ typedef enum { QEMU_ASYNC_JOB_SNAPSHOT, QEMU_ASYNC_JOB_START, QEMU_ASYNC_JOB_BACKUP, + QEMU_ASYNC_JOB_DIRTYRATE, QEMU_ASYNC_JOB_LAST } qemuDomainAsyncJob; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 825bdd9119..8457cbfb4b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12856,6 +12856,11 @@ static int qemuDomainAbortJob(virDomainPtr dom) ret = 0; break; + case QEMU_ASYNC_JOB_DIRTYRATE: + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("cannot abort get dirty rate")); + break; + case QEMU_ASYNC_JOB_LAST: default: virReportEnumRangeError(qemuDomainAsyncJob, priv->job.asyncJob); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 4e959abebf..3e4ca49df0 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1643,6 +1643,8 @@ qemuMigrationJobName(virDomainObjPtr vm) return _("start job"); case QEMU_ASYNC_JOB_BACKUP: return _("backup job"); + case QEMU_ASYNC_JOB_DIRTYRATE: + return _("dirty rate"); case QEMU_ASYNC_JOB_LAST: default: return _("job"); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5bc76a75e3..74b98cd72b 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3687,6 +3687,9 @@ qemuProcessRecoverJob(virQEMUDriverPtr driver, priv->job.current->started = now; break; + case QEMU_ASYNC_JOB_DIRTYRATE: + break; + case QEMU_ASYNC_JOB_NONE: case QEMU_ASYNC_JOB_LAST: break; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 8f11393197..909864c940 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -6166,6 +6166,7 @@ VIR_ENUM_IMPL(virshDomainJobOperation, N_("Snapshot revert"), N_("Dump"), N_("Backup"), + N_("Dirty Rate"), ); static const char * -- 2.23.0

Implement qemuDomainQueryDirtyRate which query domain's memory dirty rate using qmp "query-dirty-rate". Signed-off-by: Hao Wang <wanghao232@huawei.com> Signed-off-by: Zhou Yimin <zhouyimin@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- src/qemu/qemu_migration.c | 32 ++++++++++++++++++++++++++++++++ src/qemu/qemu_migration.h | 5 +++++ src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 27 +++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ 6 files changed, 84 insertions(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 3e4ca49df0..be89c0d807 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -5838,3 +5838,35 @@ qemuMigrationSrcFetchMirrorStats(virQEMUDriverPtr driver, virHashFree(blockinfo); return 0; } + + +int +qemuDomainQueryDirtyRate(virDomainPtr dom, + virDomainObjPtr vm, + virDomainDirtyRateInfoPtr info) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + return ret; + } + + priv = vm->privateData; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_DIRTYRATE) < 0) + return ret; + + ret = qemuMonitorQueryDirtyRate(priv->mon, info); + if (ret < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("get vm's dirty rate failed.")); + } + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + return ret; +} diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index fd9eb7cab0..0cd12adb27 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -258,3 +258,8 @@ qemuMigrationSrcFetchMirrorStats(virQEMUDriverPtr driver, virDomainObjPtr vm, qemuDomainAsyncJob asyncJob, qemuDomainJobInfoPtr jobInfo); + +int +qemuDomainQueryDirtyRate(virDomainPtr dom, + virDomainObjPtr vm, + virDomainDirtyRateInfoPtr info); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 8c991fefbb..35b0db1c13 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4646,3 +4646,15 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions, return qemuMonitorJSONTransactionBackup(actions, device, jobname, target, bitmap, syncmode); } + + +int +qemuMonitorQueryDirtyRate(qemuMonitorPtr mon, + virDomainDirtyRateInfoPtr info) +{ + VIR_DEBUG("info=%p", info); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONQueryDirtyRate(mon, info); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index a744c8975b..15601b7ee8 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1443,3 +1443,7 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions, const char *target, const char *bitmap, qemuMonitorTransactionBackupSyncMode syncmode); + +int +qemuMonitorQueryDirtyRate(qemuMonitorPtr mon, + virDomainDirtyRateInfoPtr info); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 2e920553ae..1ce9f8576b 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -9441,3 +9441,30 @@ qemuMonitorJSONExtractDirtyRateInfo(virJSONValuePtr data, return 0; } + + +int +qemuMonitorJSONQueryDirtyRate(qemuMonitorPtr mon, + virDomainDirtyRateInfoPtr info) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + virJSONValuePtr data = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("query-dirty-rate", NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + if (!(data = virJSONValueObjectGetObject(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-dirty-rate reply was missing 'return' data")); + return -1; + } + + return qemuMonitorJSONExtractDirtyRateInfo(data, info); +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 098ab857be..f3f5b78fb9 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -694,3 +694,7 @@ int qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon, int qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon, bool *migratable); + +int +qemuMonitorJSONQueryDirtyRate(qemuMonitorPtr mon, + virDomainDirtyRateInfoPtr info); -- 2.23.0

Implement qemuDomainCalculateDirtyRate which calculates domain's memory dirty rate using qmp "calc-dirty-rate". Signed-off-by: Hao Wang <wanghao232@huawei.com> Signed-off-by: Zhou Yimin <zhouyimin@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- src/qemu/qemu_migration.c | 29 +++++++++++++++++++++++++++++ src/qemu/qemu_migration.h | 5 +++++ src/qemu/qemu_monitor.c | 12 ++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ src/qemu/qemu_monitor_json.c | 22 ++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++++ 6 files changed, 76 insertions(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index be89c0d807..a3c2618bd1 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -5870,3 +5870,32 @@ qemuDomainQueryDirtyRate(virDomainPtr dom, return ret; } + + +int +qemuDomainCalculateDirtyRate(virDomainPtr dom, + virDomainObjPtr vm, + long long sec) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + return ret; + } + + priv = vm->privateData; + + VIR_DEBUG("Calculate dirty rate during %lld seconds", sec); + if (qemuDomainObjEnterMonitorAsync(driver, vm, QEMU_ASYNC_JOB_DIRTYRATE) < 0) + return ret; + + ret = qemuMonitorCalculateDirtyRate(priv->mon, sec); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + return ret; +} diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 0cd12adb27..15f707fb57 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -263,3 +263,8 @@ int qemuDomainQueryDirtyRate(virDomainPtr dom, virDomainObjPtr vm, virDomainDirtyRateInfoPtr info); + +int +qemuDomainCalculateDirtyRate(virDomainPtr dom, + virDomainObjPtr vm, + long long sec); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 35b0db1c13..017dd69dbf 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4658,3 +4658,15 @@ qemuMonitorQueryDirtyRate(qemuMonitorPtr mon, return qemuMonitorJSONQueryDirtyRate(mon, info); } + + +int +qemuMonitorCalculateDirtyRate(qemuMonitorPtr mon, + long long sec) +{ + VIR_DEBUG("seconds=%lld", sec); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONCalculateDirtyRate(mon, sec); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 15601b7ee8..5555a51fb9 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1447,3 +1447,7 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions, int qemuMonitorQueryDirtyRate(qemuMonitorPtr mon, virDomainDirtyRateInfoPtr info); + +int +qemuMonitorCalculateDirtyRate(qemuMonitorPtr mon, + long long sec); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 1ce9f8576b..d68dc86b7d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -9468,3 +9468,25 @@ qemuMonitorJSONQueryDirtyRate(qemuMonitorPtr mon, return qemuMonitorJSONExtractDirtyRateInfo(data, info); } + + +int +qemuMonitorJSONCalculateDirtyRate(qemuMonitorPtr mon, + long long sec) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("calc-dirty-rate", + "I:calc-time", (long)sec, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index f3f5b78fb9..6c5fb2b091 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -698,3 +698,7 @@ qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon, int qemuMonitorJSONQueryDirtyRate(qemuMonitorPtr mon, virDomainDirtyRateInfoPtr info); + +int +qemuMonitorJSONCalculateDirtyRate(qemuMonitorPtr mon, + long long sec); -- 2.23.0

Introduce virDomainDirtyRateFlags for DomainGetDirtyRateInfo API. Signed-off-by: Hao Wang <wanghao232@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- include/libvirt/libvirt-domain.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index c7c64b9317..68a760816a 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5013,6 +5013,17 @@ int virDomainBackupBegin(virDomainPtr domain, char *virDomainBackupGetXMLDesc(virDomainPtr domain, unsigned int flags); +/** + * virDomainDirtyRateFlags: + * + * Details on the flags used by getdirtyrate api. + */ + +typedef enum { + VIR_DOMAIN_DIRTYRATE_CALC = 1 << 0, /* calculate domain's dirtyrate */ + VIR_DOMAIN_DIRTYRATE_QUERY = 1 << 1, /* query domain's dirtyrate */ +} virDomainDirtyRateFlags; + /** * virDomainDirtyRateStatus: * -- 2.23.0

Introduce DomainGetDirtyRateInfo API for domain's memory dirty rate calculation and query. Signed-off-by: Hao Wang <wanghao232@huawei.com> Signed-off-by: Zhou Yimin <zhouyimin@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- include/libvirt/libvirt-domain.h | 13 ++++++ src/driver-hypervisor.h | 7 ++++ src/libvirt-domain.c | 46 +++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/qemu/qemu_driver.c | 71 ++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 21 +++++++++- 7 files changed, 163 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 68a760816a..9e148a238b 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5064,4 +5064,17 @@ struct _virDomainDirtyRateInfo { typedef virDomainDirtyRateInfo *virDomainDirtyRateInfoPtr; +/** + * virDomainDirtyRateInfoPtr: + * + * a virDomainDirtyRateInfoPtr is a pointer to a virDomainDirtyRateInfo structure. + */ + +typedef virDomainDirtyRateInfo *virDomainDirtyRateInfoPtr; + +int virDomainGetDirtyRateInfo(virDomainPtr domain, + virDomainDirtyRateInfoPtr info, + long long sec, + int flags); + #endif /* LIBVIRT_DOMAIN_H */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index bce023017d..dc2aefa910 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1387,6 +1387,12 @@ typedef char * (*virDrvDomainBackupGetXMLDesc)(virDomainPtr domain, unsigned int flags); +typedef int +(*virDrvDomainGetDirtyRateInfo)(virDomainPtr domain, + virDomainDirtyRateInfoPtr info, + long long sec, + int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1650,4 +1656,5 @@ struct _virHypervisorDriver { virDrvDomainAgentSetResponseTimeout domainAgentSetResponseTimeout; virDrvDomainBackupBegin domainBackupBegin; virDrvDomainBackupGetXMLDesc domainBackupGetXMLDesc; + virDrvDomainGetDirtyRateInfo domainGetDirtyRateInfo; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 3c5f55176a..8714c1ca93 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -12758,3 +12758,49 @@ virDomainBackupGetXMLDesc(virDomainPtr domain, virDispatchError(conn); return NULL; } + + +/** + * virDomainGetDirtyRateInfo: + * @domain: a domain object. + * @info: return value of current domain's memory dirty rate info. + * @sec: show dirty rate within specified seconds. + * @flags: the flags of getdirtyrate action -- calculate and/or query. + * + * Get the current domain's memory dirty rate (in MB/s). + * + * Returns 0 in case of success, -1 otherwise. + */ +int +virDomainGetDirtyRateInfo(virDomainPtr domain, + virDomainDirtyRateInfoPtr info, + long long sec, + int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "info = %p, seconds=%lld", info, sec); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + conn = domain->conn; + + virCheckNonNullArgGoto(info, error); + virCheckReadOnlyGoto(conn->flags, error); + + if (info) + memset(info, 0, sizeof(*info)); + + if (conn->driver->domainGetDirtyRateInfo) { + if (conn->driver->domainGetDirtyRateInfo(domain, info, sec, flags) < 0) + goto error; + VIR_DOMAIN_DEBUG(domain, "info = %p, seconds=%lld", info, sec); + return 0; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 539d2e3943..11864f48b1 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -873,4 +873,9 @@ LIBVIRT_6.0.0 { virDomainBackupGetXMLDesc; } LIBVIRT_5.10.0; +LIBVIRT_6.9.0 { + global: + virDomainGetDirtyRateInfo; +} LIBVIRT_6.0.0; + # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8457cbfb4b..3ef8253cb5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20055,6 +20055,76 @@ qemuDomainAgentSetResponseTimeout(virDomainPtr dom, } +#define MIN_DIRTYRATE_CALCULATION_PERIOD 1 /* 1s */ +#define MAX_DIRTYRATE_CALCULATION_PERIOD 60 /* 60s */ + +static int +qemuDomainGetDirtyRateInfo(virDomainPtr dom, + virDomainDirtyRateInfoPtr info, + long long sec, + int flags) +{ + virDomainObjPtr vm = NULL; + virQEMUDriverPtr driver = dom->conn->privateData; + int ret = -1; + + if (!(vm = qemuDomainObjFromDomain(dom))) + return ret; + + if (virDomainGetDirtyRateInfoEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (qemuDomainObjBeginAsyncJob(driver, vm, QEMU_ASYNC_JOB_DIRTYRATE, + VIR_DOMAIN_JOB_OPERATION_DIRTYRATE, 0) < 0) + goto cleanup; + + qemuDomainObjSetAsyncJobMask(vm, (QEMU_JOB_DEFAULT_MASK | + JOB_MASK(QEMU_JOB_SUSPEND))); + + if (!qemuMigrationSrcIsAllowed(driver, vm, false, 0)) + goto endjob; + + if (flags & VIR_DOMAIN_DIRTYRATE_CALC) { + if (sec < MIN_DIRTYRATE_CALCULATION_PERIOD || sec > MAX_DIRTYRATE_CALCULATION_PERIOD) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "seconds=%lld is invalid, please choose value within [1, 60].", sec); + goto endjob; + } + + if (qemuDomainCalculateDirtyRate(dom, vm, sec) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("can't calculate domain's dirty rate")); + goto endjob; + } + } + + if (flags & VIR_DOMAIN_DIRTYRATE_QUERY) { + if (flags & VIR_DOMAIN_DIRTYRATE_CALC) { + struct timespec ts = { .tv_sec = sec, .tv_nsec = 50 * 1000 * 1000ull }; + + virObjectUnlock(vm); + nanosleep(&ts, NULL); + virObjectLock(vm); + } + + if (qemuDomainQueryDirtyRate(dom, vm, info) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("can't query domain's dirty rate")); + goto endjob; + } + } + + ret = 0; + + endjob: + qemuDomainObjEndAsyncJob(driver, vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -20294,6 +20364,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainAgentSetResponseTimeout = qemuDomainAgentSetResponseTimeout, /* 5.10.0 */ .domainBackupBegin = qemuDomainBackupBegin, /* 6.0.0 */ .domainBackupGetXMLDesc = qemuDomainBackupGetXMLDesc, /* 6.0.0 */ + .domainGetDirtyRateInfo = qemuDomainGetDirtyRateInfo, /* 6.9.0 */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d318224605..5ea49f1f4e 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8426,6 +8426,7 @@ static virHypervisorDriver hypervisor_driver = { .domainAgentSetResponseTimeout = remoteDomainAgentSetResponseTimeout, /* 5.10.0 */ .domainBackupBegin = remoteDomainBackupBegin, /* 6.0.0 */ .domainBackupGetXMLDesc = remoteDomainBackupGetXMLDesc, /* 6.0.0 */ + .domainGetDirtyRateInfo = remoteDomainGetDirtyRateInfo, /* 6.9.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f4d6147676..ce3d0519c9 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3771,6 +3771,19 @@ struct remote_domain_backup_get_xml_desc_ret { remote_nonnull_string xml; }; +struct remote_domain_get_dirty_rate_info_args { + remote_nonnull_domain dom; + hyper sec; + int flags; +}; + +struct remote_domain_get_dirty_rate_info_ret { /* insert@1 */ + int status; + hyper dirtyRate; + hyper startTime; + hyper calcTime; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6668,5 +6681,11 @@ enum remote_procedure { * @priority: high * @acl: domain:read */ - REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422 + REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422, + + /** + * @generate: both + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_GET_DIRTY_RATE_INFO = 423 }; -- 2.23.0

Signed-off-by: Hao Wang <wanghao232@huawei.com> Signed-off-by: Zhou Yimin <zhouyimin@huawei.com> Reviewed-by: Chuan Zheng <zhengchuan@huawei.com> --- tools/virsh-domain.c | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 909864c940..4b2ee3f8e3 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -14334,6 +14334,112 @@ cmdGuestInfo(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "querydirtyrate" command + */ +static const vshCmdInfo info_getdirtyrate[] = { + {.name = "help", + .data = N_("Get a vm's memory dirty rate") + }, + {.name = "desc", + .data = N_("Get memory dirty rate of a domain in order to decide" + " whether it's proper to be migrated out or not.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_getdirtyrate[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "seconds", + .type = VSH_OT_INT, + .help = N_("calculate memory dirty rate within specified seconds," + " a valid range of values is [1, 60], and would default to 1s.") + }, + {.name = "calculate", + .type = VSH_OT_BOOL, + .help = N_("calculate dirty rate only, can be used together with --query," + " either or both is expected, otherwise would default to both.") + }, + {.name = "query", + .type = VSH_OT_BOOL, + .help = N_("query dirty rate only, can be used together with --calculate," + " either or both is expected, otherwise would default to both.") + }, + {.name = NULL} +}; + +static bool +cmdGetDirtyRateInfo(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + virDomainDirtyRateInfo info; + long long sec = 0; + const char *status = NULL; + unsigned int flags = 0; + int rc; + bool ret = false; + bool calc = vshCommandOptBool(cmd, "calculate"); + bool query = vshCommandOptBool(cmd, "query"); + + if (calc) + flags |= VIR_DOMAIN_DIRTYRATE_CALC; + if (query) + flags |= VIR_DOMAIN_DIRTYRATE_QUERY; + + /* if flag option is missing, default to both --calculate and --query */ + if (!calc && !query) + flags |= VIR_DOMAIN_DIRTYRATE_CALC | VIR_DOMAIN_DIRTYRATE_QUERY; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + rc = vshCommandOptLongLong(ctl, cmd, "seconds", &sec); + if (rc < 0) + goto done; + + /* if --seconds option is missing, default to 1s */ + if (!rc) + sec = 1; + + if (virDomainGetDirtyRateInfo(dom, &info, sec, flags) < 0) { + vshError(ctl, "%s", _("Get memory dirty-rate failed.")); + goto done; + } + + if (flags & VIR_DOMAIN_DIRTYRATE_QUERY) { + switch (info.status) { + case VIR_DOMAIN_DIRTYRATE_UNSTARTED: + status = _("unstarted"); + break; + case VIR_DOMAIN_DIRTYRATE_MEASURING: + status = _("measuring"); + break; + case VIR_DOMAIN_DIRTYRATE_MEASURED: + status = _("measured"); + break; + default: + status = _("unknown"); + } + + vshPrint(ctl, _("status: %s\n"), status); + vshPrint(ctl, _("startTime: %lld\n"), info.startTime); + vshPrint(ctl, _("calcTime: %lld s\n"), info.calcTime); + + if (info.status == VIR_DOMAIN_DIRTYRATE_MEASURED) + vshPrint(ctl, _("dirtyRate: %lld MB/s\n"), info.dirtyRate); + else + vshPrint(ctl, _("dirtyRate: the calculation is %s, please query results later\n"), + status); + } else { + vshPrint(ctl, _("Memory dirty rate is calculating, use --query option to display results.\n")); + } + + ret = true; + done: + virDomainFree(dom); + return ret; +} + const vshCmdDef domManagementCmds[] = { {.name = "attach-device", .handler = cmdAttachDevice, @@ -14961,5 +15067,11 @@ const vshCmdDef domManagementCmds[] = { .info = info_guestinfo, .flags = 0 }, + {.name = "getdirtyrate", + .handler = cmdGetDirtyRateInfo, + .opts = opts_getdirtyrate, + .info = info_getdirtyrate, + .flags = 0 + }, {.name = NULL} }; -- 2.23.0
participants (1)
-
Hao Wang