Merge remote-tracking branch 'luiz/queue/qmp' into staging

# By Lei Li (3) and others
# Via Luiz Capitulino
* luiz/queue/qmp:
  QAPI: Introduce memchar-read QMP command
  QAPI: Introduce memchar-write QMP command
  qemu-char: Add new char backend CirMemCharDriver
  docs: document virtio-balloon stats
  balloon: re-enable balloon stats
  balloon: drop old stats code & API
  block: Monitor command commit neglects to report some errors
This commit is contained in:
Anthony Liguori 2013-01-28 14:41:25 -06:00
commit 6cebf7afac
10 changed files with 689 additions and 89 deletions

View file

@ -642,21 +642,17 @@ void do_commit(Monitor *mon, const QDict *qdict)
if (!strcmp(device, "all")) {
ret = bdrv_commit_all();
if (ret == -EBUSY) {
qerror_report(QERR_DEVICE_IN_USE, device);
return;
}
} else {
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
monitor_printf(mon, "Device '%s' not found\n", device);
return;
}
ret = bdrv_commit(bs);
if (ret == -EBUSY) {
qerror_report(QERR_DEVICE_IN_USE, device);
return;
}
}
if (ret < 0) {
monitor_printf(mon, "'commit' error for '%s': %s\n", device,
strerror(-ret));
}
}

View file

@ -0,0 +1,104 @@
virtio balloon memory statistics
================================
The virtio balloon driver supports guest memory statistics reporting. These
statistics are available to QEMU users as QOM (QEMU Object Model) device
properties via a polling mechanism.
Before querying the available stats, clients first have to enable polling.
This is done by writing a time interval value (in seconds) to the
guest-stats-polling-interval property. This value can be:
> 0 enables polling in the specified interval. If polling is already
enabled, the polling time interval is changed to the new value
0 disables polling. Previous polled statistics are still valid and
can be queried.
Once polling is enabled, the virtio-balloon device in QEMU will start
polling the guest's balloon driver for new stats in the specified time
interval.
To retrieve those stats, clients have to query the guest-stats property,
which will return a dictionary containing:
o A key named 'stats', containing all available stats. If the guest
doesn't support a particular stat, or if it couldn't be retrieved,
its value will be -1. Currently, the following stats are supported:
- stat-swap-in
- stat-swap-out
- stat-major-faults
- stat-minor-faults
- stat-free-memory
- stat-total-memory
o A key named last-update, which contains the last stats update
timestamp in seconds. Since this timestamp is generated by the host,
a buggy guest can't influence its value
It's also important to note the following:
- Previously polled statistics remain available even if the polling is
later disabled
- As noted above, if a guest doesn't support a particular stat its value
will always be -1. However, it's also possible that a guest temporarily
couldn't update one or even all stats. If this happens, just wait for
the next update
- Polling can be enabled even if the guest doesn't have stats support
or the balloon driver wasn't loaded in the guest. If this is the case
and stats are queried, an error will be returned
- The polling timer is only re-armed when the guest responds to the
statistics request. This means that if a (buggy) guest doesn't ever
respond to the request the timer will never be re-armed, which has
the same effect as disabling polling
Here are a few examples. QEMU is started with '-balloon virtio', which
generates '/machine/peripheral-anon/device[1]' as the QOM path for the
balloon device.
Enable polling with 2 seconds interval:
{ "execute": "qom-set",
"arguments": { "path": "/machine/peripheral-anon/device[1]",
"property": "guest-stats-polling-interval", "value": 2 } }
{ "return": {} }
Change polling to 10 seconds:
{ "execute": "qom-set",
"arguments": { "path": "/machine/peripheral-anon/device[1]",
"property": "guest-stats-polling-interval", "value": 10 } }
{ "return": {} }
Get stats:
{ "execute": "qom-get",
"arguments": { "path": "/machine/peripheral-anon/device[1]",
"property": "guest-stats" } }
{
"return": {
"stats": {
"stat-swap-out": 0,
"stat-free-memory": 844943360,
"stat-minor-faults": 219028,
"stat-major-faults": 235,
"stat-total-memory": 1044406272,
"stat-swap-in": 0
},
"last-update": 1358529861
}
}
Disable polling:
{ "execute": "qom-set",
"arguments": { "path": "/machine/peripheral-anon/device[1]",
"property": "stats-polling-interval", "value": 0 } }
{ "return": {} }

View file

@ -837,6 +837,45 @@ STEXI
@item nmi @var{cpu}
@findex nmi
Inject an NMI on the given CPU (x86 only).
ETEXI
{
.name = "memchar_write",
.args_type = "device:s,data:s",
.params = "device data",
.help = "Provide writing interface for CirMemCharDriver. Write"
"'data' to it.",
.mhandler.cmd = hmp_memchar_write,
},
STEXI
@item memchar_write @var{device} @var{data}
@findex memchar_write
Provide writing interface for CirMemCharDriver. Write @var{data}
to char device 'memory'.
ETEXI
{
.name = "memchar_read",
.args_type = "device:s,size:i",
.params = "device size",
.help = "Provide read interface for CirMemCharDriver. Read from"
"it and return the data with size.",
.mhandler.cmd = hmp_memchar_read,
},
STEXI
@item memchar_read @var{device}
@findex memchar_read
Provide read interface for CirMemCharDriver. Read from char device
'memory' and return the data.
@var{size} is the size of data want to read from. Refer to unencoded
size of the raw data, would adjust to the init size of the memchar
if the requested size is larger than it.
ETEXI
{

58
hmp.c
View file

@ -465,29 +465,7 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
return;
}
monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20);
if (info->has_mem_swapped_in) {
monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in);
}
if (info->has_mem_swapped_out) {
monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out);
}
if (info->has_major_page_faults) {
monitor_printf(mon, " major_page_faults=%" PRId64,
info->major_page_faults);
}
if (info->has_minor_page_faults) {
monitor_printf(mon, " minor_page_faults=%" PRId64,
info->minor_page_faults);
}
if (info->has_free_mem) {
monitor_printf(mon, " free_mem=%" PRId64, info->free_mem);
}
if (info->has_total_mem) {
monitor_printf(mon, " total_mem=%" PRId64, info->total_mem);
}
monitor_printf(mon, "\n");
monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20);
qapi_free_BalloonInfo(info);
}
@ -684,6 +662,40 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &errp);
}
void hmp_memchar_write(Monitor *mon, const QDict *qdict)
{
uint32_t size;
const char *chardev = qdict_get_str(qdict, "device");
const char *data = qdict_get_str(qdict, "data");
Error *errp = NULL;
size = strlen(data);
qmp_memchar_write(chardev, size, data, false, 0, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_memchar_read(Monitor *mon, const QDict *qdict)
{
uint32_t size = qdict_get_int(qdict, "size");
const char *chardev = qdict_get_str(qdict, "device");
MemCharRead *meminfo;
Error *errp = NULL;
meminfo = qmp_memchar_read(chardev, size, false, 0, &errp);
if (errp) {
monitor_printf(mon, "%s\n", error_get_pretty(errp));
error_free(errp);
return;
}
if (meminfo->count > 0) {
monitor_printf(mon, "%s\n", meminfo->data);
}
qapi_free_MemCharRead(meminfo);
}
static void hmp_cont_cb(void *opaque, int err)
{
if (!err) {

2
hmp.h
View file

@ -43,6 +43,8 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
void hmp_cpu(Monitor *mon, const QDict *qdict);
void hmp_memsave(Monitor *mon, const QDict *qdict);
void hmp_pmemsave(Monitor *mon, const QDict *qdict);
void hmp_memchar_write(Monitor *mon, const QDict *qdict);
void hmp_memchar_read(Monitor *mon, const QDict *qdict);
void hmp_cont(Monitor *mon, const QDict *qdict);
void hmp_system_wakeup(Monitor *mon, const QDict *qdict);
void hmp_inject_nmi(Monitor *mon, const QDict *qdict);

View file

@ -14,6 +14,7 @@
*/
#include "qemu/iov.h"
#include "qemu/timer.h"
#include "qemu-common.h"
#include "virtio.h"
#include "pc.h"
@ -22,6 +23,7 @@
#include "virtio-balloon.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
#include "qapi/visitor.h"
#if defined(__linux__)
#include <sys/mman.h>
@ -36,6 +38,9 @@ typedef struct VirtIOBalloon
uint64_t stats[VIRTIO_BALLOON_S_NR];
VirtQueueElement stats_vq_elem;
size_t stats_vq_offset;
QEMUTimer *stats_timer;
int64_t stats_last_update;
int64_t stats_poll_interval;
DeviceState *qdev;
} VirtIOBalloon;
@ -53,6 +58,16 @@ static void balloon_page(void *addr, int deflate)
#endif
}
static const char *balloon_stat_names[] = {
[VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in",
[VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out",
[VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults",
[VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults",
[VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory",
[VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory",
[VIRTIO_BALLOON_S_NR] = NULL
};
/*
* reset_stats - Mark all items in the stats array as unset
*
@ -67,6 +82,118 @@ static inline void reset_stats(VirtIOBalloon *dev)
for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1);
}
static bool balloon_stats_supported(const VirtIOBalloon *s)
{
return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ);
}
static bool balloon_stats_enabled(const VirtIOBalloon *s)
{
return s->stats_poll_interval > 0;
}
static void balloon_stats_destroy_timer(VirtIOBalloon *s)
{
if (balloon_stats_enabled(s)) {
qemu_del_timer(s->stats_timer);
qemu_free_timer(s->stats_timer);
s->stats_timer = NULL;
s->stats_poll_interval = 0;
}
}
static void balloon_stats_change_timer(VirtIOBalloon *s, int secs)
{
qemu_mod_timer(s->stats_timer, qemu_get_clock_ms(vm_clock) + secs * 1000);
}
static void balloon_stats_poll_cb(void *opaque)
{
VirtIOBalloon *s = opaque;
if (!balloon_stats_supported(s)) {
/* re-schedule */
balloon_stats_change_timer(s, s->stats_poll_interval);
return;
}
virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset);
virtio_notify(&s->vdev, s->svq);
}
static void balloon_stats_get_all(Object *obj, struct Visitor *v,
void *opaque, const char *name, Error **errp)
{
VirtIOBalloon *s = opaque;
int i;
if (!s->stats_last_update) {
error_setg(errp, "guest hasn't updated any stats yet");
return;
}
visit_start_struct(v, NULL, "guest-stats", name, 0, errp);
visit_type_int(v, &s->stats_last_update, "last-update", errp);
visit_start_struct(v, NULL, NULL, "stats", 0, errp);
for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
errp);
}
visit_end_struct(v, errp);
visit_end_struct(v, errp);
}
static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
void *opaque, const char *name,
Error **errp)
{
VirtIOBalloon *s = opaque;
visit_type_int(v, &s->stats_poll_interval, name, errp);
}
static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v,
void *opaque, const char *name,
Error **errp)
{
VirtIOBalloon *s = opaque;
int64_t value;
visit_type_int(v, &value, name, errp);
if (error_is_set(errp)) {
return;
}
if (value < 0) {
error_setg(errp, "timer value must be greater than zero");
return;
}
if (value == s->stats_poll_interval) {
return;
}
if (value == 0) {
/* timer=0 disables the timer */
balloon_stats_destroy_timer(s);
return;
}
if (balloon_stats_enabled(s)) {
/* timer interval change */
s->stats_poll_interval = value;
balloon_stats_change_timer(s, value);
return;
}
/* create a new timer */
g_assert(s->stats_timer == NULL);
s->stats_timer = qemu_new_timer_ms(vm_clock, balloon_stats_poll_cb, s);
s->stats_poll_interval = value;
balloon_stats_change_timer(s, 0);
}
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBalloon *s = to_virtio_balloon(vdev);
@ -107,9 +234,10 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
VirtQueueElement *elem = &s->stats_vq_elem;
VirtIOBalloonStat stat;
size_t offset = 0;
qemu_timeval tv;
if (!virtqueue_pop(vq, elem)) {
return;
goto out;
}
/* Initialize the stats to get rid of any stale values. This is only
@ -128,6 +256,18 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
s->stats[tag] = val;
}
s->stats_vq_offset = offset;
if (qemu_gettimeofday(&tv) < 0) {
fprintf(stderr, "warning: %s: failed to get time of day\n", __func__);
goto out;
}
s->stats_last_update = tv.tv_sec;
out:
if (balloon_stats_enabled(s)) {
balloon_stats_change_timer(s, s->stats_poll_interval);
}
}
static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
@ -164,28 +304,6 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
{
VirtIOBalloon *dev = opaque;
#if 0
/* Disable guest-provided stats for now. For more details please check:
* https://bugzilla.redhat.com/show_bug.cgi?id=623903
*
* If you do enable it (which is probably not going to happen as we
* need a new command for it), remember that you also need to fill the
* appropriate members of the BalloonInfo structure so that the stats
* are returned to the client.
*/
if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) {
virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset);
virtio_notify(&dev->vdev, dev->svq);
return;
}
#endif
/* Stats are not supported. Clear out any stale values that might
* have been set by a more featureful guest kernel.
*/
reset_stats(dev);
info->actual = ram_size - ((uint64_t) dev->actual <<
VIRTIO_BALLOON_PFN_SHIFT);
}
@ -255,12 +373,18 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev)
s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats);
reset_stats(s);
s->qdev = dev;
register_savevm(dev, "virtio-balloon", -1, 1,
virtio_balloon_save, virtio_balloon_load, s);
object_property_add(OBJECT(dev), "guest-stats", "guest statistics",
balloon_stats_get_all, NULL, NULL, s, NULL);
object_property_add(OBJECT(dev), "guest-stats-polling-interval", "int",
balloon_stats_get_poll_interval,
balloon_stats_set_poll_interval,
NULL, s, NULL);
return &s->vdev;
}
@ -268,6 +392,7 @@ void virtio_balloon_exit(VirtIODevice *vdev)
{
VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
balloon_stats_destroy_timer(s);
qemu_remove_balloon_handler(s);
unregister_savevm(s->qdev, "virtio-balloon", s);
virtio_cleanup(vdev);

View file

@ -324,6 +324,80 @@
##
{ 'command': 'query-chardev', 'returns': ['ChardevInfo'] }
##
# @DataFormat:
#
# An enumeration of data format.
#
# @utf8: The data format is 'utf8'.
#
# @base64: The data format is 'base64'.
#
# Since: 1.4
##
{ 'enum': 'DataFormat'
'data': [ 'utf8', 'base64' ] }
##
# @memchar-write:
#
# Provide writing interface for memchardev. Write data to char
# device 'memory'.
#
# @device: the name of the memory char device.
#
# @size: the size to write in bytes.
#
# @data: the source data write to memchar.
#
# @format: #optional the format of the data write to chardev 'memory',
# by default is 'utf8'.
#
# Returns: Nothing on success
# If @device is not a valid char device, DeviceNotFound
#
# Since: 1.4
##
{ 'command': 'memchar-write',
'data': {'device': 'str', 'size': 'int', 'data': 'str',
'*format': 'DataFormat'} }
##
# @MemCharRead
#
# Result of QMP command memchar-read.
#
# @data: The data read from memchar as string.
#
# @count: The numbers of bytes read from.
#
# Since: 1.4
##
{ 'type': 'MemCharRead',
'data': { 'data': 'str', 'count': 'int' } }
##
# @memchar-read:
#
# Provide read interface for memchardev. Read from the char
# device 'memory' and return the data.
#
# @device: the name of the memory char device.
#
# @size: the size to read in bytes.
#
# @format: #optional the format of the data want to read from
# memchardev, by default is 'utf8'.
#
# Returns: @MemCharRead
# If @device is not a valid memchr device, DeviceNotFound
#
# Since: 1.4
##
{ 'command': 'memchar-read',
'data': {'device': 'str', 'size': 'int', '*format': 'DataFormat'},
'returns': 'MemCharRead' }
##
# @CommandInfo:
#
@ -977,28 +1051,10 @@
#
# @actual: the number of bytes the balloon currently contains
#
# @mem_swapped_in: #optional number of pages swapped in within the guest
#
# @mem_swapped_out: #optional number of pages swapped out within the guest
#
# @major_page_faults: #optional number of major page faults within the guest
#
# @minor_page_faults: #optional number of minor page faults within the guest
#
# @free_mem: #optional amount of memory (in bytes) free in the guest
#
# @total_mem: #optional amount of memory (in bytes) visible to the guest
#
# Since: 0.14.0
#
# Notes: all current versions of QEMU do not fill out optional information in
# this structure.
##
{ 'type': 'BalloonInfo',
'data': {'actual': 'int', '*mem_swapped_in': 'int',
'*mem_swapped_out': 'int', '*major_page_faults': 'int',
'*minor_page_faults': 'int', '*free_mem': 'int',
'*total_mem': 'int'} }
{ 'type': 'BalloonInfo', 'data': {'actual': 'int' } }
##
# @query-balloon:

View file

@ -98,6 +98,7 @@
#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096
#define CBUFF_SIZE 65536
/***********************************************************/
/* character device */
@ -2643,6 +2644,199 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr)
return d->outbuf_size;
}
/*********************************************************/
/*CircularMemory chardev*/
typedef struct {
size_t size;
size_t prod;
size_t cons;
uint8_t *cbuf;
} CirMemCharDriver;
static bool cirmem_chr_is_empty(const CharDriverState *chr)
{
const CirMemCharDriver *d = chr->opaque;
return d->cons == d->prod;
}
static size_t qemu_chr_cirmem_count(const CharDriverState *chr)
{
const CirMemCharDriver *d = chr->opaque;
return (d->prod - d->cons);
}
static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
CirMemCharDriver *d = chr->opaque;
int i;
if (!buf || (len < 0)) {
return -1;
}
for (i = 0; i < len; i++ ) {
/* Avoid writing the IAC information to the queue. */
if ((unsigned char)buf[i] == IAC) {
continue;
}
d->cbuf[d->prod++ % d->size] = buf[i];
if ((d->prod - d->cons) > d->size) {
d->cons = d->prod - d->size;
}
}
return 0;
}
static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len)
{
CirMemCharDriver *d = chr->opaque;
int i;
for (i = 0; i < len && !cirmem_chr_is_empty(chr); i++) {
buf[i] = d->cbuf[d->cons++ % d->size];
}
return i;
}
static void cirmem_chr_close(struct CharDriverState *chr)
{
CirMemCharDriver *d = chr->opaque;
g_free(d->cbuf);
g_free(d);
chr->opaque = NULL;
}
static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts)
{
CharDriverState *chr;
CirMemCharDriver *d;
chr = g_malloc0(sizeof(CharDriverState));
d = g_malloc(sizeof(*d));
d->size = qemu_opt_get_number(opts, "maxcapacity", 0);
if (d->size == 0) {
d->size = CBUFF_SIZE;
}
/* The size must be power of 2 */
if (d->size & (d->size - 1)) {
fprintf(stderr, "chardev: size of memory device must be power of 2\n");
goto fail;
}
d->prod = 0;
d->cons = 0;
d->cbuf = g_malloc0(d->size);
chr->opaque = d;
chr->chr_write = cirmem_chr_write;
chr->chr_close = cirmem_chr_close;
return chr;
fail:
g_free(d);
g_free(chr);
return NULL;
}
static bool qemu_is_chr(const CharDriverState *chr, const char *filename)
{
return strcmp(chr->filename, filename);
}
void qmp_memchar_write(const char *device, int64_t size,
const char *data, bool has_format,
enum DataFormat format,
Error **errp)
{
CharDriverState *chr;
guchar *write_data;
int ret;
gsize write_count;
chr = qemu_chr_find(device);
if (!chr) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (qemu_is_chr(chr, "memory")) {
error_setg(errp,"%s is not memory char device", device);
return;
}
write_count = (gsize)size;
if (has_format && (format == DATA_FORMAT_BASE64)) {
write_data = g_base64_decode(data, &write_count);
} else {
write_data = (uint8_t *)data;
}
ret = cirmem_chr_write(chr, write_data, write_count);
if (ret < 0) {
error_setg(errp, "Failed to write to device %s", device);
return;
}
}
MemCharRead *qmp_memchar_read(const char *device, int64_t size,
bool has_format, enum DataFormat format,
Error **errp)
{
CharDriverState *chr;
guchar *read_data;
MemCharRead *meminfo;
size_t count;
chr = qemu_chr_find(device);
if (!chr) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return NULL;
}
if (qemu_is_chr(chr, "memory")) {
error_setg(errp,"%s is not memory char device", device);
return NULL;
}
if (size <= 0) {
error_setg(errp, "size must be greater than zero");
return NULL;
}
meminfo = g_malloc0(sizeof(MemCharRead));
count = qemu_chr_cirmem_count(chr);
if (count == 0) {
meminfo->data = g_strdup("");
return meminfo;
}
size = size > count ? count : size;
read_data = g_malloc0(size + 1);
meminfo->count = cirmem_chr_read(chr, read_data, size);
if (has_format && (format == DATA_FORMAT_BASE64)) {
meminfo->data = g_base64_encode(read_data, (size_t)meminfo->count);
} else {
meminfo->data = (char *)read_data;
}
return meminfo;
}
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
{
char host[65], port[33], width[8], height[8];
@ -2697,6 +2891,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
qemu_opt_set(opts, "path", filename);
return opts;
}
if (strstart(filename, "memory", &p)) {
qemu_opt_set(opts, "backend", "memory");
qemu_opt_set(opts, "maxcapacity", p);
return opts;
}
if (strstart(filename, "file:", &p)) {
qemu_opt_set(opts, "backend", "file");
qemu_opt_set(opts, "path", p);
@ -2796,6 +2995,7 @@ static const struct {
{ .name = "udp", .open = qemu_chr_open_udp },
{ .name = "msmouse", .open = qemu_chr_open_msmouse },
{ .name = "vc", .open = text_console_init },
{ .name = "memory", .open = qemu_chr_open_cirmemchr },
#ifdef _WIN32
{ .name = "file", .open = qemu_chr_open_win_file_out },
{ .name = "pipe", .open = qemu_chr_open_win_pipe },
@ -3055,6 +3255,9 @@ QemuOptsList qemu_chardev_opts = {
},{
.name = "debug",
.type = QEMU_OPT_NUMBER,
},{
.name = "maxcapacity",
.type = QEMU_OPT_NUMBER,
},
{ /* end of list */ }
},

View file

@ -1736,6 +1736,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
"-chardev msmouse,id=id[,mux=on|off]\n"
"-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n"
" [,mux=on|off]\n"
"-chardev memory,id=id,maxcapacity=maxcapacity\n"
"-chardev file,id=id,path=path[,mux=on|off]\n"
"-chardev pipe,id=id,path=path[,mux=on|off]\n"
#ifdef _WIN32
@ -1777,6 +1778,7 @@ Backend is one of:
@option{udp},
@option{msmouse},
@option{vc},
@option{memory},
@option{file},
@option{pipe},
@option{console},
@ -1885,6 +1887,14 @@ the console, in pixels.
@option{cols} and @option{rows} specify that the console be sized to fit a text
console with the given dimensions.
@item -chardev memory ,id=@var{id} ,maxcapacity=@var{maxcapacity}
Create a circular buffer with fixed size indicated by optionally @option{maxcapacity}
which will be default 64K if it is not given.
@option{maxcapacity} specifies the max capacity of the size of circular buffer
to create. Should be power of 2.
@item -chardev file ,id=@var{id} ,path=@var{path}
Log all traffic received from the guest to a file.

View file

@ -463,6 +463,72 @@ Example:
Note: inject-nmi fails when the guest doesn't support injecting.
Currently, only x86 guests do.
EQMP
{
.name = "memchar-write",
.args_type = "device:s,size:i,data:s,format:s?",
.mhandler.cmd_new = qmp_marshal_input_memchar_write,
},
SQMP
memchar-write
-------------
Provide writing interface for CirMemCharDriver. Write data to memory
char device.
Arguments:
- "device": the name of the char device, must be unique (json-string)
- "size": the memory size, in bytes, should be power of 2 (json-int)
- "data": the source data write to memory (json-string)
- "format": the data format write to memory, default is
utf8. (json-string, optional)
- Possible values: "utf8", "base64"
Example:
-> { "execute": "memchar-write",
"arguments": { "device": foo,
"size": 8,
"data": "abcdefgh",
"format": "utf8" } }
<- { "return": {} }
EQMP
{
.name = "memchar-read",
.args_type = "device:s,size:i,format:s?",
.mhandler.cmd_new = qmp_marshal_input_memchar_read,
},
SQMP
memchar-read
-------------
Provide read interface for CirMemCharDriver. Read from the char
device memory and return the data with size.
Arguments:
- "device": the name of the char device, must be unique (json-string)
- "size": the memory size wanted to read in bytes (refer to unencoded
size of the raw data), would adjust to the init size of the
memchar if the requested size is larger than it. (json-int)
- "format": the data format write to memchardev, default is
utf8. (json-string, optional)
- Possible values: "utf8", "base64"
Example:
-> { "execute": "memchar-read",
"arguments": { "device": foo,
"size": 1000,
"format": "utf8" } }
<- { "return": { "data": "data string...", "count": 1000 } }
EQMP
{
@ -2549,13 +2615,6 @@ Make an asynchronous request for balloon info. When the request completes a
json-object will be returned containing the following data:
- "actual": current balloon value in bytes (json-int)
- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional)
- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional)
- "major_page_faults": Number of major faults (json-int, optional)
- "minor_page_faults": Number of minor faults (json-int, optional)
- "free_mem": Total amount of free and unused memory in
bytes (json-int, optional)
- "total_mem": Total amount of available memory in bytes (json-int, optional)
Example:
@ -2563,12 +2622,6 @@ Example:
<- {
"return":{
"actual":1073741824,
"mem_swapped_in":0,
"mem_swapped_out":0,
"major_page_faults":142,
"minor_page_faults":239245,
"free_mem":1014185984,
"total_mem":1044668416
}
}