Message ID | 1262711318.10698.104.camel@aglitke |
---|---|
State | New |
Headers | show |
On 01/05/2010 11:08 AM, Adam Litke wrote: > This patch has been discussed (and ACKed) in the past, but has not yet > been committed due to the congestion surrounding the 0.12 release and > other conflicting changes. I would like to rekindle the discussion so > that I can make the necessary changes to get this merged. > > This patch is ported to 0.12.1 and has one issue that I am aware of: > > It still calls monitor_suspend() and monitor_resume() which I understand > is no longer allowed. Does anyone have any ideas on how to enable async > monitor commands? > > +static void request_stats(VirtIOBalloon *vb) > +{ > + vb->stats_requested = 1; > + reset_stats(vb); > + monitor_suspend(cur_mon); > This bit is not going to work reliably anymore. Really, it never worked reliably but this is exacerbated with -qmp since a QMP session will never be the cur_mon. We have a couple of options: 1) Introduce a query-balloon command that returns immediately, and triggers a request for the guest to update the balloon stats. When the guest does update the balloon stats, trigger an asynchronous message. Asynchronous messages are ignored by the human monitor so they would never be displayed there which is unfortunate. 2) Make info balloon show the last valid stats and have it request new stats. Stats will always be delayed but it avoids async messages. 3) Make qemu request balloon stats regularly (maybe every 10 seconds) and display the latest stats with info balloon. This avoids the problem in #2 but it means that qemu determines the poll rate instead of a management tool. 4) Make info-balloon a proper asynchronous command. We need new infrastructure to allow a qmp handler to take a callback that can be used to delay the completion of the command. This addresses all of the above problems but it introduces a new one. Command completion now depends on the guest. This potentially could trip up a naive management tool that doesn't realize that the info-balloon command may never complete. I'm on the fence between 3 and 4 myself. Regards, Anthony Liguori
On 01/07/2010 05:12 PM, Anthony Liguori wrote: > > 3) Make qemu request balloon stats regularly (maybe every 10 seconds) > and display the latest stats with info balloon. This avoids the > problem in #2 but it means that qemu determines the poll rate instead > of a management tool. > > 4) Make info-balloon a proper asynchronous command. We need new > infrastructure to allow a qmp handler to take a callback that can be > used to delay the completion of the command. This addresses all of > the above problems but it introduces a new one. Command completion > now depends on the guest. This potentially could trip up a naive > management tool that doesn't realize that the info-balloon command may > never complete. > > I'm on the fence between 3 and 4 myself. > Can I tip you over to #4? #3 means we have no idea when the stats were generated. With #4, we can't be sure, but it usually be close to when the command returns. The command should include a timeout so a broken guest won't hang a management tool thread.
On 01/07/2010 09:18 AM, Avi Kivity wrote: > On 01/07/2010 05:12 PM, Anthony Liguori wrote: >> >> 3) Make qemu request balloon stats regularly (maybe every 10 seconds) >> and display the latest stats with info balloon. This avoids the >> problem in #2 but it means that qemu determines the poll rate instead >> of a management tool. >> >> 4) Make info-balloon a proper asynchronous command. We need new >> infrastructure to allow a qmp handler to take a callback that can be >> used to delay the completion of the command. This addresses all of >> the above problems but it introduces a new one. Command completion >> now depends on the guest. This potentially could trip up a naive >> management tool that doesn't realize that the info-balloon command >> may never complete. >> >> I'm on the fence between 3 and 4 myself. >> > > Can I tip you over to #4? #3 means we have no idea when the stats > were generated. With #4, we can't be sure, but it usually be close to > when the command returns. > > The command should include a timeout so a broken guest won't hang a > management tool thread. Generally, timeouts are evil but if we did something like, wait 10 seconds and if we don't hear a response from the guest, return the last data set, I think I would be okay with that. It means we may be reporting stale data, but at the same time, the data is coming from a guest so it can't be considered authoritative anyway. Regards, Anthony Liguori
Thanks for the ideas and feedback. I will play around with #4 and see if I can hack up a prototype. On Thu, 2010-01-07 at 09:22 -0600, Anthony Liguori wrote: > On 01/07/2010 09:18 AM, Avi Kivity wrote: > > On 01/07/2010 05:12 PM, Anthony Liguori wrote: > >> > >> 3) Make qemu request balloon stats regularly (maybe every 10 seconds) > >> and display the latest stats with info balloon. This avoids the > >> problem in #2 but it means that qemu determines the poll rate instead > >> of a management tool. > >> > >> 4) Make info-balloon a proper asynchronous command. We need new > >> infrastructure to allow a qmp handler to take a callback that can be > >> used to delay the completion of the command. This addresses all of > >> the above problems but it introduces a new one. Command completion > >> now depends on the guest. This potentially could trip up a naive > >> management tool that doesn't realize that the info-balloon command > >> may never complete. > >> > >> I'm on the fence between 3 and 4 myself. > >> > > > > Can I tip you over to #4? #3 means we have no idea when the stats > > were generated. With #4, we can't be sure, but it usually be close to > > when the command returns. > > > > The command should include a timeout so a broken guest won't hang a > > management tool thread. > > Generally, timeouts are evil but if we did something like, wait 10 > seconds and if we don't hear a response from the guest, return the last > data set, I think I would be okay with that. It means we may be > reporting stale data, but at the same time, the data is coming from a > guest so it can't be considered authoritative anyway. > > Regards, > > Anthony Liguori
On Thu, Jan 07, 2010 at 09:12:10AM -0600, Anthony Liguori wrote: > On 01/05/2010 11:08 AM, Adam Litke wrote: > >This patch has been discussed (and ACKed) in the past, but has not yet > >been committed due to the congestion surrounding the 0.12 release and > >other conflicting changes. I would like to rekindle the discussion so > >that I can make the necessary changes to get this merged. > > > >This patch is ported to 0.12.1 and has one issue that I am aware of: > > > >It still calls monitor_suspend() and monitor_resume() which I understand > >is no longer allowed. Does anyone have any ideas on how to enable async > >monitor commands? > > > >+static void request_stats(VirtIOBalloon *vb) > >+{ > >+ vb->stats_requested = 1; > >+ reset_stats(vb); > >+ monitor_suspend(cur_mon); > > > > This bit is not going to work reliably anymore. Really, it never worked > reliably but this is exacerbated with -qmp since a QMP session will > never be the cur_mon. > > We have a couple of options: > > 1) Introduce a query-balloon command that returns immediately, and > triggers a request for the guest to update the balloon stats. When the > guest does update the balloon stats, trigger an asynchronous message. > Asynchronous messages are ignored by the human monitor so they would > never be displayed there which is unfortunate. > > 2) Make info balloon show the last valid stats and have it request new > stats. Stats will always be delayed but it avoids async messages. > > 3) Make qemu request balloon stats regularly (maybe every 10 seconds) > and display the latest stats with info balloon. This avoids the problem > in #2 but it means that qemu determines the poll rate instead of a > management tool. I don't like the idea of the anything which has hardcoded polling because it just wastes CPU cycles for the 99% of the time when no one is going to care about these stats. > 4) Make info-balloon a proper asynchronous command. We need new > infrastructure to allow a qmp handler to take a callback that can be > used to delay the completion of the command. This addresses all of the > above problems but it introduces a new one. Command completion now > depends on the guest. This potentially could trip up a naive management > tool that doesn't realize that the info-balloon command may never complete. I think 'info-balloon' should be synchronous and without side-effects. ie return the current stats that QEMU has. We could then add a separate 'refresh-balloon' command + async event notification when that completes. The tool that is requiring the stats could thus refresh as often as it likes. Daniel
On Thu, 2010-01-07 at 15:49 +0000, Daniel P. Berrange wrote: > On Thu, Jan 07, 2010 at 09:12:10AM -0600, Anthony Liguori wrote: > > On 01/05/2010 11:08 AM, Adam Litke wrote: > > >This patch has been discussed (and ACKed) in the past, but has not yet > > >been committed due to the congestion surrounding the 0.12 release and > > >other conflicting changes. I would like to rekindle the discussion so > > >that I can make the necessary changes to get this merged. > > > > > >This patch is ported to 0.12.1 and has one issue that I am aware of: > > > > > >It still calls monitor_suspend() and monitor_resume() which I understand > > >is no longer allowed. Does anyone have any ideas on how to enable async > > >monitor commands? > > > > > >+static void request_stats(VirtIOBalloon *vb) > > >+{ > > >+ vb->stats_requested = 1; > > >+ reset_stats(vb); > > >+ monitor_suspend(cur_mon); > > > > > > > This bit is not going to work reliably anymore. Really, it never worked > > reliably but this is exacerbated with -qmp since a QMP session will > > never be the cur_mon. > > > > We have a couple of options: > > > > 1) Introduce a query-balloon command that returns immediately, and > > triggers a request for the guest to update the balloon stats. When the > > guest does update the balloon stats, trigger an asynchronous message. > > Asynchronous messages are ignored by the human monitor so they would > > never be displayed there which is unfortunate. > > > > 2) Make info balloon show the last valid stats and have it request new > > stats. Stats will always be delayed but it avoids async messages. > > > > 3) Make qemu request balloon stats regularly (maybe every 10 seconds) > > and display the latest stats with info balloon. This avoids the problem > > in #2 but it means that qemu determines the poll rate instead of a > > management tool. > > I don't like the idea of the anything which has hardcoded polling because > it just wastes CPU cycles for the 99% of the time when no one is going to > care about these stats. > > > 4) Make info-balloon a proper asynchronous command. We need new > > infrastructure to allow a qmp handler to take a callback that can be > > used to delay the completion of the command. This addresses all of the > > above problems but it introduces a new one. Command completion now > > depends on the guest. This potentially could trip up a naive management > > tool that doesn't realize that the info-balloon command may never complete. > > I think 'info-balloon' should be synchronous and without side-effects. > ie return the current stats that QEMU has. We could then add a separate > 'refresh-balloon' command + async event notification when that completes. > The tool that is requiring the stats could thus refresh as often as it > likes. This would work well for the QMP case, but what about for a traditional monitor? We could include a sequence number or timestamp in the memory stats results so the user could tell that they were updated. This doesn't seem very user friendly though.
On 01/07/2010 06:27 PM, Adam Litke wrote: > >> I think 'info-balloon' should be synchronous and without side-effects. >> ie return the current stats that QEMU has. We could then add a separate >> 'refresh-balloon' command + async event notification when that completes. >> The tool that is requiring the stats could thus refresh as often as it >> likes. >> > This would work well for the QMP case, but what about for a traditional > monitor? We could include a sequence number or timestamp in the memory > stats results so the user could tell that they were updated. This > doesn't seem very user friendly though A user would have an easier time logging into the guest and using its native tools.
On 01/07/2010 10:39 AM, Avi Kivity wrote: > On 01/07/2010 06:27 PM, Adam Litke wrote: >> >>> I think 'info-balloon' should be synchronous and without side-effects. >>> ie return the current stats that QEMU has. We could then add a separate >>> 'refresh-balloon' command + async event notification when that >>> completes. >>> The tool that is requiring the stats could thus refresh as often as it >>> likes. >> This would work well for the QMP case, but what about for a traditional >> monitor? We could include a sequence number or timestamp in the memory >> stats results so the user could tell that they were updated. This >> doesn't seem very user friendly though > > A user would have an easier time logging into the guest and using its > native tools. Yeah, without objections, I like having info-balloon attempt to refresh stats, timeout after 10s, and return the last reported stats. I think it also satisfies danpb's criteria of not wasting cpu cycles polling. Regards, Anthony Liguori
On Thu, 07 Jan 2010 10:56:19 -0600 Anthony Liguori <anthony@codemonkey.ws> wrote: > On 01/07/2010 10:39 AM, Avi Kivity wrote: > > On 01/07/2010 06:27 PM, Adam Litke wrote: > >> > >>> I think 'info-balloon' should be synchronous and without side-effects. > >>> ie return the current stats that QEMU has. We could then add a separate > >>> 'refresh-balloon' command + async event notification when that > >>> completes. > >>> The tool that is requiring the stats could thus refresh as often as it > >>> likes. > >> This would work well for the QMP case, but what about for a traditional > >> monitor? We could include a sequence number or timestamp in the memory > >> stats results so the user could tell that they were updated. This > >> doesn't seem very user friendly though > > > > A user would have an easier time logging into the guest and using its > > native tools. I'm not sure I agree with that, running 'info balloon' in the Monitor is more convenient than logging specially if you're testing something related to balloon. > Yeah, without objections, I like having info-balloon attempt to refresh > stats, timeout after 10s, and return the last reported stats. I think > it also satisfies danpb's criteria of not wasting cpu cycles polling. I like Daniel's idea too. In practice 'refresh-balloon' is going to be Anthony's idea #1 for the QMP case, which seems the right way to do it with QMP. The only problem though is the user Monitor. Ideally handlers should not try to know if they are in QMP or not. I would be ok with that in this case, but if we get more asynchronous handlers doing this we will really need an better interface for that.
On Thu, 7 Jan 2010 15:58:30 -0200 Luiz Capitulino <lcapitulino@redhat.com> wrote: > I like Daniel's idea too. In practice 'refresh-balloon' is going to > be Anthony's idea #1 for the QMP case, which seems the right way to > do it with QMP. Hm, something that has just occurred to me: it's easy to have async messages in the user Monitor, we could add a new type of user print callback called async_print. This new callback would be called by the Monitor when the async message API is called but we are in user mode. This is really today's user_print, but user data is printed asynchronously.
Anthony Liguori wrote: > On 01/07/2010 09:18 AM, Avi Kivity wrote: > >On 01/07/2010 05:12 PM, Anthony Liguori wrote: > >> > >>3) Make qemu request balloon stats regularly (maybe every 10 seconds) > >>and display the latest stats with info balloon. This avoids the > >>problem in #2 but it means that qemu determines the poll rate instead > >>of a management tool. > >> > >>4) Make info-balloon a proper asynchronous command. We need new > >>infrastructure to allow a qmp handler to take a callback that can be > >>used to delay the completion of the command. This addresses all of > >>the above problems but it introduces a new one. Command completion > >>now depends on the guest. This potentially could trip up a naive > >>management tool that doesn't realize that the info-balloon command > >>may never complete. > >> > >>I'm on the fence between 3 and 4 myself. > >> > > > >Can I tip you over to #4? #3 means we have no idea when the stats > >were generated. With #4, we can't be sure, but it usually be close to > >when the command returns. > > > >The command should include a timeout so a broken guest won't hang a > >management tool thread. > > Generally, timeouts are evil but if we did something like, wait 10 > seconds and if we don't hear a response from the guest, return the last > data set, I think I would be okay with that. It means we may be > reporting stale data, but at the same time, the data is coming from a > guest so it can't be considered authoritative anyway. A flag in the response to say if it's fresh or stale would be good - or simply a timestamp in host time. The timeout does not need to be as long as 10 seconds. If the guest is so slow to respond, management can poll at whatever rate it wants until it gets a fresh response. For this a timestamp is better, because it may be happy with a response that is sufficiently recent but not due to the most recent request. -- Jamie
On 01/07/2010 12:30 PM, Luiz Capitulino wrote: > On Thu, 7 Jan 2010 15:58:30 -0200 > Luiz Capitulino<lcapitulino@redhat.com> wrote: > > >> I like Daniel's idea too. In practice 'refresh-balloon' is going to >> be Anthony's idea #1 for the QMP case, which seems the right way to >> do it with QMP. >> > Hm, something that has just occurred to me: it's easy to have > async messages in the user Monitor, we could add a new type of > user print callback called async_print. > > This new callback would be called by the Monitor when the async > message API is called but we are in user mode. > > This is really today's user_print, but user data is printed > asynchronously. > Even if we did that, it still suffers from the problem of a malicious or broken guest that would not respond thereby hanging the monitor. Regards, Anthony Liguori
On Fri, 08 Jan 2010 10:31:28 -0600 Anthony Liguori <anthony@codemonkey.ws> wrote: > On 01/07/2010 12:30 PM, Luiz Capitulino wrote: > > On Thu, 7 Jan 2010 15:58:30 -0200 > > Luiz Capitulino<lcapitulino@redhat.com> wrote: > > > > > >> I like Daniel's idea too. In practice 'refresh-balloon' is going to > >> be Anthony's idea #1 for the QMP case, which seems the right way to > >> do it with QMP. > >> > > Hm, something that has just occurred to me: it's easy to have > > async messages in the user Monitor, we could add a new type of > > user print callback called async_print. > > > > This new callback would be called by the Monitor when the async > > message API is called but we are in user mode. > > > > This is really today's user_print, but user data is printed > > asynchronously. > > > > Even if we did that, it still suffers from the problem of a malicious or > broken guest that would not respond thereby hanging the monitor. True. We could print the timeout but that's not user friendly. Btw, I don't remember if I said that already but we could send the command id with the timeout event.
On Thu, 2010-01-07 at 16:30 -0200, Luiz Capitulino wrote: > On Thu, 7 Jan 2010 15:58:30 -0200 > Luiz Capitulino <lcapitulino@redhat.com> wrote: > > > I like Daniel's idea too. In practice 'refresh-balloon' is going to > > be Anthony's idea #1 for the QMP case, which seems the right way to > > do it with QMP. > > Hm, something that has just occurred to me: it's easy to have > async messages in the user Monitor, we could add a new type of > user print callback called async_print. > > This new callback would be called by the Monitor when the async > message API is called but we are in user mode. > > This is really today's user_print, but user data is printed > asynchronously. There is one thing I am struggling with as I try to implement this asynchronous behavior. How can I suspend the monitor thread to wait for the stats update to come through? I see qemu_cond_timedwait(), but that appears to only be used for a VM's cpu threads. I also see the bottom half infrastructure, but I am not sure if that is appropriate either. Any pointers would be greatly appreciated.
On Fri, 08 Jan 2010 11:01:31 -0600 Adam Litke <agl@us.ibm.com> wrote: > On Thu, 2010-01-07 at 16:30 -0200, Luiz Capitulino wrote: > > On Thu, 7 Jan 2010 15:58:30 -0200 > > Luiz Capitulino <lcapitulino@redhat.com> wrote: > > > > > I like Daniel's idea too. In practice 'refresh-balloon' is going to > > > be Anthony's idea #1 for the QMP case, which seems the right way to > > > do it with QMP. > > > > Hm, something that has just occurred to me: it's easy to have > > async messages in the user Monitor, we could add a new type of > > user print callback called async_print. > > > > This new callback would be called by the Monitor when the async > > message API is called but we are in user mode. > > > > This is really today's user_print, but user data is printed > > asynchronously. > > There is one thing I am struggling with as I try to implement this > asynchronous behavior. How can I suspend the monitor thread to wait for > the stats update to come through? I see qemu_cond_timedwait(), but that > appears to only be used for a VM's cpu threads. I also see the bottom > half infrastructure, but I am not sure if that is appropriate either. IIUC, in QMP you won't wait at all and I believe suspend/resume should work on the user Monitor. You can use monitor_ctrl_mode() to know whether you're in QMP or not.
diff --git a/balloon.h b/balloon.h index 60b4a5d..dc851d3 100644 --- a/balloon.h +++ b/balloon.h @@ -16,12 +16,12 @@ #include "cpu-defs.h" -typedef ram_addr_t (QEMUBalloonEvent)(void *opaque, ram_addr_t target); +typedef QObject *(QEMUBalloonEvent)(void *opaque, ram_addr_t target); void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); -void qemu_balloon(ram_addr_t target); +int qemu_balloon(ram_addr_t target); -ram_addr_t qemu_balloon_status(void); +QObject *qemu_balloon_status(void); #endif diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index cfd3b41..03b9e2e 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -19,6 +19,10 @@ #include "balloon.h" #include "virtio-balloon.h" #include "kvm.h" +#include "monitor.h" +#include "qlist.h" +#include "qint.h" +#include "qstring.h" #if defined(__linux__) #include <sys/mman.h> @@ -27,9 +31,13 @@ typedef struct VirtIOBalloon { VirtIODevice vdev; - VirtQueue *ivq, *dvq; + VirtQueue *ivq, *dvq, *svq; uint32_t num_pages; uint32_t actual; + uint64_t stats[VIRTIO_BALLOON_S_NR]; + VirtQueueElement stats_vq_elem; + size_t stats_vq_offset; + uint8_t stats_requested; } VirtIOBalloon; static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) @@ -46,6 +54,33 @@ static void balloon_page(void *addr, int deflate) #endif } +static inline void reset_stats(VirtIOBalloon *dev) +{ + int i; + for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); +} + +static void stat_put(QDict *dict, const char *label, uint64_t val) +{ + if (val != -1) + qdict_put(dict, label, qint_from_int(val)); +} + +static QObject *get_stats_qobject(VirtIOBalloon *dev) +{ + QDict *dict = qdict_new(); + uint32_t actual = ram_size - (dev->actual << VIRTIO_BALLOON_PFN_SHIFT); + + stat_put(dict, "actual", actual); + stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]); + stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]); + stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]); + stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]); + stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]); + stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]); + return QOBJECT(dict); +} + /* FIXME: once we do a virtio refactoring, this will get subsumed into common * code */ static size_t memcpy_from_iovector(void *data, size_t offset, size_t size, @@ -104,6 +139,36 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) } } +static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + VirtQueueElement *elem = &s->stats_vq_elem; + VirtIOBalloonStat stat; + size_t offset = 0; + + if (!virtqueue_pop(vq, elem)) + return; + + while (memcpy_from_iovector(&stat, offset, sizeof(stat), elem->out_sg, + elem->out_num) == sizeof(stat)) { + uint16_t tag = tswap16(stat.tag); + uint64_t val = tswap64(stat.val); + + offset += sizeof(stat); + if (tag < VIRTIO_BALLOON_S_NR) + s->stats[tag] = val; + } + s->stats_vq_offset = offset; + + if (s->stats_requested) { + QObject *stats = get_stats_qobject(s); + monitor_print_balloon(cur_mon, stats); + qobject_decref(stats); + monitor_resume(cur_mon); + s->stats_requested = 0; + } +} + static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) { VirtIOBalloon *dev = to_virtio_balloon(vdev); @@ -126,12 +191,22 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, static uint32_t virtio_balloon_get_features(VirtIODevice *vdev) { - return 0; + return 1 << VIRTIO_BALLOON_F_STATS_VQ; +} + +static void request_stats(VirtIOBalloon *vb) +{ + vb->stats_requested = 1; + reset_stats(vb); + monitor_suspend(cur_mon); + virtqueue_push(vb->svq, &vb->stats_vq_elem, vb->stats_vq_offset); + virtio_notify(&vb->vdev, vb->svq); } -static ram_addr_t virtio_balloon_to_target(void *opaque, ram_addr_t target) +static QObject *virtio_balloon_to_target(void *opaque, ram_addr_t target) { VirtIOBalloon *dev = opaque; + QObject *ret = NULL; if (target > ram_size) target = ram_size; @@ -139,9 +214,15 @@ static ram_addr_t virtio_balloon_to_target(void *opaque, ram_addr_t target) if (target) { dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; virtio_notify_config(&dev->vdev); + } else if (dev->vdev.features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { + request_stats(dev); + ret = QOBJECT(qdict_new()); + } else { + reset_stats(dev); + ret = get_stats_qobject(dev); } - return ram_size - (dev->actual << VIRTIO_BALLOON_PFN_SHIFT); + return ret; } static void virtio_balloon_save(QEMUFile *f, void *opaque) @@ -152,6 +233,9 @@ static void virtio_balloon_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->num_pages); qemu_put_be32(f, s->actual); + qemu_put_buffer(f, (uint8_t *)&s->stats_vq_elem, sizeof(VirtQueueElement)); + qemu_put_buffer(f, (uint8_t *)&s->stats_vq_offset, sizeof(size_t)); + qemu_put_byte(f, s->stats_requested); } static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) @@ -165,6 +249,9 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) s->num_pages = qemu_get_be32(f); s->actual = qemu_get_be32(f); + qemu_get_buffer(f, (uint8_t *)&s->stats_vq_elem, sizeof(VirtQueueElement)); + qemu_get_buffer(f, (uint8_t *)&s->stats_vq_offset, sizeof(size_t)); + s->stats_requested = qemu_get_byte(f); return 0; } @@ -183,6 +270,7 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); + s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); qemu_add_balloon_handler(virtio_balloon_to_target, s); diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h index 9a0d119..e20cf6b 100644 --- a/hw/virtio-balloon.h +++ b/hw/virtio-balloon.h @@ -25,6 +25,7 @@ /* The feature bitmap for virtio balloon */ #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory stats virtqueue */ /* Size of a PFN in the balloon interface. */ #define VIRTIO_BALLOON_PFN_SHIFT 12 @@ -37,4 +38,18 @@ struct virtio_balloon_config uint32_t actual; }; +/* Memory Statistics */ +#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ +#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ +#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ +#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ +#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ +#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ +#define VIRTIO_BALLOON_S_NR 6 + +typedef struct VirtIOBalloonStat { + uint16_t tag; + uint64_t val; +} __attribute__((packed)) VirtIOBalloonStat; + #endif diff --git a/monitor.c b/monitor.c index c0dc48e..c4a669a 100644 --- a/monitor.c +++ b/monitor.c @@ -2058,43 +2058,27 @@ static void do_info_status(Monitor *mon, QObject **ret_data) vm_running, singlestep); } -static ram_addr_t balloon_get_value(void) +static void print_balloon_stat(const char *key, QObject *obj, void *opaque) { - ram_addr_t actual; - - if (kvm_enabled() && !kvm_has_sync_mmu()) { - qemu_error_new(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); - return 0; - } - - actual = qemu_balloon_status(); - if (actual == 0) { - qemu_error_new(QERR_DEVICE_NOT_ACTIVE, "balloon"); - return 0; - } - - return actual; -} + Monitor *mon = opaque; -/** - * do_balloon(): Request VM to change its memory allocation - */ -static void do_balloon(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - if (balloon_get_value()) { - /* ballooning is active */ - qemu_balloon(qdict_get_int(qdict, "value")); - } + if (strcmp(key, "actual")) + monitor_printf(mon, ",%s=%" PRId64, key, + qint_get_int(qobject_to_qint(obj))); } -static void monitor_print_balloon(Monitor *mon, const QObject *data) +void monitor_print_balloon(Monitor *mon, const QObject *data) { QDict *qdict; qdict = qobject_to_qdict(data); + if (!qdict_haskey(qdict, "actual")) + return; - monitor_printf(mon, "balloon: actual=%" PRId64 "\n", - qdict_get_int(qdict, "balloon") >> 20); + monitor_printf(mon, "balloon: actual=%" PRId64, + qdict_get_int(qdict, "actual") >> 20); + qdict_iter(qdict, print_balloon_stat, mon); + monitor_printf(mon, "\n"); } /** @@ -2102,21 +2086,48 @@ static void monitor_print_balloon(Monitor *mon, const QObject *data) * * Return a QDict with the following information: * - * - "balloon": current balloon value in bytes + * - "actual": current balloon value in bytes + * The following fields may or may not be present: + * - "mem_swapped_in": Amount of memory swapped in (bytes) + * - "mem_swapped_out": Amount of memory swapped out (bytes) + * - "major_page_faults": Number of major faults + * - "minor_page_faults": Number of minor faults + * - "free_mem": Total amount of free and unused memory (bytes) + * - "total_mem": Total amount of available memory (bytes) * * Example: * - * { "balloon": 1073741824 } + * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0, + * "major_page_faults": 142, "minor_page_faults": 239245, + * "free_mem": 1014185984, "total_mem": 1044668416 } */ static void do_info_balloon(Monitor *mon, QObject **ret_data) { - ram_addr_t actual; + if (kvm_enabled() && !kvm_has_sync_mmu()) { + qemu_error_new(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + return; + } + + *ret_data = qemu_balloon_status(); + if (*ret_data == NULL) + qemu_error_new(QERR_DEVICE_NOT_ACTIVE, "balloon"); +} - actual = balloon_get_value(); - if (actual != 0) { - *ret_data = qobject_from_jsonf("{ 'balloon': %" PRId64 "}", - (int64_t) actual); +/** + * do_balloon(): Request VM to change its memory allocation + */ +static void do_balloon(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + int ret; + + if (kvm_enabled() && !kvm_has_sync_mmu()) { + qemu_error_new(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + return; } + + ret = qemu_balloon(qdict_get_int(qdict, "value")); + if (ret == 0) + qemu_error_new(QERR_DEVICE_NOT_ACTIVE, "balloon"); } static qemu_acl *find_acl(Monitor *mon, const char *name) diff --git a/monitor.h b/monitor.h index 6ed117a..f7d8780 100644 --- a/monitor.h +++ b/monitor.h @@ -32,6 +32,7 @@ void monitor_resume(Monitor *mon); void monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, BlockDriverCompletionFunc *completion_cb, void *opaque); +void monitor_print_balloon(Monitor *mon, const QObject *data); int monitor_get_fd(Monitor *mon, const char *fdname); diff --git a/vl.c b/vl.c index 8be3648..029e572 100644 --- a/vl.c +++ b/vl.c @@ -362,17 +362,21 @@ void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) qemu_balloon_event_opaque = opaque; } -void qemu_balloon(ram_addr_t target) +int qemu_balloon(ram_addr_t target) { - if (qemu_balloon_event) + if (qemu_balloon_event) { qemu_balloon_event(qemu_balloon_event_opaque, target); + return 1; + } else { + return 0; + } } -ram_addr_t qemu_balloon_status(void) +QObject *qemu_balloon_status(void) { if (qemu_balloon_event) return qemu_balloon_event(qemu_balloon_event_opaque, 0); - return 0; + return NULL; } /***********************************************************/