@@ -68,6 +68,8 @@ static void virtio_balloon_pci_instance_init(Object *obj)
object_property_add_alias(obj, "guest-stats-polling-interval",
OBJECT(&dev->vdev),
"guest-stats-polling-interval");
+ object_property_add_alias(obj, "guest-working-set", OBJECT(&dev->vdev),
+ "guest-working-set");
}
static const VirtioPCIDeviceTypeInfo virtio_balloon_pci_info = {
@@ -229,7 +229,7 @@ static void virtio_balloon_receive_working_set(VirtIODevice *vdev,
}
}
-static __attribute__((unused)) void virtio_balloon_send_working_set_request(
+static void virtio_balloon_send_working_set_request(
VirtIODevice *vdev, VirtQueue *vq)
{
VirtQueueElement *elem;
@@ -248,7 +248,7 @@ static __attribute__((unused)) void virtio_balloon_send_working_set_request(
g_free(elem);
}
-static __attribute__((unused)) void virtio_balloon_send_working_set_config(
+static void virtio_balloon_send_working_set_config(
VirtIODevice *vdev, VirtQueue *vq,
uint64_t i0, uint64_t i1, uint64_t i2,
uint64_t refresh, uint64_t report)
@@ -353,6 +353,43 @@ static void balloon_stats_poll_cb(void *opaque)
s->stats_vq_elem = NULL;
}
+static void balloon_working_set_get_all(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ Error *err = NULL;
+ VirtIOBalloon *s = VIRTIO_BALLOON(obj);
+ char ws_buf[4];
+ WorkingSetInfo *wsinfo;
+ int i;
+
+ if (!visit_start_struct(v, name, NULL, 0, &err)) {
+ goto out;
+ }
+
+ if (!visit_start_struct(v, "working_set", NULL, 0, &err)) {
+ goto out_end;
+ }
+ for (i = 0; i < VIRTIO_BALLOON_WS_NR_BINS; i++) {
+ wsinfo = s->ws + i;
+ sprintf(ws_buf, "ws%d", i);
+ if (!visit_type_WorkingSetInfo(v, ws_buf, &wsinfo, &err)) {
+ goto out_nested;
+ }
+ }
+ visit_check_struct(v, &err);
+out_nested:
+ visit_end_struct(v, NULL);
+
+ if (!err) {
+ visit_check_struct(v, &err);
+ }
+out_end:
+ visit_end_struct(v, NULL);
+out:
+ error_propagate(errp, err);
+}
+
static void balloon_stats_get_all(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
@@ -917,6 +954,25 @@ static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
VIRTIO_BALLOON_PFN_SHIFT);
}
+static void virtio_balloon_working_set_request(void *opaque)
+{
+ VirtIOBalloon *dev = VIRTIO_BALLOON(opaque);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+
+ virtio_balloon_send_working_set_request(vdev, dev->notification_vq);
+}
+
+static void virtio_balloon_working_set_config(void *opaque, uint64_t i0,
+ uint64_t i1, uint64_t i2,
+ uint64_t refresh, uint64_t report)
+{
+ VirtIOBalloon *dev = VIRTIO_BALLOON(opaque);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+
+ virtio_balloon_send_working_set_config(vdev, dev->notification_vq, i0, i1,
+ i2, refresh, report);
+}
+
static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
{
VirtIOBalloon *dev = VIRTIO_BALLOON(opaque);
@@ -992,7 +1048,9 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
virtio_init(vdev, VIRTIO_ID_BALLOON, virtio_balloon_config_size(s));
ret = qemu_add_balloon_handler(virtio_balloon_to_target,
- virtio_balloon_stat, s);
+ virtio_balloon_stat,
+ virtio_balloon_working_set_request,
+ virtio_balloon_working_set_config, s);
if (ret < 0) {
error_setg(errp, "Only one balloon device is supported");
@@ -1148,6 +1206,9 @@ static void virtio_balloon_instance_init(Object *obj)
balloon_stats_get_poll_interval,
balloon_stats_set_poll_interval,
NULL, NULL);
+
+ object_property_add(obj, "guest-working-set", "guest working set",
+ balloon_working_set_get_all, NULL, NULL, NULL);
}
static const VMStateDescription vmstate_virtio_balloon = {
@@ -18,10 +18,17 @@
#include "qapi/qapi-types-machine.h"
typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target);
+typedef void (QEMUBalloonWorkingSetRequest)(void *opaque);
+typedef void (QEMUBalloonWorkingSetConfig)(void *opaque, uint64_t i0,
+ uint64_t i1, uint64_t i2, uint64_t refresh,
+ uint64_t report);
typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info);
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
- QEMUBalloonStatus *stat_func, void *opaque);
+ QEMUBalloonStatus *stat_func,
+ QEMUBalloonWorkingSetRequest *ws_func,
+ QEMUBalloonWorkingSetConfig *config_func,
+ void *opaque);
void qemu_remove_balloon_handler(void *opaque);
#endif
@@ -333,6 +333,7 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = {
[QAPI_EVENT_QUORUM_FAILURE] = { 1000 * SCALE_MS },
[QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS },
[QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE] = { 1000 * SCALE_MS },
+ [QAPI_EVENT_WORKING_SET_EVENT] = { 1000 * SCALE_MS },
};
/*
@@ -1055,6 +1055,57 @@
##
{ 'command': 'balloon', 'data': {'value': 'int'} }
+##
+# @working-set-config:
+#
+# Specify the config parameters for Working Set reporting.
+#
+# @i0: the endpoint of the first interval (in ms)
+#
+# @i1: the endpoint of the second interval (in ms)
+#
+# @i2: the endpoint of the third interval (in ms)
+#
+# @refresh: the refresh threshold (in ms) for Working Set reporting
+#
+# @report: the report threshold (in ms) for Working Set reporting
+#
+# Returns: - Nothing on success
+# - If no balloon device is present, DeviceNotActive
+#
+# Example:
+#
+# -> { "execute": "working-set-config",
+# "arguments": { "i0": 100,
+# "i1": 500,
+# "i2": 2000,
+# "refresh": 750,
+# "report": 1000 } }
+# <- { "return": {} }
+#
+##
+{ 'command': 'working-set-config', 'data': {'i0': 'uint64',
+ 'i1': 'uint64',
+ 'i2': 'uint64',
+ 'refresh': 'uint64',
+ 'report': 'uint64'} }
+##
+# @working-set-request:
+#
+# Request the Working Set report from the guest.
+#
+# Returns: - Nothing on success
+# - If no balloon device is present, DeviceNotActive
+#
+# Example:
+#
+# -> { "execute": "working-set-request", "arguments": {} }
+# <- { "return": {} }
+#
+##
+{ 'command': 'working-set-request', 'data': {} }
+
+
##
# @BalloonInfo:
#
@@ -1113,6 +1164,21 @@
{ 'event': 'BALLOON_CHANGE',
'data': { 'actual': 'int' } }
+##
+# @WORKING_SET_EVENT:
+#
+# Emitted when the guest sends a new Working Set report.
+#
+# Note: this event is rate-limited.
+#
+# Example:
+#
+# <- { "event": "WORKING_SET_EVENT",
+# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+#
+##
+{ 'event': 'WORKING_SET_EVENT' }
+
##
# @MemoryInfo:
#
@@ -35,6 +35,8 @@
static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn;
+static QEMUBalloonWorkingSetRequest *balloon_ws_request_fn;
+static QEMUBalloonWorkingSetConfig *balloon_ws_config_fn;
static void *balloon_opaque;
static bool have_balloon(Error **errp)
@@ -53,9 +55,13 @@ static bool have_balloon(Error **errp)
}
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
- QEMUBalloonStatus *stat_func, void *opaque)
+ QEMUBalloonStatus *stat_func,
+ QEMUBalloonWorkingSetRequest *ws_request_func,
+ QEMUBalloonWorkingSetConfig *ws_config_func,
+ void *opaque)
{
- if (balloon_event_fn || balloon_stat_fn || balloon_opaque) {
+ if (balloon_event_fn || balloon_stat_fn || balloon_ws_request_fn \
+ || balloon_ws_config_fn || balloon_opaque) {
/* We're already registered one balloon handler. How many can
* a guest really have?
*/
@@ -63,6 +69,8 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
}
balloon_event_fn = event_func;
balloon_stat_fn = stat_func;
+ balloon_ws_request_fn = ws_request_func;
+ balloon_ws_config_fn = ws_config_func;
balloon_opaque = opaque;
return 0;
}
@@ -104,3 +112,22 @@ void qmp_balloon(int64_t target, Error **errp)
trace_balloon_event(balloon_opaque, target);
balloon_event_fn(balloon_opaque, target);
}
+
+void qmp_working_set_request(Error **errp)
+{
+ if (!have_balloon(errp)) {
+ return;
+ }
+
+ balloon_ws_request_fn(balloon_opaque);
+}
+
+void qmp_working_set_config(uint64_t i0, uint64_t i1, uint64_t i2,
+ uint64_t refresh, uint64_t report, Error **errp)
+{
+ if (!have_balloon(errp)) {
+ return;
+ }
+
+ balloon_ws_config_fn(balloon_opaque, i0, i1, i2, refresh, report);
+}