Patchwork [RFD] virtio: Add memory statistics reporting to the balloon driver

login
register
mail settings
Submitter Adam Litke
Date Jan. 5, 2010, 5:08 p.m.
Message ID <1262711318.10698.104.camel@aglitke>
Download mbox | patch
Permalink /patch/42167/
State New
Headers show

Comments

Adam Litke - Jan. 5, 2010, 5:08 p.m.
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?
Anthony Liguori - Jan. 7, 2010, 3:12 p.m.
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
Avi Kivity - Jan. 7, 2010, 3:18 p.m.
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.
Anthony Liguori - Jan. 7, 2010, 3:22 p.m.
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
Adam Litke - Jan. 7, 2010, 3:39 p.m.
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
Daniel P. Berrange - Jan. 7, 2010, 3:49 p.m.
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
Adam Litke - Jan. 7, 2010, 4:27 p.m.
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.
Avi Kivity - Jan. 7, 2010, 4:39 p.m.
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.
Anthony Liguori - Jan. 7, 2010, 4:56 p.m.
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
Luiz Capitulino - Jan. 7, 2010, 5:58 p.m.
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.
Luiz Capitulino - Jan. 7, 2010, 6:30 p.m.
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.
Jamie Lokier - Jan. 8, 2010, 1:33 a.m.
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
Anthony Liguori - Jan. 8, 2010, 4:31 p.m.
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
Luiz Capitulino - Jan. 8, 2010, 4:51 p.m.
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.
Adam Litke - Jan. 8, 2010, 5:01 p.m.
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.
Luiz Capitulino - Jan. 8, 2010, 5:25 p.m.
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.

Patch

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;
 }
 
 /***********************************************************/