[PATCH v7 0/7] migration/dirtyrate: Introduce APIs for getting domain memory dirty rate

V6 -> V7: add virDomainDirtyRateStatus enum define calc_status as int split 5/5 in v6 into two parts: 6/7 in v7: extend dirtyrate statistics for domGetStats 7/7 in v7: extend corresponding virsh command V5 -> V6: split DomainGetDirtyRateInfo(domdirtyrate) API into two parts: 1. DomainStartDirtyRateCalc(domdirtyrate-calc) for starting dirty rate calculation; 2. qemuDomainGetStatsDirtyRate(domstats --dirtyrate) for querying dirty rate infomation. V4 -> V5: squash 1/7 and bits of 5/7 and 6/7 into 2/7 in v4 (to be 1/5 in v5) squash left of 5/7 into 4/7 in v4 (to be 3/5 in v5) add VIR_DOMAIN_DIRTYRATE_DEFAULT flag remove redundant error report rename virsh api to "domdirtyrate" use vshTablePtr for virsh api output add description in docs/manpages/virsh.rst other format optimize V3 -> V4: define flags to unsigned int fix some compile warnings V2 -> V3: reorganize patchset to fix compile warning V1 -> V2: replace QEMU_JOB_ASYNC with QEMU_JOB_QUERY 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: 1. Calculating Introduce a new API DomainStartDirtyRateCalc and corresponding virsh api (domdirtyrate-calc) for starting dirty rate calculation by calling qmp 'calc-dirty-rate'. # virsh domdirtyrate-calc <domain> [--seconds <sec>] 2. Querying Introduce command 'virsh domstats --dirtyrate' for reporting memory dirty rate infomation by calling qmp 'query-dirty-rate'. The info is listed as: Domain: 'vm0' dirtyrate.calc_status=2 dirtyrate.calc_start_time=1534523 dirtyrate.calc_period=1 dirtyrate.megabytes_per_second=5 Hao Wang (7): migration/dirtyrate: Introduce virDomainStartDirtyRateCalc API migration/dirtyrate: Implement qemuDomainStartDirtyRateCalc migration/dirtyrate: Introduce domdirtyrate-calc virsh api migration/dirtyrate: Introduce virDomainDirtyRateStatus enum migration/dirtyrate: Implement qemuMonitorQueryDirtyRate migration/dirtyrate: Extend dirtyrate statistics for domGetStats migration/dirtyrate: Introduce command 'virsh domstats --dirtyrate' docs/manpages/virsh.rst | 33 +++++++++- include/libvirt/libvirt-domain.h | 23 +++++++ src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 59 +++++++++++++++++ src/libvirt_public.syms | 5 ++ src/qemu/qemu_driver.c | 110 +++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 24 +++++++ src/qemu/qemu_monitor.h | 19 ++++++ src/qemu/qemu_monitor_json.c | 101 ++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 14 +++- src/remote_protocol-structs | 6 ++ tools/virsh-domain-monitor.c | 7 ++ tools/virsh-domain.c | 58 ++++++++++++++++ 15 files changed, 471 insertions(+), 3 deletions(-) -- 2.23.0

Introduce virDomainStartDirtyRateCalc API for start calculation of a domain's memory dirty rate with a specified time. Signed-off-by: Hao Wang <wanghao232@huawei.com> --- include/libvirt/libvirt-domain.h | 4 +++ src/driver-hypervisor.h | 6 +++++ src/libvirt-domain.c | 44 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 ++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 14 +++++++++- src/remote_protocol-structs | 6 +++++ 7 files changed, 79 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 8011cf9fe1..7aa5ef4543 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5128,4 +5128,8 @@ int virDomainGetMessages(virDomainPtr domain, char ***msgs, unsigned int flags); +int virDomainStartDirtyRateCalc(virDomainPtr domain, + int seconds, + unsigned int flags); + #endif /* LIBVIRT_DOMAIN_H */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 05d7dfb5c7..2ec7b8b24a 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1405,6 +1405,11 @@ typedef int char ***msgs, unsigned int flags); +typedef int +(*virDrvDomainStartDirtyRateCalc)(virDomainPtr domain, + int seconds, + unsigned int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1671,4 +1676,5 @@ struct _virHypervisorDriver { virDrvDomainAuthorizedSSHKeysGet domainAuthorizedSSHKeysGet; virDrvDomainAuthorizedSSHKeysSet domainAuthorizedSSHKeysSet; virDrvDomainGetMessages domainGetMessages; + virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index e54d11e513..91001fc4ed 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -13158,3 +13158,47 @@ virDomainGetMessages(virDomainPtr domain, virDispatchError(conn); return -1; } + + +/** + * virDomainStartDirtyRateCalc: + * @domain: a domain object + * @seconds: specified calculating time in seconds + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Calculate the current domain's memory dirty rate in next @seconds. + * The calculated dirty rate information is available by calling + * virConnectGetAllDomainStats. + * + * Returns 0 in case of success, -1 otherwise. + */ +int +virDomainStartDirtyRateCalc(virDomainPtr domain, + int seconds, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "seconds=%d, flags=0x%x", seconds, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + conn = domain->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + if (conn->driver->domainStartDirtyRateCalc) { + int ret; + ret = conn->driver->domainStartDirtyRateCalc(domain, seconds, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index d851333eb0..51a3d7265a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -884,4 +884,9 @@ LIBVIRT_7.1.0 { virDomainGetMessages; } LIBVIRT_6.10.0; +LIBVIRT_7.2.0 { + global: + virDomainStartDirtyRateCalc; +} LIBVIRT_7.1.0; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 31868269b1..494f4b6dc5 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8571,6 +8571,7 @@ static virHypervisorDriver hypervisor_driver = { .domainAuthorizedSSHKeysGet = remoteDomainAuthorizedSSHKeysGet, /* 6.10.0 */ .domainAuthorizedSSHKeysSet = remoteDomainAuthorizedSSHKeysSet, /* 6.10.0 */ .domainGetMessages = remoteDomainGetMessages, /* 7.1.0 */ + .domainStartDirtyRateCalc = remoteDomainStartDirtyRateCalc, /* 7.2.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index d3724bc305..7fdc65f029 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3811,6 +3811,12 @@ struct remote_domain_get_messages_ret { remote_nonnull_string msgs<REMOTE_DOMAIN_MESSAGES_MAX>; }; +struct remote_domain_start_dirty_rate_calc_args { + remote_nonnull_domain dom; + int seconds; + unsigned int flags; +}; + /*----- Protocol. -----*/ @@ -6733,5 +6739,11 @@ enum remote_procedure { * @generate: none * @acl: domain:read */ - REMOTE_PROC_DOMAIN_GET_MESSAGES = 426 + REMOTE_PROC_DOMAIN_GET_MESSAGES = 426, + + /** + * @generate: both + * @acl: domain:read + */ + REMOTE_PROC_DOMAIN_START_DIRTY_RATE_CALC = 427 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index c0c034ac6a..d13dc81a82 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -3172,6 +3172,11 @@ struct remote_domain_get_messages_ret { remote_nonnull_string * msgs_val; } msgs; }; +struct remote_domain_start_dirty_rate_calc_args { + remote_nonnull_domain dom; + int seconds; + u_int flags; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3599,4 +3604,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_AUTHORIZED_SSH_KEYS_GET = 424, REMOTE_PROC_DOMAIN_AUTHORIZED_SSH_KEYS_SET = 425, REMOTE_PROC_DOMAIN_GET_MESSAGES = 426, + REMOTE_PROC_DOMAIN_START_DIRTY_RATE_CALC = 427, }; -- 2.23.0

Implement qemuDomainStartDirtyRateCalc which calculates domain's memory dirty rate calling qmp "calc-dirty-rate". Signed-off-by: Hao Wang <wanghao232@huawei.com> --- src/qemu/qemu_driver.c | 59 ++++++++++++++++++++++++++++++++++++ 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 +++ 5 files changed, 101 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 16c5ccae45..8836d9fd16 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20342,6 +20342,64 @@ qemuDomainGetMessages(virDomainPtr dom, } +#define MIN_DIRTYRATE_CALC_PERIOD 1 /* supported min dirtyrate calculating time: 1s */ +#define MAX_DIRTYRATE_CALC_PERIOD 60 /* supported max dirtyrate calculating time: 60s */ + +static int +qemuDomainStartDirtyRateCalc(virDomainPtr dom, + int seconds, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + qemuDomainObjPrivatePtr priv; + int ret = -1; + + virCheckFlags(0, -1); + + if (seconds < MIN_DIRTYRATE_CALC_PERIOD || + seconds > MAX_DIRTYRATE_CALC_PERIOD) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("seconds=%d is invalid, please choose value within [%d, %d]."), + seconds, + MIN_DIRTYRATE_CALC_PERIOD, + MAX_DIRTYRATE_CALC_PERIOD); + return -1; + } + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainStartDirtyRateCalcEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + VIR_DEBUG("Calculate dirty rate in next %d seconds", seconds); + + priv = vm->privateData; + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorStartDirtyRateCalc(priv->mon, seconds); + + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -20584,6 +20642,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainAuthorizedSSHKeysGet = qemuDomainAuthorizedSSHKeysGet, /* 6.10.0 */ .domainAuthorizedSSHKeysSet = qemuDomainAuthorizedSSHKeysSet, /* 6.10.0 */ .domainGetMessages = qemuDomainGetMessages, /* 7.1.0 */ + .domainStartDirtyRateCalc = qemuDomainStartDirtyRateCalc, /* 7.2.0 */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b4f2641504..ce927687e3 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4742,3 +4742,15 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions, return qemuMonitorJSONTransactionBackup(actions, device, jobname, target, bitmap, syncmode); } + + +int +qemuMonitorStartDirtyRateCalc(qemuMonitorPtr mon, + int seconds) +{ + VIR_DEBUG("seconds=%d", seconds); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONStartDirtyRateCalc(mon, seconds); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index d25c26343a..94813906b4 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1526,3 +1526,7 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions, const char *target, const char *bitmap, qemuMonitorTransactionBackupSyncMode syncmode); + +int +qemuMonitorStartDirtyRateCalc(qemuMonitorPtr mon, + int seconds); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index ad62e24cfc..588fcd3a1c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -9480,3 +9480,25 @@ qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon, return virJSONValueGetBoolean(virJSONValueObjectGet(reply, "return"), migratable); } + + +int +qemuMonitorJSONStartDirtyRateCalc(qemuMonitorPtr mon, + int seconds) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("calc-dirty-rate", + "i:calc-time", seconds, + 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 3dd1eb24c7..32782cf681 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -711,3 +711,7 @@ int qemuMonitorJSONSetDBusVMStateIdList(qemuMonitorPtr mon, int qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon, bool *migratable); + +int +qemuMonitorJSONStartDirtyRateCalc(qemuMonitorPtr mon, + int seconds); -- 2.23.0

Introduce domdirtyrate-calc virsh api to start calculating domain's memory dirty rate: # virsh domdirtyrate-calc <domain> [--seconds <sec>] Signed-off-by: Hao Wang <wanghao232@huawei.com> --- docs/manpages/virsh.rst | 17 ++++++++++++ tools/virsh-domain.c | 58 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 9ff31a0160..367b4bddeb 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -1704,6 +1704,23 @@ states other than "ok" or "error" the command also prints number of seconds elapsed since the control interface entered its current state. +domdirtyrate-calc +----------------- + +**Syntax:** + +:: + + domdirtyrate-calc <domain> [--seconds <sec>] + +Calculate an active domain's memory dirty rate which may be expected by +user in order to decide whether it's proper to be migrated out or not. +The ``seconds`` parameter can be used to calculate dirty rate in a +specific time which allows 60s at most now and would be default to 1s +if missing. The calculated dirty rate information is available by calling +'domstats --dirtyrate'. + + domdisplay ---------- diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f8f36ce361..7db88f700a 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -14402,6 +14402,58 @@ cmdSetUserSSHKeys(vshControl *ctl, const vshCmd *cmd) } +/* + * "domdirtyrate" command + */ +static const vshCmdInfo info_domdirtyrate_calc[] = { + {.name = "help", + .data = N_("Calculate a vm's memory dirty rate") + }, + {.name = "desc", + .data = N_("Calculate memory dirty rate of a domain in order to " + "decide whether it's proper to be migrated out or not.\n" + "The calculated dirty rate information is available by " + "calling 'domstats --dirtyrate'.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_domdirtyrate_calc[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "seconds", + .type = VSH_OT_INT, + .help = N_("calculate memory dirty rate within specified seconds, " + "the supported value range from 1 to 60, default to 1.") + }, + {.name = NULL} +}; + +static bool +cmdDomDirtyRateCalc(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + int seconds = 1; /* the default value is 1 */ + bool ret = false; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptInt(ctl, cmd, "seconds", &seconds) < 0) + goto cleanup; + + if (virDomainStartDirtyRateCalc(dom, seconds, 0) < 0) + goto cleanup; + + vshPrintExtra(ctl, _("Start to calculate domain's memory " + "dirty rate successfully.\n")); + ret = true; + + cleanup: + virshDomainFree(dom); + return ret; +} + + const vshCmdDef domManagementCmds[] = { {.name = "attach-device", .handler = cmdAttachDevice, @@ -15041,5 +15093,11 @@ const vshCmdDef domManagementCmds[] = { .info = info_guestinfo, .flags = 0 }, + {.name = "domdirtyrate-calc", + .handler = cmdDomDirtyRateCalc, + .opts = opts_domdirtyrate_calc, + .info = info_domdirtyrate_calc, + .flags = 0 + }, {.name = NULL} }; -- 2.23.0

Introduce virDomainDirtyRateStatus enum. Signed-off-by: Hao Wang <wanghao232@huawei.com> --- include/libvirt/libvirt-domain.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 7aa5ef4543..b05bb94131 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -5128,6 +5128,24 @@ int virDomainGetMessages(virDomainPtr domain, char ***msgs, unsigned int flags); +/** + * virDomainDirtyRateStatus: + * + * Details on the cause of a dirty rate 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; + int virDomainStartDirtyRateCalc(virDomainPtr domain, int seconds, unsigned int flags); -- 2.23.0

Implement qemuMonitorQueryDirtyRate which query domain's memory dirty rate calling qmp "query-dirty-rate". Signed-off-by: Hao Wang <wanghao232@huawei.com> --- src/qemu/qemu_monitor.c | 12 ++++++ src/qemu/qemu_monitor.h | 15 +++++++ src/qemu/qemu_monitor_json.c | 79 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 4 ++ 4 files changed, 110 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index ce927687e3..9f1f645b0b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4754,3 +4754,15 @@ qemuMonitorStartDirtyRateCalc(qemuMonitorPtr mon, return qemuMonitorJSONStartDirtyRateCalc(mon, seconds); } + + +int +qemuMonitorQueryDirtyRate(qemuMonitorPtr mon, + qemuMonitorDirtyRateInfoPtr 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 94813906b4..c812aa9cc3 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1530,3 +1530,18 @@ qemuMonitorTransactionBackup(virJSONValuePtr actions, int qemuMonitorStartDirtyRateCalc(qemuMonitorPtr mon, int seconds); + +typedef struct _qemuMonitorDirtyRateInfo qemuMonitorDirtyRateInfo; +typedef qemuMonitorDirtyRateInfo *qemuMonitorDirtyRateInfoPtr; + +struct _qemuMonitorDirtyRateInfo { + int status; /* the status of last dirtyrate calculation, + one of virDomainDirtyRateStatus */ + int calcTime; /* the period of dirtyrate calculation */ + long long startTime; /* the start time of dirtyrate calculation */ + long long dirtyRate; /* the dirtyrate in MiB/s */ +}; + +int +qemuMonitorQueryDirtyRate(qemuMonitorPtr mon, + qemuMonitorDirtyRateInfoPtr info); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 588fcd3a1c..b669630bc8 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -9502,3 +9502,82 @@ qemuMonitorJSONStartDirtyRateCalc(qemuMonitorPtr mon, return 0; } + +VIR_ENUM_DECL(qemuMonitorDirtyRateStatus); +VIR_ENUM_IMPL(qemuMonitorDirtyRateStatus, + VIR_DOMAIN_DIRTYRATE_LAST, + "unstarted", + "measuring", + "measured"); + +static int +qemuMonitorJSONExtractDirtyRateInfo(virJSONValuePtr data, + qemuMonitorDirtyRateInfoPtr info) +{ + const char *statusstr; + int status; + + if (!(statusstr = virJSONValueObjectGetString(data, "status"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-dirty-rate reply was missing 'status' data")); + return -1; + } + + if ((status = qemuMonitorDirtyRateStatusTypeFromString(statusstr)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown dirty rate status: %s"), statusstr); + return -1; + } + info->status = status; + + /* `query-dirty-rate` replies `dirty-rate` data only if the status of the latest + * calculation is `measured`. + */ + 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 (virJSONValueObjectGetNumberInt(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; +} + + +int +qemuMonitorJSONQueryDirtyRate(qemuMonitorPtr mon, + qemuMonitorDirtyRateInfoPtr 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 32782cf681..8d4232f67b 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -715,3 +715,7 @@ qemuMonitorJSONGetCPUMigratable(qemuMonitorPtr mon, int qemuMonitorJSONStartDirtyRateCalc(qemuMonitorPtr mon, int seconds); + +int +qemuMonitorJSONQueryDirtyRate(qemuMonitorPtr mon, + qemuMonitorDirtyRateInfoPtr info); -- 2.23.0

Extend dirtyrate statistics for domGetStats to display the information of a domain's memory dirty rate produced by domainStartDirtyRateCalc. Signed-off-by: Hao Wang <wanghao232@huawei.com> --- include/libvirt/libvirt-domain.h | 1 + src/libvirt-domain.c | 15 ++++++++++ src/qemu/qemu_driver.c | 51 ++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index b05bb94131..03c119fe26 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -2185,6 +2185,7 @@ typedef enum { VIR_DOMAIN_STATS_PERF = (1 << 6), /* return domain perf event info */ VIR_DOMAIN_STATS_IOTHREAD = (1 << 7), /* return iothread poll info */ VIR_DOMAIN_STATS_MEMORY = (1 << 8), /* return domain memory info */ + VIR_DOMAIN_STATS_DIRTYRATE = (1 << 9), /* return domain dirty rate info */ } virDomainStatsTypes; typedef enum { diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 91001fc4ed..83ff7df9fe 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11887,6 +11887,21 @@ virConnectGetDomainCapabilities(virConnectPtr conn, * bytes consumed by @vcpus that passing through all * memory controllers, either local or remote controller. * + * VIR_DOMAIN_STATS_DIRTYRATE: + * Return memory dirty rate information. The typed parameter keys are in + * this format: + * + * "dirtyrate.calc_status" - the status of last memory dirty rate calculation, + * returned as int from virDomainDirtyRateStatus + * enum. + * "dirtyrate.calc_start_time" - the start time of last memory dirty rate + * calculation as long long. + * "dirtyrate.calc_period" - the period of last memory dirty rate calculation + * as int. + * "dirtyrate.megabytes_per_second" - the calculated memory dirty rate in + * MiB/s as long long. It is produced + * only if the calc_status is measured. + * * Note that entire stats groups or individual stat fields may be missing from * the output in case they are not supported by the given hypervisor, are not * applicable for the current state of the guest domain, or their retrieval diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8836d9fd16..f3f8caab44 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -18577,6 +18577,56 @@ qemuDomainGetStatsPerf(virQEMUDriverPtr driver G_GNUC_UNUSED, return 0; } +static int +qemuDomainGetStatsDirtyRateMon(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuMonitorDirtyRateInfoPtr info) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret; + + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorQueryDirtyRate(priv->mon, info); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + return ret; +} + +static int +qemuDomainGetStatsDirtyRate(virQEMUDriverPtr driver, + virDomainObjPtr dom, + virTypedParamListPtr params, + unsigned int privflags) +{ + qemuMonitorDirtyRateInfo info; + + if (!HAVE_JOB(privflags) || !virDomainObjIsActive(dom)) + return 0; + + if (qemuDomainGetStatsDirtyRateMon(driver, dom, &info) < 0) + return -1; + + if (virTypedParamListAddInt(params, info.status, + "dirtyrate.calc_status") < 0) + return -1; + + if (virTypedParamListAddLLong(params, info.startTime, + "dirtyrate.calc_start_time") < 0) + return -1; + + if (virTypedParamListAddInt(params, info.calcTime, + "dirtyrate.calc_period") < 0) + return -1; + + if ((info.status == VIR_DOMAIN_DIRTYRATE_MEASURED) && + virTypedParamListAddLLong(params, info.dirtyRate, + "dirtyrate.megabytes_per_second") < 0) + return -1; + + return 0; +} + typedef int (*qemuDomainGetStatsFunc)(virQEMUDriverPtr driver, virDomainObjPtr dom, @@ -18599,6 +18649,7 @@ static struct qemuDomainGetStatsWorker qemuDomainGetStatsWorkers[] = { { qemuDomainGetStatsPerf, VIR_DOMAIN_STATS_PERF, false }, { qemuDomainGetStatsIOThread, VIR_DOMAIN_STATS_IOTHREAD, true }, { qemuDomainGetStatsMemory, VIR_DOMAIN_STATS_MEMORY, false }, + { qemuDomainGetStatsDirtyRate, VIR_DOMAIN_STATS_DIRTYRATE, true }, { NULL, 0, false } }; -- 2.23.0

Introduce command 'virsh domstats --dirtyrate' for reporting memory dirty rate information. The info is listed as: Domain: 'vm0' dirtyrate.calc_status=2 dirtyrate.calc_start_time=1534523 dirtyrate.calc_period=1 dirtyrate.megabytes_per_second=5 Signed-off-by: Hao Wang <wanghao232@huawei.com> --- docs/manpages/virsh.rst | 16 ++++++++++++++-- tools/virsh-domain-monitor.c | 7 +++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 367b4bddeb..813fb0bd60 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -2219,7 +2219,7 @@ domstats domstats [--raw] [--enforce] [--backing] [--nowait] [--state] [--cpu-total] [--balloon] [--vcpu] [--interface] - [--block] [--perf] [--iothread] [--memory] + [--block] [--perf] [--iothread] [--memory] [--dirtyrate] [[--list-active] [--list-inactive] [--list-persistent] [--list-transient] [--list-running]y [--list-paused] [--list-shutoff] [--list-other]] | [domain ...] @@ -2238,7 +2238,8 @@ behavior use the *--raw* flag. The individual statistics groups are selectable via specific flags. By default all supported statistics groups are returned. Supported statistics groups flags are: *--state*, *--cpu-total*, *--balloon*, -*--vcpu*, *--interface*, *--block*, *--perf*, *--iothread*, *--memory*. +*--vcpu*, *--interface*, *--block*, *--perf*, *--iothread*, *--memory*, +*--dirtyrate*. Note that - depending on the hypervisor type and version or the domain state - not all of the following statistics may be returned. @@ -2435,6 +2436,17 @@ not available for statistical purposes. bytes consumed by @vcpus that passing through all memory controllers, either local or remote controller. +*--dirtyrate* returns: + +* ``dirtyrate.calc_status`` - the status of last memory dirty rate + calculation, returned as number from virDomainDirtyRateStatus enum. +* ``dirtyrate.calc_start_time`` - the start time of last memory dirty + rate calculation. +* ``dirtyrate.calc_period`` - the period of last memory dirty rate + calculation. +* ``dirtyrate.megabytes_per_second`` - the calculated memory dirty + rate in MiB/s. + Selecting a specific statistics groups doesn't guarantee that the daemon supports the selected group of stats. Flag *--enforce* diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 897339b6f9..c4d7464695 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -2148,6 +2148,10 @@ static const vshCmdOptDef opts_domstats[] = { .type = VSH_OT_BOOL, .help = N_("report domain memory usage"), }, + {.name = "dirtyrate", + .type = VSH_OT_BOOL, + .help = N_("report domain dirty rate information"), + }, {.name = "list-active", .type = VSH_OT_BOOL, .help = N_("list only active domains"), @@ -2267,6 +2271,9 @@ cmdDomstats(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool(cmd, "memory")) stats |= VIR_DOMAIN_STATS_MEMORY; + if (vshCommandOptBool(cmd, "dirtyrate")) + stats |= VIR_DOMAIN_STATS_DIRTYRATE; + if (vshCommandOptBool(cmd, "list-active")) flags |= VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE; -- 2.23.0

On 3/16/21 1:32 PM, Hao Wang wrote:
V6 -> V7: add virDomainDirtyRateStatus enum define calc_status as int split 5/5 in v6 into two parts: 6/7 in v7: extend dirtyrate statistics for domGetStats 7/7 in v7: extend corresponding virsh command
V5 -> V6: split DomainGetDirtyRateInfo(domdirtyrate) API into two parts: 1. DomainStartDirtyRateCalc(domdirtyrate-calc) for starting dirty rate calculation; 2. qemuDomainGetStatsDirtyRate(domstats --dirtyrate) for querying dirty rate infomation.
V4 -> V5: squash 1/7 and bits of 5/7 and 6/7 into 2/7 in v4 (to be 1/5 in v5) squash left of 5/7 into 4/7 in v4 (to be 3/5 in v5) add VIR_DOMAIN_DIRTYRATE_DEFAULT flag remove redundant error report rename virsh api to "domdirtyrate" use vshTablePtr for virsh api output add description in docs/manpages/virsh.rst other format optimize
V3 -> V4: define flags to unsigned int fix some compile warnings
V2 -> V3: reorganize patchset to fix compile warning
V1 -> V2: replace QEMU_JOB_ASYNC with QEMU_JOB_QUERY
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:
1. Calculating Introduce a new API DomainStartDirtyRateCalc and corresponding virsh api (domdirtyrate-calc) for starting dirty rate calculation by calling qmp 'calc-dirty-rate'.
# virsh domdirtyrate-calc <domain> [--seconds <sec>]
2. Querying Introduce command 'virsh domstats --dirtyrate' for reporting memory dirty rate infomation by calling qmp 'query-dirty-rate'.
The info is listed as: Domain: 'vm0' dirtyrate.calc_status=2 dirtyrate.calc_start_time=1534523 dirtyrate.calc_period=1 dirtyrate.megabytes_per_second=5
Hao Wang (7): migration/dirtyrate: Introduce virDomainStartDirtyRateCalc API migration/dirtyrate: Implement qemuDomainStartDirtyRateCalc migration/dirtyrate: Introduce domdirtyrate-calc virsh api migration/dirtyrate: Introduce virDomainDirtyRateStatus enum migration/dirtyrate: Implement qemuMonitorQueryDirtyRate migration/dirtyrate: Extend dirtyrate statistics for domGetStats migration/dirtyrate: Introduce command 'virsh domstats --dirtyrate'
docs/manpages/virsh.rst | 33 +++++++++- include/libvirt/libvirt-domain.h | 23 +++++++ src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 59 +++++++++++++++++ src/libvirt_public.syms | 5 ++ src/qemu/qemu_driver.c | 110 +++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 24 +++++++ src/qemu/qemu_monitor.h | 19 ++++++ src/qemu/qemu_monitor_json.c | 101 ++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 14 +++- src/remote_protocol-structs | 6 ++ tools/virsh-domain-monitor.c | 7 ++ tools/virsh-domain.c | 58 ++++++++++++++++ 15 files changed, 471 insertions(+), 3 deletions(-)
Looks good to me. Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Will push shortly. Can you please post a follow up patch that adds NEWS.rst entry? I think this deserves to be mention in release notes. Michal

Sure. I'll post NEWS.rst information soon, and really appreciate your help! Best Regards, Hao On 2021/3/18 15:49, Michal Privoznik wrote:
On 3/16/21 1:32 PM, Hao Wang wrote:
Hao Wang (7): migration/dirtyrate: Introduce virDomainStartDirtyRateCalc API migration/dirtyrate: Implement qemuDomainStartDirtyRateCalc migration/dirtyrate: Introduce domdirtyrate-calc virsh api migration/dirtyrate: Introduce virDomainDirtyRateStatus enum migration/dirtyrate: Implement qemuMonitorQueryDirtyRate migration/dirtyrate: Extend dirtyrate statistics for domGetStats migration/dirtyrate: Introduce command 'virsh domstats --dirtyrate'
docs/manpages/virsh.rst | 33 +++++++++- include/libvirt/libvirt-domain.h | 23 +++++++ src/driver-hypervisor.h | 6 ++ src/libvirt-domain.c | 59 +++++++++++++++++ src/libvirt_public.syms | 5 ++ src/qemu/qemu_driver.c | 110 +++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 24 +++++++ src/qemu/qemu_monitor.h | 19 +++++ src/qemu/qemu_monitor_json.c | 101 ++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 8 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 14 +++- src/remote_protocol-structs | 6 ++ tools/virsh-domain-monitor.c | 7 ++ tools/virsh-domain.c | 58 ++++++++++++++++ 15 files changed, 471 insertions(+), 3 deletions(-)
Looks good to me.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Will push shortly. Can you please post a follow up patch that adds NEWS.rst entry? I think this deserves to be mention in release notes.
Michal
.
participants (2)
-
Hao Wang
-
Michal Privoznik