Re: [Qemu-devel] [PATCH] qemu-char: (Re-)connect for tcp_chr_write() unconnected writing
Ping? On 07/20/2012 07:09 AM, Anthony Liguori wrote: Lei Li writes: tcp_chr_write() did not deal with writing to an unconnected connection and return the original length of the data, it's not right and would cause false writing. So (re-)connect it and return 0 for this situation. Signed-off-by: Lei Li Looks good. Reviewed-by: Anthony Liguori Regards, Anthony Liguori --- qemu-char.c |7 +-- 1 files changed, 5 insertions(+), 2 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index c2aaaee..25eaeeb 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2141,14 +2141,17 @@ typedef struct { static void tcp_chr_accept(void *opaque); +static void tcp_chr_connect(void *opaque); + static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; if (s->connected) { return send_all(s->fd, buf, len); } else { -/* XXX: indicate an error ? */ -return len; +/* (Re-)connect for unconnected writing */ +tcp_chr_connect(chr); +return 0; } } -- 1.7.7.6 -- Lei
Re: [Qemu-devel] [RFC PATCH 1/4] qemu-char: Convert MemCharDriver to circular buffer
On 08/02/2012 05:30 AM, Anthony Liguori wrote: Lei Li writes: Signed-off-by: Lei Li --- qemu-char.c | 96 +++--- qemu-char.h |2 +- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index c2aaaee..087c92d 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2517,38 +2517,96 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) /***/ /* Memory chardev */ typedef struct { -size_t outbuf_size; -size_t outbuf_capacity; -uint8_t *outbuf; +size_t cbuf_capacity; +size_t cbuf_in; +size_t cbuf_out; +size_t cbuf_count; +uint8_t *cbuf; } MemoryDriver; Probably should move the buffer into a separate structure and then you can drop the cbuf_ prefixes. +static int mem_chr_is_empty(CharDriverState *chr) +{ +MemoryDriver *d = chr->opaque; + +return d->cbuf_count == 0; +} + +static int mem_chr_is_full(CharDriverState *chr) +{ +MemoryDriver *d = chr->opaque; + +return d->cbuf_count == d->cbuf_capacity; +} + Typically, you would use a producer and a consumer index. To test for empty, you would check if (consumer == producer). To check for full, you would check if ((producer - consumer) == size). To get the actual index, you always modulus the indexes with size. This only works if size is a power of 2 but that's a reasonable restriction. static int mem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { MemoryDriver *d = chr->opaque; +int left; -/* TODO: the QString implementation has the same code, we should - * introduce a generic way to do this in cutils.c */ -if (d->outbuf_capacity < d->outbuf_size + len) { -/* grow outbuf */ -d->outbuf_capacity += len; -d->outbuf_capacity *= 2; -d->outbuf = g_realloc(d->outbuf, d->outbuf_capacity); +if (d->cbuf_capacity < len) { +return -1; } -memcpy(d->outbuf + d->outbuf_size, buf, len); -d->outbuf_size += len; +left = d->cbuf_capacity - d->cbuf_count % d->cbuf_capacity; + +/* Some of cbuf need to be overwrited */ +if (left < len) { +memcpy(d->cbuf + d->cbuf_in, buf, left); +memcpy(d->cbuf + d->cbuf_out, buf + left, len - left); +d->cbuf_out = (d->cbuf_out + len - left) % d->cbuf_capacity; +d->cbuf_count = d->cbuf_count + left; Doing a mempcy() like this may work, but seems inefficient to me. I think reading like a ring queue works a bit nicer. Hi Anthony, What do you mean "reading like a ring queue"? I am a little confused here. Could you please give more details? And thanks for your suggestions. :) +} else { +/* Completely overwrite */ +if (mem_chr_is_full(chr)) { +d->cbuf_out = (d->cbuf_out + len) % d->cbuf_capacity; +} else { +/* Enough cbuf to write */ +memcpy(d->cbuf + d->cbuf_in, buf, len); +d->cbuf_count += len; +} Looks like indenting is off here. Regards, Anthony Liguori +} + +d->cbuf_in = (d->cbuf_in + len) % d->cbuf_capacity; return len; } -void qemu_chr_init_mem(CharDriverState *chr) +static void mem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +MemoryDriver *d = chr->opaque; +int left; + +if (mem_chr_is_empty(chr)) { +return; +} + +left = d->cbuf_capacity - d->cbuf_count % d->cbuf_capacity; + +if (d->cbuf_capacity < len) { +len = d->cbuf_capacity; +} + +if (left < len) { +memcpy(buf, d->cbuf + d->cbuf_out, left); +memcpy(buf + left, d->cbuf + d->cbuf_out + left, len - left); +} else { +memcpy(buf, d->cbuf + d->cbuf_out, len); +} + +d->cbuf_out = (d->cbuf_out + len) % d->cbuf_capacity; +d->cbuf_count -= len; +} + +void qemu_chr_init_mem(CharDriverState *chr, size_t size) { MemoryDriver *d; d = g_malloc(sizeof(*d)); -d->outbuf_size = 0; -d->outbuf_capacity = 4096; -d->outbuf = g_malloc0(d->outbuf_capacity); +d->cbuf_capacity = size; +d->cbuf_in = 0; +d->cbuf_out = 0; +d->cbuf_count = 0; +d->cbuf = g_malloc0(d->cbuf_capacity); memset(chr, 0, sizeof(*chr)); chr->opaque = d; @@ -2558,7 +2616,7 @@ void qemu_chr_init_mem(CharDriverState *chr) QString *qemu_chr_mem_to_qs(CharDriverState *chr) { MemoryDriver *d = chr->opaque; -return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1); +return qstring_from_substr((char *) d->cbuf, 0, d->cbuf_count - 1); } /* NOTE: this driver can not be closed with qemu_chr_delete()! */ @@ -2566,7 +2624,7 @@ void
[Qemu-devel] [PATCH 1/6] qemu-char: Convert MemCharDriver to circular buffer
Signed-off-by: Lei Li --- qemu-char.c | 96 +++--- qemu-char.h |2 +- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 398baf1..b21b93a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2528,38 +2528,96 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) /***/ /* Memory chardev */ typedef struct { -size_t outbuf_size; -size_t outbuf_capacity; -uint8_t *outbuf; +size_t cbuf_capacity; +size_t cbuf_in; +size_t cbuf_out; +size_t cbuf_count; +uint8_t *cbuf; } MemoryDriver; +static int mem_chr_is_empty(CharDriverState *chr) +{ +MemoryDriver *d = chr->opaque; + +return d->cbuf_count == 0; +} + +static int mem_chr_is_full(CharDriverState *chr) +{ +MemoryDriver *d = chr->opaque; + +return d->cbuf_count == d->cbuf_capacity; +} + static int mem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { MemoryDriver *d = chr->opaque; +int left; -/* TODO: the QString implementation has the same code, we should - * introduce a generic way to do this in cutils.c */ -if (d->outbuf_capacity < d->outbuf_size + len) { -/* grow outbuf */ -d->outbuf_capacity += len; -d->outbuf_capacity *= 2; -d->outbuf = g_realloc(d->outbuf, d->outbuf_capacity); +if (d->cbuf_capacity < len) { +return -1; } -memcpy(d->outbuf + d->outbuf_size, buf, len); -d->outbuf_size += len; +left = d->cbuf_capacity - d->cbuf_count % d->cbuf_capacity; + +/* Some of cbuf need to be overwrited */ +if (left < len) { +memcpy(d->cbuf + d->cbuf_in, buf, left); +memcpy(d->cbuf + d->cbuf_out, buf + left, len - left); +d->cbuf_out = (d->cbuf_out + len - left) % d->cbuf_capacity; +d->cbuf_count = d->cbuf_count + left; +} else { +/* Completely overwrite */ +if (mem_chr_is_full(chr)) { +d->cbuf_out = (d->cbuf_out + len) % d->cbuf_capacity; +} else { +/* Enough cbuf to write */ +d->cbuf_count += len; +} +memcpy(d->cbuf + d->cbuf_in, buf, len); +} + +d->cbuf_in = (d->cbuf_in + len) % d->cbuf_capacity; return len; } -void qemu_chr_init_mem(CharDriverState *chr) +static void mem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +MemoryDriver *d = chr->opaque; +int left; + +if (mem_chr_is_empty(chr)) { +return; +} + +left = d->cbuf_capacity - d->cbuf_count % d->cbuf_capacity; + +if (d->cbuf_capacity < len) { +len = d->cbuf_capacity; +} + +if (left < len) { +memcpy(buf, d->cbuf + d->cbuf_out, left); +memcpy(buf + left, d->cbuf + d->cbuf_out + left, len - left); +} else { +memcpy(buf, d->cbuf + d->cbuf_out, len); +} + +d->cbuf_out = (d->cbuf_out + len) % d->cbuf_capacity; +d->cbuf_count -= len; +} + +void qemu_chr_init_mem(CharDriverState *chr, size_t size) { MemoryDriver *d; d = g_malloc(sizeof(*d)); -d->outbuf_size = 0; -d->outbuf_capacity = 4096; -d->outbuf = g_malloc0(d->outbuf_capacity); +d->cbuf_capacity = size; +d->cbuf_in = 0; +d->cbuf_out = 0; +d->cbuf_count = 0; +d->cbuf = g_malloc0(d->cbuf_capacity); memset(chr, 0, sizeof(*chr)); chr->opaque = d; @@ -2569,7 +2627,7 @@ void qemu_chr_init_mem(CharDriverState *chr) QString *qemu_chr_mem_to_qs(CharDriverState *chr) { MemoryDriver *d = chr->opaque; -return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1); +return qstring_from_substr((char *) d->cbuf, 0, d->cbuf_count - 1); } /* NOTE: this driver can not be closed with qemu_chr_delete()! */ @@ -2577,7 +2635,7 @@ void qemu_chr_close_mem(CharDriverState *chr) { MemoryDriver *d = chr->opaque; -g_free(d->outbuf); +g_free(d->cbuf); g_free(chr->opaque); chr->opaque = NULL; chr->chr_write = NULL; @@ -2586,7 +2644,7 @@ void qemu_chr_close_mem(CharDriverState *chr) size_t qemu_chr_mem_osize(const CharDriverState *chr) { const MemoryDriver *d = chr->opaque; -return d->outbuf_size; +return d->cbuf_count; } QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) diff --git a/qemu-char.h b/qemu-char.h index 486644b..d8d90cc 100644 --- a/qemu-char.h +++ b/qemu-char.h @@ -243,7 +243,7 @@ CharDriverState *qemu_chr_open_eventfd(int eventfd); extern int term_escape_char; /* memory chardev */ -void qemu_chr_init_mem(CharDriverState *chr); +void qemu_chr_init_mem(CharDriverState *chr, size_t size); void qemu_chr_close_mem(CharDriverState *chr); QString *qemu_chr_mem_to_qs(CharDriverState *chr); size_t qemu_chr_mem_osize(const CharDriverState *chr); -- 1.7.7.6
[Qemu-devel] [RFC v2 ATCH 0/4] char: expose MemoryCharDriver to users and provide QMP interface
This RFC series attempts to convert the MemCharDriver to use a circular buffer for input and output, expose it to users by introducing QMP commands memchar_write and memchar_read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar_write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str'} } { 'command': 'memchar_read', 'data': {'chardev': 'str', 'size': 'int'}, 'returns': 'str' } Expose MemCharDriver via the command line like: qemu -chardev memchr,max-capacity=640k,id=foo -serial chardev:foo Note: This series is just a incomplete sketch and not completely tested which I am still struggling with, but I want to get this out ealier to have your suggestion. Please comment and let me know if this seems like the direction we should be headed, thanks! TODO: 1) Add congestion mechanism. 2) Add HMP "console" command so that can interact with multiple chardevs via a single monitor socket Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. For the comments of MemCharDriver improvment, which I am working on and will send out within v3 with the rest feature implemented in few days. Lei Li (6): qemu-char: Convert MemCharDriver to circular buffer monitor: Adjust qmp_human_monitor_command to new MemCharDriver QAPI: Introduce memchar_write QMP command QAPI: Introduce memchar_read QMP command Fix enumeration typo error Expose MemCharDriver via command line hmp-commands.hx| 32 hmp.c | 30 hmp.h |2 + monitor.c |8 ++- qapi-schema-guest.json |2 +- qapi-schema.json | 69 +- qemu-char.c| 190 +++- qemu-char.h|2 +- qemu-config.c |3 + qemu-options.hx| 10 +++ qmp-commands.hx| 64 11 files changed, 388 insertions(+), 24 deletions(-)
[Qemu-devel] [PATCH 2/6] monitor: Adjust qmp_human_monitor_command to new MemCharDriver
Signed-off-by: Lei Li --- monitor.c |8 +++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/monitor.c b/monitor.c index 480f583..ab4650b 100644 --- a/monitor.c +++ b/monitor.c @@ -642,7 +642,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, CharDriverState mchar; memset(&hmp, 0, sizeof(hmp)); -qemu_chr_init_mem(&mchar); + +/* Since the backend of MemCharDriver convert to a circular + * buffer with fixed size, so should indicate the init memory + * size. + * + * XXX: is 4096 as init memory enough for this? */ +qemu_chr_init_mem(&mchar, 4096); hmp.chr = &mchar; old_mon = cur_mon; -- 1.7.7.6
[Qemu-devel] [PATCH 5/6] Fix enumeration typo error
Signed-off-by: Lei Li --- qapi-schema-guest.json |2 +- qapi-schema.json |4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index d955cf1..ed0eb69 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -293,7 +293,7 @@ ## # @GuestFsFreezeStatus # -# An enumation of filesystem freeze states +# An enumeration of filesystem freeze states # # @thawed: filesystems thawed/unfrozen # diff --git a/qapi-schema.json b/qapi-schema.json index 1346bcc..8e13a05 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -118,7 +118,7 @@ ## # @RunState # -# An enumation of VM run states. +# An enumeration of VM run states. # # @debug: QEMU is running on a debugger # @@ -836,7 +836,7 @@ ## # @SpiceQueryMouseMode # -# An enumation of Spice mouse states. +# An enumeration of Spice mouse states. # # @client: Mouse cursor position is determined by the client. # -- 1.7.7.6
[Qemu-devel] [PATCH 4/6] QAPI: Introduce memchar_read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 16 hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 23 +++ qemu-char.c | 32 qmp-commands.hx | 31 +++ 6 files changed, 118 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 829aea1..9f61633 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -813,6 +813,22 @@ to memchr char device with size @var{size}. ETEXI { +.name = "memchar-read", +.args_type = "chardev:s,size:i,format:s?", +.params = "chardev size", +.help = "Provide read interface for memchardev. Read from" + "memchr char device and return 'size' of the data", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar-read @var{chardev} @var{size} @var{format} +@findex memchar-read +Provide read interface for memchardev. Read from memchr +char device and return @var{size} of the data. +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index c1164eb..0c0182a 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,21 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +int con = qdict_get_try_bool(qdict, "utf8", 0); +enum DataFormat format; +Error *errp = NULL; + +format = con ? DATA_FORMAT_UTF8 : DATA_FORMAT_BASE64; + +data = qmp_memchar_read(chardev, size, true, format, &errp); +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 15e1311..b734e31 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 2fc1a27..1346bcc 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -264,6 +264,29 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. +# +# @format: #optional the format of the data want to read from +# memchardev, by default is 'utf8'. +# +# Returns: The data read from memchar as string. +# If @chardev is not a valid memchr device, DeviceNotFound +# If an I/O error occurs while reading, IOError +# +# Since: 1.2 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index d8f3238..ff6651b 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2683,6 +2683,38 @@ void qmp_memchar_write(const char *chardev, int64_t size, } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) + +{ +CharDriverState *chr; +guchar *read_data; +char * data = NULL; + +read_data = g_malloc0(size); + +chr = qemu_chr_find(chardev); +if(!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return NULL; +} + +mem_chr_read(chr, read_data, size); + +if (has_format) { +if (format == DATA_FORMAT_BASE64) { +data = g_base64_encode(read_data, (size_t)size); +} +} else { +data = (char *)read_data; +} + +g_free(read_data); + +return data; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; diff --git a/qmp-commands.hx b/qmp-commands.hx index 3c747cd..a9890db 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -474,6 +474,37 @@ Example: EQMP { +.name = "memchar-read", +.args_type = "chardev:s,size:i", +.m
[Qemu-devel] [PATCH 6/6] Expose MemCharDriver via command line
Signed-off-by: Lei Li --- qemu-char.c | 24 qemu-config.c |3 +++ qemu-options.hx | 10 ++ 3 files changed, 37 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index ff6651b..36f4ecc 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2647,6 +2648,23 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->cbuf_count; } +static CharDriverState *qemu_chr_open_memchr(QemuOpts *opts) +{ +CharDriverState *chr; +size_t capacity; + +chr = g_malloc0(sizeof(CharDriverState)); + +capacity = qemu_opt_get_number(opts, "maxcapacity", 0); +if (capacity == 0) { +capacity = CBUFF_SIZE; +} + +qemu_chr_init_mem(chr, capacity); + +return chr; +} + void qmp_memchar_write(const char *chardev, int64_t size, const char *data, bool has_format, enum DataFormat format, @@ -2779,6 +2797,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2852,6 +2875,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 = "memchr",.open = qemu_chr_open_memchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, diff --git a/qemu-config.c b/qemu-config.c index c05ffbc..7118d0e 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -114,6 +114,9 @@ static QemuOptsList qemu_drive_opts = { .name = "copy-on-read", .type = QEMU_OPT_BOOL, .help = "copy read data from backing file into image file", +},{ +.name = "max-capacity", +.type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 3c411c4..1ccf295 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1647,6 +1647,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 memchr,id=id,max-capacity=max-capacity\n" "-chardev file,id=id,path=path[,mux=on|off]\n" "-chardev pipe,id=id,path=path[,mux=on|off]\n" #ifdef _WIN32 @@ -1685,6 +1686,7 @@ Backend is one of: @option{udp}, @option{msmouse}, @option{vc}, +@option{memchr}, @option{file}, @option{pipe}, @option{console}, @@ -1791,6 +1793,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 memchr ,id=@var{id} ,max-capacity=@var{max-capacity} + +Create a circular buffer with fixed size indicated by optionally @option{max-capacity} +which will be default 64K if it is not given. + +@option{max-capacity} specify the max capacity of the size of circular buffer +want to create. + @item -chardev file ,id=@var{id} ,path=@var{path} Log all traffic received from the guest to a file. -- 1.7.7.6
[Qemu-devel] [PATCH 3/6] QAPI: Introduce memchar_write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 16 hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 28 qemu-char.c | 36 qmp-commands.hx | 33 + 6 files changed, 129 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index f6104b0..829aea1 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -797,6 +797,22 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar-write", +.args_type = "chardev:s,size:i,data:s,format:s?", +.params = "chardev size data", +.help = "Provide writing interface for memchardev. Write" + "'data' to memchr char device with size 'size'", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar-write @var{chardev} @var{size} @var{data} @var{format} +@findex memchar-write +Provide writing interface for memchardev. Write @var{data} +to memchr char device with size @var{size}. +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 81c8acb..c1164eb 100644 --- a/hmp.c +++ b/hmp.c @@ -668,6 +668,21 @@ 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 = qdict_get_int(qdict, "size"); +const char *chardev = qdict_get_str(qdict, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +int con = qdict_get_try_bool(qdict, "utf8", 0); +enum DataFormat format; +Error *errp = NULL; + +format = con ? DATA_FORMAT_UTF8 : DATA_FORMAT_BASE64; +qmp_memchar_write(chardev, size, data, true, format, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 7dd93bf..15e1311 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index bd8ad74..2fc1a27 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -235,6 +235,34 @@ ## { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar 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 memchardev, by +# default is 'utf8'. +# +# Returns: Nothing on success +# If @chardev is not a valid memchr device, DeviceNotFound +# If an I/O error occurs while writing, IOError +# +# Since: 1.3 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + ## # @CommandInfo: # diff --git a/qemu-char.c b/qemu-char.c index b21b93a..d8f3238 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2647,6 +2647,42 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->cbuf_count; } +void qmp_memchar_write(const char *chardev, 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(chardev); + +if(!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return; +} + +write_count = (gsize)size; + +if (has_format) { +if (format == DATA_FORMAT_BASE64) { +write_data = g_base64_decode(data, &write_count); +} +} else { +write_data = (uint8_t *)data; +} + +ret = mem_chr_write(chr, write_data, size); +if (ret <= 0) { +error_set(errp, QERR_IO_ERROR); +} +g_free(write_data); + +} + QemuOpts *qemu_chr_parse_compat(const char *label,
Re: [Qemu-devel] [PATCH 3/6] QAPI: Introduce memchar_write QMP command
On 08/23/2012 01:42 PM, Eric Blake wrote: On 08/22/2012 11:14 PM, Lei Li wrote: Signed-off-by: Lei Li Subject line uses '_', but the QMP command uses '-' [1] --- hmp-commands.hx | 16 hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 28 qemu-char.c | 36 qmp-commands.hx | 33 + 6 files changed, 129 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index f6104b0..829aea1 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -797,6 +797,22 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar-write", HMP commands should use '_', not '-'. +++ b/qapi-schema.json @@ -235,6 +235,34 @@ ## { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } Missing documentation for DataFormat (see for example how @ErrorClass is documented). Sure, I will look into it and add documentation in v3, thanks! -- Lei
Re: [Qemu-devel] [PATCH 4/6] QAPI: Introduce memchar_read QMP command
On 08/23/2012 01:46 PM, Eric Blake wrote: On 08/22/2012 11:14 PM, Lei Li wrote: Signed-off-by: Lei Li Again, subject line should use '-' not '_' for QMP. --- hmp-commands.hx | 16 hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 23 +++ qemu-char.c | 32 qmp-commands.hx | 31 +++ 6 files changed, 118 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 829aea1..9f61633 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -813,6 +813,22 @@ to memchr char device with size @var{size}. ETEXI { +.name = "memchar-read", while the HMP command should use '_' not '-'. +++ b/qapi-schema.json @@ -264,6 +264,29 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar char device. Why two spaces? +# +# @size: the size to write in bytes. s/write/read/ +# +# @format: #optional the format of the data want to read from +# memchardev, by default is 'utf8'. +# +# Returns: The data read from memchar as string. +# If @chardev is not a valid memchr device, DeviceNotFound +# If an I/O error occurs while reading, IOError +# +# Since: 1.2 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } While writing can default to UTF-8, I'm worried about reading - does encoding in UTF-8 allow transmission of NUL bytes through JSON, or do you have to use base64 to allow full binary data through? What happens if the data read includes a NUL byte that can't be encoded back into the requested DataFormat? Yes, you are right. g_base64_encode did not support NULL bytes encoding, so encoding in UTF-8 should not allow transmission of NULL bytes through JSON. Thanks for pointing this out! -- Lei
Re: [Qemu-devel] [PATCH v2] chardev: add hotplug support.
On 10/12/2012 08:39 PM, Gerd Hoffmann wrote: This patch adds chardev_add and chardev_del monitor commands. chardev_del is pretty straight forward, it just takes an id argument and zaps the chardev specified. chardev_add is more tricky as there are tons of arguments for the different backends. The hmp version limited to the most common use cases, especially when it comes to sockets: You can only specify port (tcp) or path (unix) and qemu will create a listening socket. For example this ... (qemu) chardev_add foo socket 42 ... will do the same as ... -chardev socket,id=foo,port=42,server,nowait on the qemu command line. The qmp version has full support for everything the -chardev command line switch can handle. The implementation is pretty straight forward: It just puts all arguments it got into a QemuOpts, then goes call qemu_chr_new_from_opts(). Signed-off-by: Gerd Hoffmann --- hmp-commands.hx | 32 hmp.c| 31 +++ hmp.h|2 + qapi-schema.json | 39 ++ qemu-char.c | 50 +++- qemu-char.h |2 + qmp-commands.hx | 61 ++ 7 files changed, 216 insertions(+), 1 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..48504d1 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1404,6 +1404,38 @@ passed since 1970, i.e. unix epoch. ETEXI { +.name = "chardev_add", +.args_type = "args:s", +.params = "args", +.help = "add chardev", +.mhandler.cmd = hmp_chardev_add, +}, + +STEXI +@item chardev_add args +@findex chardev_add + +chardev_add accepts the same parameters as the -chardev command line switch. + +ETEXI + +{ +.name = "chardev_del", +.args_type = "id:s", +.params = "id", +.help = "del chardev", +.mhandler.cmd = hmp_chardev_del, +}, + +STEXI +@item chardev_del id +@findex chardev_del + +Removes the chardev @var{id}. + +ETEXI + +{ .name = "info", .args_type = "item:s?", .params = "[subcommand]", diff --git a/hmp.c b/hmp.c index 70bdec2..b494d05 100644 --- a/hmp.c +++ b/hmp.c @@ -1209,3 +1209,34 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +void hmp_chardev_add(Monitor *mon, const QDict *qdict) +{ +const char *args = qdict_get_str(qdict, "args"); +CharDriverState *chr; +Error *err = NULL; +QemuOpts *opts; + +opts = qemu_opts_parse(qemu_find_opts("chardev"), args, 1); +if (opts == NULL) { +error_setg(&err, "Parsing chardev args failed\n"); +goto out; +} + +chr = qemu_chr_new_from_opts(opts, NULL); +if (chr == NULL) { +qemu_opts_del(opts); +error_setg(&err, "Creating chardev failed\n"); +} + +out: +hmp_handle_error(mon, &err); +} + +void hmp_chardev_del(Monitor *mon, const QDict *qdict) +{ +Error *err = NULL; +qmp_chardev_del(qdict_get_str(qdict, "id"), +&err); +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 71ea384..080afaa 100644 --- a/hmp.h +++ b/hmp.h @@ -75,5 +75,7 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_chardev_add(Monitor *mon, const QDict *qdict); +void hmp_chardev_del(Monitor *mon, const QDict *qdict); #endif diff --git a/qapi-schema.json b/qapi-schema.json index f9dbdae..550e4c7 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2796,3 +2796,42 @@ # Since: 0.14.0 ## { 'command': 'screendump', 'data': {'filename': 'str'} } + +## +# @chardev_add: +# +# Add a chardev +# +# @id: the chardev's ID, must be unique +# @backend: the chardev backend: "file", "socket", ... +# @path: file / device / unix socket path +# @name: spice channel name +# @host: host name +# @port: port number +# @server: create socket in server mode +# @wait: wait for connect +# @ipv4: force ipv4-only +# @ipv6: force ipv6-only +# @telnet: telnet negotiation +# +# Returns: Nothing on success +# +# Since: 1.3.0 +## +{ 'command': 'chardev_add', 'data': {'id' : 'str', + 'backend' : 'str', + '*props' : '**' }, + 'gen': 'no' } + +## +# @chardev_del: +# +# Remove a chardev +# +# @id: the chardev's ID, must exist and not be in use +# +# Returns: Nothing on success +# +# Since: 1.3.0 +## +{ 'command': 'chardev_del', 'data': {'id': 'str'} } diff --git a/qemu-char.c b/qemu-char.c index b082bae..7bbc490 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2805,6 +2805,7 @@ CharDriverState *qemu_chr_new_from_opts(Qe
Re: [Qemu-devel] [PATCH v3 9/9] chardev: add hotplug support.
On 10/15/2012 04:06 PM, Gerd Hoffmann wrote: This patch adds chardev_add and chardev_del monitor commands. They work simliar to the netdev_{add,del} commands. The hmp version of chardev_add accepts like the -chardev command line option does. The qmp version expects the arguments being passed as named parameters. chardev_del just takes an id argument and zaps the chardev specified. Signed-off-by: Gerd Hoffmann --- hmp-commands.hx | 32 hmp.c| 23 hmp.h|2 + qapi-schema.json | 39 ++ qemu-char.c | 44 ++ qemu-char.h |1 + qmp-commands.hx | 61 ++ 7 files changed, 202 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..48504d1 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1404,6 +1404,38 @@ passed since 1970, i.e. unix epoch. ETEXI { +.name = "chardev_add", +.args_type = "args:s", +.params = "args", +.help = "add chardev", +.mhandler.cmd = hmp_chardev_add, +}, + +STEXI +@item chardev_add args +@findex chardev_add + +chardev_add accepts the same parameters as the -chardev command line switch. + +ETEXI + +{ +.name = "chardev_del", +.args_type = "id:s", +.params = "id", +.help = "del chardev", +.mhandler.cmd = hmp_chardev_del, +}, + +STEXI +@item chardev_del id +@findex chardev_del + +Removes the chardev @var{id}. + +ETEXI + +{ .name = "info", .args_type = "item:s?", .params = "[subcommand]", diff --git a/hmp.c b/hmp.c index 70bdec2..96bb900 100644 --- a/hmp.c +++ b/hmp.c @@ -1209,3 +1209,26 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +void hmp_chardev_add(Monitor *mon, const QDict *qdict) +{ +const char *args = qdict_get_str(qdict, "args"); +Error *err = NULL; +QemuOpts *opts; + +opts = qemu_opts_parse(qemu_find_opts("chardev"), args, 1); +if (opts == NULL) { +error_setg(&err, "Parsing chardev args failed\n"); +} else { +qemu_chr_new_from_opts(opts, NULL, &err); +} +hmp_handle_error(mon, &err); +} + +void hmp_chardev_del(Monitor *mon, const QDict *qdict) +{ +Error *err = NULL; +qmp_chardev_del(qdict_get_str(qdict, "id"), +&err); +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 71ea384..080afaa 100644 --- a/hmp.h +++ b/hmp.h @@ -75,5 +75,7 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_chardev_add(Monitor *mon, const QDict *qdict); +void hmp_chardev_del(Monitor *mon, const QDict *qdict); #endif diff --git a/qapi-schema.json b/qapi-schema.json index f9dbdae..550e4c7 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2796,3 +2796,42 @@ # Since: 0.14.0 ## { 'command': 'screendump', 'data': {'filename': 'str'} } + +## +# @chardev_add: +# +# Add a chardev +# +# @id: the chardev's ID, must be unique +# @backend: the chardev backend: "file", "socket", ... +# @path: file / device / unix socket path +# @name: spice channel name +# @host: host name +# @port: port number +# @server: create socket in server mode +# @wait: wait for connect +# @ipv4: force ipv4-only +# @ipv6: force ipv6-only +# @telnet: telnet negotiation +# +# Returns: Nothing on success +# +# Since: 1.3.0 +## +{ 'command': 'chardev_add', 'data': {'id' : 'str', + 'backend' : 'str', + '*props' : '**' }, + 'gen': 'no' } + +## +# @chardev_del: +# +# Remove a chardev +# +# @id: the chardev's ID, must exist and not be in use +# +# Returns: Nothing on success +# +# Since: 1.3.0 +## +{ 'command': 'chardev_del', 'data': {'id': 'str'} } diff --git a/qemu-char.c b/qemu-char.c index be4ec61..dae3e3c 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2911,3 +2911,47 @@ CharDriverState *qemu_char_get_next_serial(void) return serial_hds[next_serial++]; } +int qmp_chardev_add(Monitor *mon, const QDict *qdict, QObject **ret) +{ +Error *err = NULL; +QemuOptsList *opts_list; +QemuOpts *opts; + +opts_list = qemu_find_opts_err("chardev", &err); +if (error_is_set(&err)) { +goto exit_err; +} + +opts = qemu_opts_from_qdict(opts_list, qdict, &err); +if (error_is_set(&err)) { +goto exit_err; +} + +qemu_chr_new_from_opts(opts, NULL, &err); +if (error_is_set(&err)) { +goto exit_err; +} +return 0; + +exit_err: +qerror_report_err(err); +error_free(err); +return -1; +} + +void
[Qemu-devel] [PATCH 1/5] qemu-char: Add new char backend CircularMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 72 +++ 1 files changed, 72 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..b174da1 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2588,6 +2588,78 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemoryr chardev*/ + +typedef struct { +size_t size; +size_t producer; +size_t consumer; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer == d->consumer; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return (d->producer - d->consumer) >= d->size; +} + +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (len < 0) { +return -1; +} + +/* The size should be a power of 2. */ +for (i = 0; i < len; i++ ) { +d->cbuf[d->producer % d->size] = buf[i]; +d->producer++; +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +if (len > d->size) { +len = d->size; +} + +for (i = 0; i < len; i++) { +buf[i] = d->cbuf[d->consumer % d->size]; +d->consumer++; +} + +return 0; +} + +static void cirmem_chr_close(struct CharDriverState *chr) +{ +CirMemCharDriver *d = chr->opaque; + +g_free(d); +g_free(chr->opaque); +chr->opaque = NULL; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; -- 1.7.7.6
[Qemu-devel] [PATCH 2/5] Expose CirMemCharDriver via command line
Signed-off-by: Lei Li --- qemu-char.c | 36 qemu-config.c |3 +++ qemu-options.hx | 10 ++ 3 files changed, 49 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b174da1..381bf60 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2660,6 +2661,35 @@ static void cirmem_chr_close(struct CharDriverState *chr) 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; +} + +if (d->size & (d->size -1)) { +return NULL; +} + +d->producer = 0; +d->consumer = 0; +d->cbuf = g_malloc0(d->size); + +memset(chr, 0, sizeof(*chr)); +chr->opaque = d; +chr->chr_write = cirmem_chr_write; +chr->chr_close = cirmem_chr_close; + +return chr; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2724,6 +2754,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2797,6 +2832,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 = "memchr",.open = qemu_chr_open_cirmemchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, diff --git a/qemu-config.c b/qemu-config.c index cd1ec21..5553bb6 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -213,6 +213,9 @@ static QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, +},{ +.name = "maxcapacity", +.type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 7d97f96..4f90f20 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1684,6 +1684,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 memchr,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 @@ -1722,6 +1723,7 @@ Backend is one of: @option{udp}, @option{msmouse}, @option{vc}, +@option{memchr}, @option{file}, @option{pipe}, @option{console}, @@ -1828,6 +1830,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 memchr ,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} specify the max capacity of the size of circular buffer +want 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. -- 1.7.7.6
[Qemu-devel] [PATCH 0/5 V4] char: add CirMemCharDriver and provide QMP interface
This RFC series attempts to convert the MemCharDriver to use a circular buffer for input and output, expose it to users by introducing QMP commands memchar_write and memchar_read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str', 'control': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str', 'control': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memchr,id=foo,maxcapacity=640k -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Please comment and let me know if there is anything else need to be improved. Your suggestion would be very appreciated! Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (5): qemu-char: Add new char backend CirMemCharDriver Expose CirMemCharDriver via command line QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 4/5] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 25 + hmp.c| 17 + hmp.h|1 + qapi-schema.json | 27 +++ qemu-char.c | 52 qmp-commands.hx | 40 6 files changed, 162 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 753aab3..5f91428 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -847,6 +847,31 @@ that specifies behavior when the queue is full/empty. By default is ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,control:-b,size:i", +.params = "chardev [-b] size", +.help = "Provide read interface for CirMemCharDriver. Read from" + "it and return 'size' of the data. (Use -b for 'block'" + "option)", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read [-b] @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of the data. + +@var{size} is the size of data want to read from. + +@var{control} is option['block', 'drop'] for read and write command +that specifies behavior when the queue is full/empty. By default is +'drop'. Note that the 'block' option is not supported now. +-b for 'block' option. None for 'drop' option. + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 18ca61b..fa858c4 100644 --- a/hmp.c +++ b/hmp.c @@ -690,6 +690,23 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +enum DataFormat format; +int con = qdict_get_try_bool(qdict, "block", 0); +enum CongestionControl control; +Error *errp = NULL; + +format = DATA_FORMAT_UTF8; +control = con ? CONGESTION_CONTROL_BLOCK : CONGESTION_CONTROL_DROP; +data = qmp_memchar_read(chardev, size, true, format, +true, control, &errp); +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 406ebb1..a5a0cfe 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index a908aa6..4db91b0 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -394,6 +394,33 @@ '*control': 'CongestionControl'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar 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'. +# +# @control: #optional options for read and write command that specifies +# behavior when the queue is full/empty. +# +# Returns: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# If an I/O error occurs while reading, IOError +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat', + '*control': 'CongestionControl'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 133d282..a8bcead 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2736,6 +2736,58 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + bool has_control, enum CongestionControl control, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +
[Qemu-devel] [PATCH 3/5] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 22 + hmp.c| 19 +++ hmp.h|1 + qapi-schema.json | 69 ++ qemu-char.c | 46 qmp-commands.hx | 40 +++ 6 files changed, 197 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..753aab3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,28 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,control:-b,data:s", +.params = "chardev [-b] data", +.help = "Provide writing interface for CirMemCharDriver. Write" + "'data' to it. (Use -b for 'block' option)", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} [-b] @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. The size of the data write to the driver +should better be power of 2. + +@var{control} is option('block', 'drop') for read and write command +that specifies behavior when the queue is full/empty. By default is +'drop'. Note that the 'block' option is not supported now. +-b for 'block' option. None for 'drop' option. +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 70bdec2..18ca61b 100644 --- a/hmp.c +++ b/hmp.c @@ -671,6 +671,25 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; +int con = qdict_get_try_bool(qdict, "block", 0); +enum CongestionControl control; +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +control = con ? CONGESTION_CONTROL_BLOCK : CONGESTION_CONTROL_DROP; +qmp_memchar_write(chardev, size, data, true, format, + true, control, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index f9dbdae..a908aa6 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,75 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format write to or read from +# memchardev. The default value would be utf8. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', will support +# other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @CongestionControl +# +# An enumeration of options for the read and write command that +# specifies behavior when the queue is full/empty. The default +# option would be drop. +# +# @drop: Would result in reads returning empty strings and writes +#dropping queued data. +# +# @block: Would make the session block until data was available +# or the queue had space available. +# +# Note: The option 'block' is not supported now due to the miss +# feature in qmp. Will add it later when we gain the necessary +# infrastructure enhancement. +# +# Since: 1.3 +## +{'enum': 'CongestionControl' + 'data': [ 'drop', 'block' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @data: the source data write to memchar. +# +# @format: #optional the format of the data write to mem
[Qemu-devel] [PATCH 5/5] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp-commands.hx | 23 +++ hmp.c | 53 + hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 95 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 5f91428..f862a53 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -868,7 +868,30 @@ char device and return @var{size} of the data. that specifies behavior when the queue is full/empty. By default is 'drop'. Note that the 'block' option is not supported now. -b for 'block' option. None for 'drop' option. +ETEXI + +{ +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example ETEXI { diff --git a/hmp.c b/hmp.c index fa858c4..bc245f4 100644 --- a/hmp.c +++ b/hmp.c @@ -1245,3 +1245,56 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum CongestionControl control = CONGESTION_CONTROL_DROP; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, + 0, control, &err); +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index 131b325..453c084 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b6e7d95..735bd1b 100644 --- a/monitor.h +++ b/monitor.h @@ -86,6 +86,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
Re: [Qemu-devel] [PATCH 1/5] qemu-char: Add new char backend CircularMemCharDriver
On 10/22/2012 10:08 PM, Eric Blake wrote: On 10/21/2012 10:47 AM, Lei Li wrote: Signed-off-by: Lei Li --- qemu-char.c | 72 +++ 1 files changed, 72 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..b174da1 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2588,6 +2588,78 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemoryr chardev*/ s/CircularMemoryr/CircularMemory/ Yeah, I just found it... Thanks! +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (len < 0) { +return -1; +} + +/* The size should be a power of 2. */ Shouldn't you enforce that, then? Yes, it has been checked when open the CirMemChar backend in patch 2/5, as code below: if (d->size & (d->size -1)) { return NULL; } -- Lei
Re: [Qemu-devel] [PATCH 3/5] QAPI: Introduce memchar-write QMP command
On 10/23/2012 02:37 AM, Luiz Capitulino wrote: On Mon, 22 Oct 2012 00:47:59 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 22 + hmp.c| 19 +++ hmp.h|1 + qapi-schema.json | 69 ++ qemu-char.c | 46 qmp-commands.hx | 40 +++ 6 files changed, 197 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..753aab3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,28 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,control:-b,data:s", +.params = "chardev [-b] data", +.help = "Provide writing interface for CirMemCharDriver. Write" + "'data' to it. (Use -b for 'block' option)", +.mhandler.cmd = hmp_memchar_write, Honest question: is this (and memchr_read) really useful? Isn't the console command alone good enough? +}, + +STEXI +@item memchar_write @var{chardev} [-b] @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. The size of the data write to the driver +should better be power of 2. As this is a human interface, it makes sense to round-up automatically. Actually, you don't even accept a size parameter :) Yeah, I have take your suggestion drop the size parameters by calculating, but I forgot to get rid of the comment here... :-[ + +@var{control} is option('block', 'drop') for read and write command +that specifies behavior when the queue is full/empty. By default is +'drop'. Note that the 'block' option is not supported now. +-b for 'block' option. None for 'drop' option. +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 70bdec2..18ca61b 100644 --- a/hmp.c +++ b/hmp.c @@ -671,6 +671,25 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; +int con = qdict_get_try_bool(qdict, "block", 0); +enum CongestionControl control; +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +control = con ? CONGESTION_CONTROL_BLOCK : CONGESTION_CONTROL_DROP; +qmp_memchar_write(chardev, size, data, true, format, + true, control, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index f9dbdae..a908aa6 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,75 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: DataEncoding? +# +# An enumeration of data format write to or read from +# memchardev. The default value would be utf8. This is generic enough, don't need to mention memchardev. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', will support +# other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @CongestionControl +# +# An enumeration of options for the read and write command that +# specifies behavior when the queue is full/empty. The default +# option would be drop. +# +# @drop: Would result in reads returning empty strings and writes +#dropping queued data. +# +# @block: Would make the session block until data was available +# or the queue had space available. +# +# Note: The option 'block' is not supported now due t
Re: [Qemu-devel] [PATCH 1/5] qemu-char: Add new char backend CircularMemCharDriver
On 10/23/2012 02:14 AM, Luiz Capitulino wrote: On Mon, 22 Oct 2012 00:47:57 +0800 Lei Li wrote: Signed-off-by: Lei Li This patch should be squashed in the next one. More comments below. --- qemu-char.c | 72 +++ 1 files changed, 72 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..b174da1 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2588,6 +2588,78 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemoryr chardev*/ s/Memoryr/Memory + +typedef struct { +size_t size; +size_t producer; +size_t consumer; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer == d->consumer; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return (d->producer - d->consumer) >= d->size; +} + +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (len < 0) { +return -1; +} + +/* The size should be a power of 2. */ +for (i = 0; i < len; i++ ) { +d->cbuf[d->producer % d->size] = buf[i]; +d->producer++; +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +if (len > d->size) { +len = d->size; +} + +for (i = 0; i < len; i++) { +buf[i] = d->cbuf[d->consumer % d->size]; +d->consumer++; +} + +return 0; +} You don't seem to reset producer/consumer anywhere, I wonder if it's possible for a long running VM to trigger the limit here. Yes, it make sense. -- Lei
Re: [Qemu-devel] [PATCH 5/5] HMP: Introduce console command
On 10/23/2012 02:59 AM, Luiz Capitulino wrote: On Mon, 22 Oct 2012 00:48:01 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 23 +++ hmp.c | 53 + hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 95 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 5f91428..f862a53 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -868,7 +868,30 @@ char device and return @var{size} of the data. that specifies behavior when the queue is full/empty. By default is 'drop'. Note that the 'block' option is not supported now. -b for 'block' option. None for 'drop' option. +ETEXI + +{ +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example ETEXI { diff --git a/hmp.c b/hmp.c index fa858c4..bc245f4 100644 --- a/hmp.c +++ b/hmp.c @@ -1245,3 +1245,56 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum CongestionControl control = CONGESTION_CONTROL_DROP; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, + 0, control, &err); +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} Shouldn't you also read from the device? The use-case for this console command is just allow the user to write data to each memchar device as in a signal terminal. Then type escape sequences to take you back to the monitor. So I don't think it is also need to read from the device in this command. BTW, we can read from the device by hmp_memchar_read. :) + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} No need to do this here as the QMP command will do it too. I think we should do this check here, otherwise would cause core dump when 'console' to a chardev that does not exist. Wepass parameterchr->label by qmp_memchar_write() in the handler hmp_read_console. + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index 131b325..453c084 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 &am
Re: [Qemu-devel] [PATCH 5/5] HMP: Introduce console command
On 10/24/2012 08:55 PM, Luiz Capitulino wrote: On Wed, 24 Oct 2012 15:17:21 +0800 Lei Li wrote: On 10/23/2012 02:59 AM, Luiz Capitulino wrote: On Mon, 22 Oct 2012 00:48:01 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 23 +++ hmp.c | 53 + hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 95 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 5f91428..f862a53 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -868,7 +868,30 @@ char device and return @var{size} of the data. that specifies behavior when the queue is full/empty. By default is 'drop'. Note that the 'block' option is not supported now. -b for 'block' option. None for 'drop' option. +ETEXI + +{ +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example ETEXI { diff --git a/hmp.c b/hmp.c index fa858c4..bc245f4 100644 --- a/hmp.c +++ b/hmp.c @@ -1245,3 +1245,56 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum CongestionControl control = CONGESTION_CONTROL_DROP; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, + 0, control, &err); +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} Shouldn't you also read from the device? The use-case for this console command is just allow the user to write data to each memchar device as in a signal terminal. Then type escape sequences to take you back to the monitor. So I don't think it is also need to read from the device in this command. BTW, we can read from the device by hmp_memchar_read. :) And how is the console command better than the hmp_memchar_write one? Could you please give examples? Sure. The "console" command behaves like: (qemu) console foo foo: hello world (qemu) you can input data, then put enter or ctrl-] to return back to the monitor. The data input from console would be written to the CirMemCharDriver backend, which provide a more friendly and nice HMP command as Anthony suggested. + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} No need to do this here as the QMP command will do it too. I think we should do this check here, otherwise would cause core dump when 'console' to a chardev that does not exist. Wepass parameterchr->label by qmp_memchar_write() in the handler hmp_read_console. Do you need the chr object? Why don't you just pass 'device' to monitor_read_console()? Yes, we need 'device' and 'chr' both here. 'device' pass to monitor_read_console for the friendly prompt. 'chr' pass to handler hmp_read_console, the reason did not pass 'device' directly to it is the limitation of argument for the handler hmp_read_console. Also it's better to give the prompt before the user enter the input console if the device not found, the use-case as I post above. + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +
[Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 136 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 149 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..45d2a86 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2588,6 +2589,130 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemoryr chardev*/ + +typedef struct { +size_t size; +size_t producer; +size_t consumer; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer == d->consumer; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return (d->producer - d->consumer) >= d->size; +} + +static bool cirmem_chr_unlimit(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer >= d->consumer; +} + +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++ ) { +if (cirmem_chr_unlimit(chr)) { +d->cbuf[d->producer % d->size] = buf[i]; +d->producer++; +} else { +return -1; +} +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +if (len > d->size) { +len = d->size; +} + +for (i = 0; i < len; i++) { +if (cirmem_chr_unlimit(chr)) { +buf[i] = d->cbuf[d->consumer % d->size]; +d->consumer++; + +if (cirmem_chr_is_empty(chr)) { +break; +} +} else { +return -1; +} +} + +return 0; +} + +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)) { +goto fail; +} + +d->producer = 0; +d->consumer = 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2652,6 +2777,16 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2725,6 +2860,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 = "memchr",.open = qemu_chr_open_cirmemchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, diff --git a/qemu-config.c b/qemu-config.c index cd1ec21..5553bb6 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -213,6 +213,9 @@ static QemuOptsList qemu_chardev_opts = { },{
[Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 17 + hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 47 +++ qemu-char.c | 44 qmp-commands.hx | 34 ++ 6 files changed, 158 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..a37b8e9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,23 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. Note that we will add 'control' options +for read and write command that specifies behavior when the queue +is full/empty, for now just assume a drop behaver in these two commands. + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 2b97982..082985b 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,21 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +qmp_memchar_write(chardev, size, data, true, format, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index c615ee2..43ef6bc 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,53 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format. The default value would +# be utf8. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', +# will support other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @data: the source data write to memchar. +# +# @format: #optional the format of the data write to memchardev, by +# default is 'utf8'. +# +# Returns: Nothing on success +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. +# +# Since: 1.3 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 45d2a86..78fc634 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2713,6 +2713,50 @@ fail: return NULL; } +void qmp_memchar_write(const char *chardev, 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(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return; +} + +if (strcmp(chr-&
[Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 19 ++ hmp.h|1 + qapi-schema.json | 27 ++ qemu-char.c | 55 ++ qmp-commands.hx | 40 +++ 6 files changed, 161 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a37b8e9..df294eb 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -842,6 +842,25 @@ is full/empty, for now just assume a drop behaver in these two commands. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 082985b..ef85736 100644 --- a/hmp.c +++ b/hmp.c @@ -698,6 +698,25 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +enum DataFormat format; +Error *errp = NULL; + +format = DATA_FORMAT_UTF8; +data = qmp_memchar_read(chardev, size, true, format, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 406ebb1..a5a0cfe 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 43ef6bc..a8c9430 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -372,6 +372,33 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 78fc634..a7c5ea2 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2757,6 +2757,61 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; + +read_data = g_malloc0(size); + +chr = qemu_chr_find(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +goto fail; +} + +if (strcmp(chr->filename, "memchr") != 0) { +error_setg(errp,"The %s is not memory char device\n", chardev); +goto fail; +} + +/* XXX: For the sync command as 'block', waiting for the qmp + * to support necessary feature. Now just act as 'drop'. */ +if (cirmem_chr_is_empty(chr)) { +error_setg(err
[Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 136 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 149 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..45d2a86 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2588,6 +2589,130 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemory chardev*/ + +typedef struct { +size_t size; +size_t producer; +size_t consumer; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer == d->consumer; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return (d->producer - d->consumer) >= d->size; +} + +static bool cirmem_chr_unlimit(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer >= d->consumer; +} + +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++ ) { +if (cirmem_chr_unlimit(chr)) { +d->cbuf[d->producer % d->size] = buf[i]; +d->producer++; +} else { +return -1; +} +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +if (len > d->size) { +len = d->size; +} + +for (i = 0; i < len; i++) { +if (cirmem_chr_unlimit(chr)) { +buf[i] = d->cbuf[d->consumer % d->size]; +d->consumer++; + +if (cirmem_chr_is_empty(chr)) { +break; +} +} else { +return -1; +} +} + +return 0; +} + +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)) { +goto fail; +} + +d->producer = 0; +d->consumer = 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2652,6 +2777,16 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2725,6 +2860,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 = "memchr",.open = qemu_chr_open_cirmemchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, diff --git a/qemu-config.c b/qemu-config.c index cd1ec21..5553bb6 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -213,6 +213,9 @@ static QemuOptsList qemu_chardev_opts = { },{
[Qemu-devel] [PATCH 0/4 V5] char: Add CirMemCharDriver and provide QMP interface
This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memchr,id=foo,maxcapacity=640k -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo foo: Input data Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Please comment and let me know if there is anything else need to be improved. Your suggestion would be very appreciated! Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (4): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 17 + hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 47 +++ qemu-char.c | 44 qmp-commands.hx | 34 ++ 6 files changed, 158 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..a37b8e9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,23 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. Note that we will add 'control' options +for read and write command that specifies behavior when the queue +is full/empty, for now just assume a drop behaver in these two commands. + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 2b97982..082985b 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,21 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +qmp_memchar_write(chardev, size, data, true, format, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index c615ee2..43ef6bc 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,53 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format. The default value would +# be utf8. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', +# will support other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @data: the source data write to memchar. +# +# @format: #optional the format of the data write to memchardev, by +# default is 'utf8'. +# +# Returns: Nothing on success +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. +# +# Since: 1.3 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 45d2a86..78fc634 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2713,6 +2713,50 @@ fail: return NULL; } +void qmp_memchar_write(const char *chardev, 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(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return; +} + +if (strcmp(chr-&
[Qemu-devel] [PATCH 4/4] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp-commands.hx | 25 + hmp.c | 52 hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 96 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index df294eb..7cba42c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -861,6 +861,31 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ef85736..d716410 100644 --- a/hmp.c +++ b/hmp.c @@ -1255,3 +1255,55 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index d17ae2d..7e90115 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b6e7d95..735bd1b 100644 --- a/monitor.h +++ b/monitor.h @@ -86,6 +86,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
[Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 19 ++ hmp.h|1 + qapi-schema.json | 27 ++ qemu-char.c | 55 ++ qmp-commands.hx | 40 +++ 6 files changed, 161 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a37b8e9..df294eb 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -842,6 +842,25 @@ is full/empty, for now just assume a drop behaver in these two commands. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 082985b..ef85736 100644 --- a/hmp.c +++ b/hmp.c @@ -698,6 +698,25 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +enum DataFormat format; +Error *errp = NULL; + +format = DATA_FORMAT_UTF8; +data = qmp_memchar_read(chardev, size, true, format, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 406ebb1..a5a0cfe 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 43ef6bc..a8c9430 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -372,6 +372,33 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 78fc634..a7c5ea2 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2757,6 +2757,61 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; + +read_data = g_malloc0(size); + +chr = qemu_chr_find(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +goto fail; +} + +if (strcmp(chr->filename, "memchr") != 0) { +error_setg(errp,"The %s is not memory char device\n", chardev); +goto fail; +} + +/* XXX: For the sync command as 'block', waiting for the qmp + * to support necessary feature. Now just act as 'drop'. */ +if (cirmem_chr_is_empty(chr)) { +error_setg(err
[Qemu-devel] [PATCH 4/4] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp-commands.hx | 25 + hmp.c | 52 hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 96 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index df294eb..7cba42c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -861,6 +861,31 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ef85736..d716410 100644 --- a/hmp.c +++ b/hmp.c @@ -1255,3 +1255,55 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index d17ae2d..7e90115 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b6e7d95..735bd1b 100644 --- a/monitor.h +++ b/monitor.h @@ -86,6 +86,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
[Qemu-devel] [PATCH 0/4 V5] char: Add CirMemCharDriver and provide QMP interface
This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memchr,id=foo,maxcapacity=640k -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo foo: Input data Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Please comment and let me know if there is anything else need to be improved. Your suggestion would be very appreciated! Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (4): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-)
Re: [Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
On 10/26/2012 04:05 AM, Eric Blake wrote: On 10/25/2012 01:54 PM, Lei Li wrote: Signed-off-by: Lei Li --- qemu-char.c | 136 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 149 insertions(+), 0 deletions(-) +/*/ +/*CircularMemory chardev*/ + Better; I'll assume the other thread also title v5 was a mis-send. Yeah, you are right. Sorry for the mis-sending... :-[ +Create a circular buffer with fixed size indicated by optionally @option{maxcapacity} +which will be default 64K if it is not given. + +@option{maxcapacity} specify the max capacity of the size of circular buffer +want to create. Should be power of 2. Still, this grammar could be improved: s/specify/specifies/ s/want// Sure, thanks! -- Lei
Re: [Qemu-devel] [PATCH 0/4 V5] char: Add CirMemCharDriver and provide QMP interface
Please ignore this thread and review the other thread also titled V5. Sorry for the mis-sending mostly caused by the unconsciousness at midnight... On 10/26/2012 03:48 AM, Lei Li wrote: This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memchr,id=foo,maxcapacity=640k -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo foo: Input data Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Please comment and let me know if there is anything else need to be improved. Your suggestion would be very appreciated! Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (4): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-) -- Lei
[Qemu-devel] [PATCH 0/4 V6] char: Add CirMemCharDriver and provide QMP interface
This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memchr,id=foo,maxcapacity=640k -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo foo: Input data Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Please comment and let me know if there is anything else need to be improved. Your suggestion would be very appreciated! Changes since v5: - Avoid writing the IAC information to the queue. - Grammar of the doc for command line options improved from Eric. Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (4): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 140 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 153 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..c3ec43d 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2588,6 +2589,134 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemory chardev*/ + +typedef struct { +size_t size; +size_t producer; +size_t consumer; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer == d->consumer; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return (d->producer - d->consumer) >= d->size; +} + +static bool cirmem_chr_unlimit(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer >= d->consumer; +} + +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++ ) { +if (cirmem_chr_unlimit(chr)) { +/* Avoid writing the IAC information to the queue. */ +if ((unsigned char)buf[i] == IAC) { +continue; +} +d->cbuf[d->producer % d->size] = buf[i]; +d->producer++; +} else { +return -1; +} +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +if (len > d->size) { +len = d->size; +} + +for (i = 0; i < len; i++) { +if (cirmem_chr_unlimit(chr)) { +buf[i] = d->cbuf[d->consumer % d->size]; +d->consumer++; + +if (cirmem_chr_is_empty(chr)) { +break; +} +} else { +return -1; +} +} + +return 0; +} + +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)) { +goto fail; +} + +d->producer = 0; +d->consumer = 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2652,6 +2781,16 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2725,6 +2864,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 = "memchr",.open = qemu_chr_open_cirmemchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, diff --git a/qemu-config.c b/qemu-config.c i
[Qemu-devel] [PATCH 4/4] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp-commands.hx | 25 + hmp.c | 52 hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 96 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index df294eb..7cba42c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -861,6 +861,31 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ef85736..d716410 100644 --- a/hmp.c +++ b/hmp.c @@ -1255,3 +1255,55 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index d17ae2d..7e90115 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b6e7d95..735bd1b 100644 --- a/monitor.h +++ b/monitor.h @@ -86,6 +86,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
[Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 17 + hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 47 +++ qemu-char.c | 44 qmp-commands.hx | 34 ++ 6 files changed, 158 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..a37b8e9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,23 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. Note that we will add 'control' options +for read and write command that specifies behavior when the queue +is full/empty, for now just assume a drop behaver in these two commands. + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 2b97982..082985b 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,21 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +qmp_memchar_write(chardev, size, data, true, format, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index c615ee2..43ef6bc 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,53 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format. The default value would +# be utf8. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', +# will support other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @data: the source data write to memchar. +# +# @format: #optional the format of the data write to memchardev, by +# default is 'utf8'. +# +# Returns: Nothing on success +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. +# +# Since: 1.3 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index c3ec43d..6114e29 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2717,6 +2717,50 @@ fail: return NULL; } +void qmp_memchar_write(const char *chardev, 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(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return; +} + +if (strcmp(chr-&
[Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 19 ++ hmp.h|1 + qapi-schema.json | 27 ++ qemu-char.c | 55 ++ qmp-commands.hx | 40 +++ 6 files changed, 161 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a37b8e9..df294eb 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -842,6 +842,25 @@ is full/empty, for now just assume a drop behaver in these two commands. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 082985b..ef85736 100644 --- a/hmp.c +++ b/hmp.c @@ -698,6 +698,25 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +enum DataFormat format; +Error *errp = NULL; + +format = DATA_FORMAT_UTF8; +data = qmp_memchar_read(chardev, size, true, format, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 406ebb1..a5a0cfe 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 43ef6bc..a8c9430 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -372,6 +372,33 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 6114e29..cf88f71 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2761,6 +2761,61 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; + +read_data = g_malloc0(size); + +chr = qemu_chr_find(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +goto fail; +} + +if (strcmp(chr->filename, "memchr") != 0) { +error_setg(errp,"The %s is not memory char device\n", chardev); +goto fail; +} + +/* XXX: For the sync command as 'block', waiting for the qmp + * to support necessary feature. Now just act as 'drop'. */ +if (cirmem_chr_is_empty(chr)) { +error_setg(err
Re: [Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
On 10/27/2012 01:39 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:51 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 19 ++ hmp.h|1 + qapi-schema.json | 27 ++ qemu-char.c | 55 ++ qmp-commands.hx | 40 +++ 6 files changed, 161 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a37b8e9..df294eb 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -842,6 +842,25 @@ is full/empty, for now just assume a drop behaver in these two commands. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 082985b..ef85736 100644 --- a/hmp.c +++ b/hmp.c @@ -698,6 +698,25 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +enum DataFormat format; You don't need this variable. ok. +Error *errp = NULL; + +format = DATA_FORMAT_UTF8; +data = qmp_memchar_read(chardev, size, true, format, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 406ebb1..a5a0cfe 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 43ef6bc..a8c9430 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -372,6 +372,33 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. Please, replace this note with an explanation of the current behavior. No need to talk about the future. ok. +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 6114e29..cf88f71 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2761,6 +2761,61 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; + +read_data = g_malloc0(size); This is unsafe, as qmp clients could pass any value here. You should check the number of available bytes and allocate only that. This size is not the init size of ring buffer, it's the size user want to read from the ci
Re: [Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
On 10/27/2012 01:17 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:50 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 17 + hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 47 +++ qemu-char.c | 44 qmp-commands.hx | 34 ++ 6 files changed, 158 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..a37b8e9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,23 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. Note that we will add 'control' options +for read and write command that specifies behavior when the queue +is full/empty, for now just assume a drop behaver in these two commands. You can drop everything after "Note". ok + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 2b97982..082985b 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,21 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; Why do you need this variable? Sure, I will drop this. +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +qmp_memchar_write(chardev, size, data, true, format, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index c615ee2..43ef6bc 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,53 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format. The default value would +# be utf8. Please, remove the "default value" part. This is decided by the command using this type. Now the option format is optional, if it's not set then default by 'utf8'. I think it's a reasonable behaver. :) +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', +# will support other data format as well. Please, drop this note. It's not needed. ok +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @data: the source data write to memchar. +# +# @format: #optional the format of the data write to memchardev, by +# default is 'utf8'. +# +# Returns: Nothing on success +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. Please, replace this note with an explanation of the current behavior. No need to talk about the future. Sure. +# +# Since: 1.3 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + +## #
Re: [Qemu-devel] [PATCH 4/4] HMP: Introduce console command
On 10/27/2012 01:43 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:52 +0800 Lei Li wrote: Signed-off-by: Lei Li I still don't understand how this command, in its current form, is different from memchar-write. One more comment below. Hi Luiz, Yes, I have replied to it in patch series v4. You can look at it as link below: https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04568.html --- hmp-commands.hx | 25 + hmp.c | 52 hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 96 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index df294eb..7cba42c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -861,6 +861,31 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ef85736..d716410 100644 --- a/hmp.c +++ b/hmp.c @@ -1255,3 +1255,55 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} As I said before, I don't why chr is needed. It seems to me that passing 'device' down is enough. + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index d17ae2d..7e90115 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b6e7d95..735bd1b 100644 --- a/monitor.h +++ b/monitor.h @@ -86,6 +86,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- Lei
Re: [Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
On 10/27/2012 12:47 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:49 +0800 Lei Li wrote: Signed-off-by: Lei Li --- qemu-char.c | 140 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 153 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index b082bae..c3ec43d 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2588,6 +2589,134 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemory chardev*/ + +typedef struct { +size_t size; +size_t producer; +size_t consumer; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer == d->consumer; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return (d->producer - d->consumer) >= d->size; +} + +static bool cirmem_chr_unlimit(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->producer >= d->consumer; +} I'm not sure I get why this is needed. Are you trying to fix the problem I mentioned in my last review that producer and consumer might overflow for long running VMs? If yes, then the problem still exists. The only fix I can think of is to re-initialize them. Of course that you could do it in a way that they keep pointing to the right position in cbuf[]. + +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++ ) { +if (cirmem_chr_unlimit(chr)) { +/* Avoid writing the IAC information to the queue. */ +if ((unsigned char)buf[i] == IAC) { +continue; +} +d->cbuf[d->producer % d->size] = buf[i]; +d->producer++; +} else { +return -1; +} +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +if (len > d->size) { +len = d->size; +} + +for (i = 0; i < len; i++) { +if (cirmem_chr_unlimit(chr)) { +buf[i] = d->cbuf[d->consumer % d->size]; +d->consumer++; + +if (cirmem_chr_is_empty(chr)) { +break; +} +} else { +return -1; +} +} + +return 0; +} + +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 this is enforced in vl.c (which I can't remember right now), then you should turn this into an assert(). It is just enforced in here, not in vl.c. +if (d->size & (d->size - 1)) { +goto fail; +} + +d->producer = 0; +d->consumer = 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2652,6 +2781,16 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; Duplication? Yeah... +} if (strstart(filename, "tcp:", &p)
[Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 131 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 144 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index afe2bfb..11c320f 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2598,6 +2599,130 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemory chardev*/ + +typedef struct { +size_t size; +size_t head; +size_t count; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->count == 0; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->count == d->size; +} + +static size_t qemu_chr_cirmem_count(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->count; +} + +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; +int tail; + +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; +} + +tail = (d->head + d->count) % d->size; +d->cbuf[tail] = buf[i]; +if (d->count == d->size) { +d->head = (d->head + 1) % d->size; +} else { +++d->count; +} +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +for (i = 0; i < len; i++) { +buf[i] = d->cbuf[d->head]; +d->head = (d->head + 1) % d->size; +d->count--; + +if (cirmem_chr_is_empty(chr)) { +break; +} +} + +return 0; +} + +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)) { +goto fail; +} + +d->head = 0; +d->count = 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2662,6 +2787,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memory", &p)) { +qemu_opt_set(opts, "backend", "memory"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2735,6 +2865,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 }, diff --git a/qemu-config.c b/qemu-config.c index e854fff..8fa0022 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -213,6 +213,9 @@ static QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, +},{ +.name = "maxcapacity", +.type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, diff --g
[Qemu-devel] [PATCH 4/4] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp-commands.hx | 21 + hmp.c | 51 +++ hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 91 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index c79c9d3..35e0646 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -874,6 +874,27 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by 'ctrl-]' or enter. + +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 0465c42..ec08377 100644 --- a/hmp.c +++ b/hmp.c @@ -1289,3 +1289,54 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, 0, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 6289037..0d384b5 100644 --- a/hmp.h +++ b/hmp.h @@ -79,5 +79,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index eeef32e..5ef9cbe 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b4ef955..5f584b9 100644 --- a/monitor.h +++ b/monitor.h @@ -87,6 +87,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
Re: [Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
On 10/29/2012 09:23 PM, Luiz Capitulino wrote: On Mon, 29 Oct 2012 12:10:24 +0800 Lei Li wrote: On 10/27/2012 01:17 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:50 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 17 + hmp.c| 15 +++ hmp.h|1 + qapi-schema.json | 47 +++ qemu-char.c | 44 qmp-commands.hx | 34 ++ 6 files changed, 158 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index e0b537d..a37b8e9 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -825,6 +825,23 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device. Note that we will add 'control' options +for read and write command that specifies behavior when the queue +is full/empty, for now just assume a drop behaver in these two commands. You can drop everything after "Note". ok + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 2b97982..082985b 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,21 @@ 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, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +enum DataFormat format; Why do you need this variable? Sure, I will drop this. +Error *errp = NULL; + +size = strlen(data); +format = DATA_FORMAT_UTF8; +qmp_memchar_write(chardev, size, data, true, format, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 71ea384..406ebb1 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index c615ee2..43ef6bc 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,53 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format. The default value would +# be utf8. Please, remove the "default value" part. This is decided by the command using this type. Now the option format is optional, if it's not set then default by 'utf8'. I think it's a reasonable behaver. :) What I meant is that the right place to say what the value is is in the command documentation, not in @DataFormat doc. Here, only having "An enumeration of data encodings" is fine. OK, done. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', +# will support other data format as well. Please, drop this note. It's not needed. ok +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to memchar +# char device. +# +# @chardev: the name of the memchar char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @data: the source data write to memchar. +# +# @format: #optional the format of the data write to memchardev, by +# default is 'utf8'. +# +# Returns: Nothing on success +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. Please, replace this note with an explanation of the current behavior. No ne
Re: [Qemu-devel] [PATCH 4/4] HMP: Introduce console command
On 10/29/2012 09:26 PM, Luiz Capitulino wrote: On Mon, 29 Oct 2012 12:18:03 +0800 Lei Li wrote: On 10/27/2012 01:43 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:52 +0800 Lei Li wrote: Signed-off-by: Lei Li I still don't understand how this command, in its current form, is different from memchar-write. One more comment below. Hi Luiz, Yes, I have replied to it in patch series v4. You can look at it as link below: https://lists.gnu.org/archive/html/qemu-devel/2012-10/msg04568.html Unfortunately your answer doesn't answer my (honest) question. Can you actually show me how the console command is better than the memchar-write one? Maybe I'm missing something obvious... As the example I post in the link, I think the main benefit for console command is that it's more friendly. A simple example, when we try to input "hello world" to this char device, the memchar_write command would like this: (qemu) memchar_write foo "hello world" We need to add quotation marks for the string due to the monitor behaver.(Would treat as another argument for the string which has space in it.) The console command can avoid this like: (qemu) console foo foo: hello world \r / ^] (qemu) It drops you into an interactive mode where it's like your at the serial console and \r or ctrl-] escape sequences takes you back to the monitor suggested by Anthony. I hope this can answer your question. :) --- hmp-commands.hx | 25 + hmp.c | 52 hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 96 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index df294eb..7cba42c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -861,6 +861,31 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.help = "Connect to the serial console from within the" + "monitor, allow to write data to memchardev" + "'chardev'. Exit from the console and return back" + "to monitor by typing 'ctrl-]'", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console + +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by typing 'ctrl-]'. +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ef85736..d716410 100644 --- a/hmp.c +++ b/hmp.c @@ -1255,3 +1255,55 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} As I said before, I don't why chr is needed. It seems to me that passing 'device' down is enough. + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index a5a0cfe..5b54a79 100644 --- a/hmp.h +++ b/hmp.h @@ -77,5 +77,6 @@ void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif
Re: [Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
On 10/29/2012 09:17 PM, Luiz Capitulino wrote: On Mon, 29 Oct 2012 12:09:38 +0800 Lei Li wrote: On 10/27/2012 01:39 AM, Luiz Capitulino wrote: On Fri, 26 Oct 2012 19:21:51 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 19 ++ hmp.h|1 + qapi-schema.json | 27 ++ qemu-char.c | 55 ++ qmp-commands.hx | 40 +++ 6 files changed, 161 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a37b8e9..df294eb 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -842,6 +842,25 @@ is full/empty, for now just assume a drop behaver in these two commands. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 082985b..ef85736 100644 --- a/hmp.c +++ b/hmp.c @@ -698,6 +698,25 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +enum DataFormat format; You don't need this variable. ok. +Error *errp = NULL; + +format = DATA_FORMAT_UTF8; +data = qmp_memchar_read(chardev, size, true, format, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 406ebb1..a5a0cfe 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 43ef6bc..a8c9430 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -372,6 +372,33 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from memchar +# char device and return the data. +# +# @chardev: the name of the memchar 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: The option 'block' is not supported now due to the miss +#feature in qmp. Will add it later when we gain the necessary +#infrastructure enhancement. For now just assume 'drop' behaver +#for this command. Please, replace this note with an explanation of the current behavior. No need to talk about the future. ok. +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 6114e29..cf88f71 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2761,6 +2761,61 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; + +read_data = g_malloc0(size); This is unsafe, as qmp clients could pass any value here. You should check the number of availabl
[Qemu-devel] [PATCH 0/4 V7] char: Add CirMemCharDriver and provide QMP interface
This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memory,id=foo,maxcapacity=65536 -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo foo: Input data Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Please comment and let me know if there is anything else need to be improved. Your suggestion would be very appreciated! Changes since v6: - Improve the document based on Luiz's comments. - Keep pointing to the right position in cbuf for the case producer and consumer might overflow for long running VMs pointed by Luiz. - Limit the size of read_data to the amount of bytes available in the circular buffer. - Other fixups from Luiz. Changes since v5: - Avoid writing the IAC information to the queue. - Grammar of the doc for command line options improved from Eric. Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (4): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 15 +++ hmp.c| 13 + hmp.h|1 + qapi-schema.json | 41 + qemu-char.c | 48 qmp-commands.hx | 34 ++ 6 files changed, 152 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index f916385..305b0d7 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -840,6 +840,21 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to char device 'memory'. + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 895a343..1ffb3d6 100644 --- a/hmp.c +++ b/hmp.c @@ -682,6 +682,19 @@ 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, "chardev"); +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); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 34eb2b3..6c3ec45 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index 542e3ac..f0acaac 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,47 @@ { '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.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @memchar-write: +# +# Provide writing interface for memchardev. Write data to char +# device 'memory'. +# +# @chardev: the name of the memory char device. +# +# @size: the size to write in bytes. Should be power of 2. +# +# @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 @chardev is not a valid char device, DeviceNotFound +# +# Notes: For now assume 'drop' behaver, which would result in writes +#dropping queued data. +# +# Since: 1.3 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 11c320f..92b4cec 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2723,6 +2723,54 @@ fail: return NULL; } +static bool qemu_is_chr(const CharDriverState *chr, const char *filename) +{ +return strcmp(chr->filename, filename); +} + +void qmp_memchar_write(const char *chardev, 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(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return; +} + +if (qemu_is_chr(chr, "memory")) { +error_setg(errp,"%s is not memory char device\n", chardev); +return; +} + +/* XXX: Drop the coming data when the buffer is full. */ +if (cirmem_chr_is_full(chr)) { +error_setg(errp, "Memory device %s is full", chardev); +return; +} + +write_count = (gsize)size; + +if (has_format && (format == DATA_FORMAT_B
[Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 17 hmp.h|1 + qapi-schema.json | 27 - qemu-char.c | 57 ++ qmp-commands.hx | 34 6 files changed, 154 insertions(+), 1 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 305b0d7..c79c9d3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -855,6 +855,25 @@ to char device 'memory'. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from char device +'memory' and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 1ffb3d6..0465c42 100644 --- a/hmp.c +++ b/hmp.c @@ -695,6 +695,23 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +Error *errp = NULL; + +data = qmp_memchar_read(chardev, size, false, 0, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 6c3ec45..6289037 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index f0acaac..0696902 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -346,7 +346,7 @@ # # @chardev: the name of the memory char device. # -# @size: the size to write in bytes. Should be power of 2. +# @size: the size to write in bytes. # # @data: the source data write to memchar. # @@ -366,6 +366,31 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from the char +# device 'memory' and return the data. +# +# @chardev: 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: For now assume 'drop' behaver, which would result in reads +#returning empty strings. +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 92b4cec..a16276a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2771,6 +2771,63 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; +size_t count; + +chr = qemu_chr_find(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return NULL; +} + +if (qemu_is_chr(chr, "memory")) { +error_setg(errp,"%s is not memory char device\n", chardev); +return NULL; +} + +if (size <= 0) { +error_setg(errp, "Failed to read from device %s", chardev); +return NULL; +} + +/* XXX: Return the empty strings when the buffer is empty. */ +if (cirmem_chr_is_empty(chr)) { +erro
[Qemu-devel] [Bug 1086745] Re: serial port data THRE comes too early
Could you please give more details, like the steps to reproduce this problems. Thanks. -- You received this bug notification because you are a member of qemu- devel-ml, which is subscribed to QEMU. https://bugs.launchpad.net/bugs/1086745 Title: serial port data THRE comes too early Status in QEMU: New Bug description: When using a serial port with a Linux guest (and host) and the application uses hardware handshake, this fails because the handling of TEMT and/or THRE is not operating properly in such cases. As long as it takes _time_ for the 'real' port to output the data TEMT may not return true. After writing characters to a real port, the driver should timeout the transmission and after the total time expired, TEMT can be set. Some applications i.e. with a simplex modem do: RTS_on, WRITE_data, repeat IOCTL(GET_LSR_INFO), RTS_off, READ_data. At the moment this fails because very early in the transmission, GET_LSR_INFO returns true and the modem transmitter is switched off. I looked in the source (git) and found that 'char_transmit_time' is present. My skills fail to implement it myself. I build and ran the latest git version and found it to fail as decribed above. I hope someone can solve it. To manage notifications about this bug go to: https://bugs.launchpad.net/qemu/+bug/1086745/+subscriptions
[Qemu-devel] [PATCH 1/4] qemu-char: Add new char backend CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 131 +++ qemu-config.c |3 + qemu-options.hx | 10 3 files changed, 144 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 242b799..3e45ce6 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -99,6 +99,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2599,6 +2600,130 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemory chardev*/ + +typedef struct { +size_t size; +size_t head; +size_t count; +uint8_t *cbuf; +} CirMemCharDriver; + +static bool cirmem_chr_is_empty(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->count == 0; +} + +static bool cirmem_chr_is_full(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->count == d->size; +} + +static size_t qemu_chr_cirmem_count(const CharDriverState *chr) +{ +const CirMemCharDriver *d = chr->opaque; + +return d->count; +} + +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; +int tail; + +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; +} + +tail = (d->head + d->count) % d->size; +d->cbuf[tail] = buf[i]; +if (d->count == d->size) { +d->head = (d->head + 1) % d->size; +} else { +++d->count; +} +} + +return 0; +} + +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CirMemCharDriver *d = chr->opaque; +int i; + +if (cirmem_chr_is_empty(chr) || len < 0) { +return -1; +} + +for (i = 0; i < len; i++) { +buf[i] = d->cbuf[d->head]; +d->head = (d->head + 1) % d->size; +d->count--; + +if (cirmem_chr_is_empty(chr)) { +break; +} +} + +return 0; +} + +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)) { +goto fail; +} + +d->head = 0; +d->count = 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2663,6 +2788,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memory", &p)) { +qemu_opt_set(opts, "backend", "memory"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2736,6 +2866,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 }, diff --git a/qemu-config.c b/qemu-config.c index 10d1ba4..ddc2a2a 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -217,6 +217,9 @@ static QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, +},{ +.name = "maxcapacity", +.type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, diff --g
[Qemu-devel] [PATCH 0/4 V8] char: Add CirMemCharDriver and provide QMP interface
This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memory,id=foo,maxcapacity=65536 -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo foo: Input data Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Since this patch series have been for mailing list for some time and missed 1.3, rebase it with minor fix. Changes since v7: - Rebase the code and fix the format error pointed by Eric. - Modify the version info. Changes since v6: - Improve the document based on Luiz's comments. - Keep pointing to the right position in cbuf for the case producer and consumer might overflow for long running VMs pointed by Luiz. - Limit the size of read_data to the amount of bytes available in the circular buffer. - Other fixups from Luiz. Changes since v5: - Avoid writing the IAC information to the queue. - Grammar of the doc for command line options improved from Eric. Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (4): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 +++ hmp.c| 99 +++ hmp.h|3 + monitor.c| 15 monitor.h|3 + qapi-schema.json | 96 + qemu-char.c | 217 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 89 + 10 files changed, 607 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 3/4] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 19 ++ hmp.c| 17 hmp.h|1 + qapi-schema.json | 25 +++ qemu-char.c | 57 ++ qmp-commands.hx | 34 6 files changed, 153 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a60ba69..3975e22 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -855,6 +855,25 @@ to char device 'memory'. ETEXI { +.name = "memchar_read", +.args_type = "chardev:s,size:i", +.params = "chardev size", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from char device +'memory' and return @var{size} of 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 05b8c21..413012f 100644 --- a/hmp.c +++ b/hmp.c @@ -696,6 +696,23 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +Error *errp = NULL; + +data = qmp_memchar_read(chardev, size, false, 0, &errp); +if (errp) { +monitor_printf(mon, "%s\n", error_get_pretty(errp)); +error_free(errp); +return; +} + +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 3ea9896..22a6646 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index d9fd635..811377c 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -366,6 +366,31 @@ '*format': 'DataFormat'} } ## +# @memchar-read: +# +# Provide read interface for memchardev. Read from the char +# device 'memory' and return the data. +# +# @chardev: 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: The data read from memchar as string +# If @chardev is not a valid memchr device, DeviceNotFound +# +# Notes: For now assume 'drop' behaver, which would result in reads +#returning empty strings. +# +# Since: 1.4 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index a407087..b69b9dc 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2772,6 +2772,63 @@ void qmp_memchar_write(const char *chardev, int64_t size, } } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool has_format, enum DataFormat format, + Error **errp) +{ +CharDriverState *chr; +guchar *read_data; +char *data = NULL; +int ret; +size_t count; + +chr = qemu_chr_find(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return NULL; +} + +if (qemu_is_chr(chr, "memory")) { +error_setg(errp,"%s is not memory char device\n", chardev); +return NULL; +} + +if (size <= 0) { +error_setg(errp, "Failed to read from device %s", chardev); +return NULL; +} + +/* XXX: Return the empty strings when the buffer is empty. */ +if (cirmem_chr_is_empty(chr)) { +error_setg(errp, "Memory device %s is empty", chardev); +return NULL; +} + +count = qemu_chr_cirmem_count(chr); +size = size > count ? count : size; +read_data = g_malloc0(size + 1);
[Qemu-devel] [PATCH 2/4] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 15 +++ hmp.c| 13 + hmp.h|1 + qapi-schema.json | 41 + qemu-char.c | 48 qmp-commands.hx | 34 ++ 6 files changed, 152 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 010b8c9..a60ba69 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -840,6 +840,21 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,data:s", +.params = "chardev data", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{data} +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to char device 'memory'. + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 180ba2b..05b8c21 100644 --- a/hmp.c +++ b/hmp.c @@ -683,6 +683,19 @@ 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, "chardev"); +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); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 0ab03be..3ea9896 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index 5dfa052..d9fd635 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,47 @@ { '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'. +# +# @chardev: 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 @chardev is not a valid char device, DeviceNotFound +# +# Notes: For now assume 'drop' behaver, which would result in writes +#dropping queued data. +# +# Since: 1.4 +## +{ 'command': 'memchar-write', + 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', + '*format': 'DataFormat'} } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 3e45ce6..a407087 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2724,6 +2724,54 @@ fail: return NULL; } +static bool qemu_is_chr(const CharDriverState *chr, const char *filename) +{ +return strcmp(chr->filename, filename); +} + +void qmp_memchar_write(const char *chardev, 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(chardev); +if (!chr) { +error_set(errp, QERR_DEVICE_NOT_FOUND, chardev); +return; +} + +if (qemu_is_chr(chr, "memory")) { +error_setg(errp,"%s is not memory char device\n", chardev); +return; +} + +/* XXX: Drop the coming data when the buffer is full. */ +if (cirmem_chr_is_full(chr)) { +error_setg(errp, "Memory device %s is full", chardev); +return; +} + +write_count = (gsize)size; + +if (has_format && (format == DATA_FORMAT_BASE64)) { +w
[Qemu-devel] [PATCH 4/4] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp-commands.hx | 21 + hmp.c | 52 hmp.h |1 + monitor.c | 15 +++ monitor.h |3 +++ 5 files changed, 92 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 3975e22..9ec5ed7 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -874,6 +874,27 @@ if the requested size is larger than it. ETEXI { +.name = "console", +.args_type = "chardev:s", +.params = "chardev", +.mhandler.cmd = hmp_console, +}, + +STEXI +@item console @var{device} +@findex console +Connect to the serial console from within the monitor, allow to write data +to memchardev @var{chardev}. Exit from the console and return back to +monitor by 'ctrl-]' or enter. + +@example +(qemu) console foo +foo: data string... +@end example + +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 413012f..a6c053c 100644 --- a/hmp.c +++ b/hmp.c @@ -1365,3 +1365,55 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) qmp_nbd_server_stop(&errp); hmp_handle_error(mon, &errp); } + +enum escape_char +{ +ESCAPE_CHAR_CTRL_GS = 0x1d /* ctrl-] used for escape */ +}; + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum escape_char console_escape = ESCAPE_CHAR_CTRL_GS; + +Error *err = NULL; + +if (*data == console_escape) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, 0, &err); + +if (err) { +monitor_printf(mon, "%s\n", error_get_pretty(err)); +monitor_read_command(mon,1); +error_free(err); +return; +} + +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +goto out; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} + +out: +hmp_handle_error(mon, &err); +} diff --git a/hmp.h b/hmp.h index 22a6646..9082324 100644 --- a/hmp.h +++ b/hmp.h @@ -82,5 +82,6 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict); void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); +void hmp_console(Monitor *mon, const QDict *qdict); #endif diff --git a/monitor.c b/monitor.c index c0e32d6..695a19a 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,21 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) { +return -1; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); + +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index b4ef955..5f584b9 100644 --- a/monitor.h +++ b/monitor.h @@ -87,6 +87,9 @@ ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); + int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
[Qemu-devel] [PATCH] qemu-char: inheriting ptys and imporve output from -serial pty
When controlling a qemu instance from another program, it's hard to know which serial port or monitor device is redirected to which pty. With more than one device using "pty" a lot of guesswork is involved. $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device redirected to /dev/pts/5 char device redirected to /dev/pts/6 char device redirected to /dev/pts/7 Although we can find out what everything else is connected to by the "info chardev" with "-monitor stdio" in the command line, It'd be very useful to be able to have qemu inherit pseudo-tty file descriptors so they could just be specified on the command line like: $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device compat_monitor0 redirected to /dev/pts/5 char device serial0 redirected to /dev/pts/6 char device serial1 redirected to /dev/pts/7 Referred link: https://bugs.launchpad.net/qemu/+bug/938552 Reported-by: Craig Ringer Signed-off-by: Lei Li --- qemu-char.c |4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 242b799..2b0f5f4 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -981,6 +981,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) CharDriverState *chr; PtyCharDriver *s; struct termios tty; +char *label; int master_fd, slave_fd, len; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; @@ -1006,7 +1007,8 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr->filename = g_malloc(len); snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); qemu_opt_set(opts, "path", q_ptsname(master_fd)); -fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd)); +label = g_strdup(qemu_opts_id(opts)); +fprintf(stderr, "char device %s redirected to %s\n", label, q_ptsname(master_fd)); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; -- 1.7.7.6
[Qemu-devel] [PATCH] qemu-char: inherit ptys and imporve output from -serial pty
When controlling a qemu instance from another program, it's hard to know which serial port or monitor device is redirected to which pty. With more than one device using "pty" a lot of guesswork is involved. $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device redirected to /dev/pts/5 char device redirected to /dev/pts/6 char device redirected to /dev/pts/7 Although we can find out what everything else is connected to by the "info chardev" with "-monitor stdio" in the command line, It'd be very useful to be able to have qemu inherit pseudo-tty file descriptors so they could just be specified on the command line like: $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device compat_monitor0 redirected to /dev/pts/5 char device serial0 redirected to /dev/pts/6 char device serial1 redirected to /dev/pts/7 Referred link: https://bugs.launchpad.net/qemu/+bug/938552 Reported-by: Craig Ringer Signed-off-by: Lei Li --- qemu-char.c |4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 242b799..2b0f5f4 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -981,6 +981,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) CharDriverState *chr; PtyCharDriver *s; struct termios tty; +char *label; int master_fd, slave_fd, len; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; @@ -1006,7 +1007,8 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr->filename = g_malloc(len); snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); qemu_opt_set(opts, "path", q_ptsname(master_fd)); -fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd)); +label = g_strdup(qemu_opts_id(opts)); +fprintf(stderr, "char device %s redirected to %s\n", label, q_ptsname(master_fd)); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; -- 1.7.7.6
Re: [Qemu-devel] [PATCH] qemu-char: inherit ptys and imporve output from -serial pty
Sorry, s/imporve/improve... On 12/20/2012 10:49 PM, Lei Li wrote: When controlling a qemu instance from another program, it's hard to know which serial port or monitor device is redirected to which pty. With more than one device using "pty" a lot of guesswork is involved. $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device redirected to /dev/pts/5 char device redirected to /dev/pts/6 char device redirected to /dev/pts/7 Although we can find out what everything else is connected to by the "info chardev" with "-monitor stdio" in the command line, It'd be very useful to be able to have qemu inherit pseudo-tty file descriptors so they could just be specified on the command line like: $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device compat_monitor0 redirected to /dev/pts/5 char device serial0 redirected to /dev/pts/6 char device serial1 redirected to /dev/pts/7 Referred link: https://bugs.launchpad.net/qemu/+bug/938552 Reported-by: Craig Ringer Signed-off-by: Lei Li --- qemu-char.c |4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 242b799..2b0f5f4 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -981,6 +981,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) CharDriverState *chr; PtyCharDriver *s; struct termios tty; +char *label; int master_fd, slave_fd, len; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; @@ -1006,7 +1007,8 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr->filename = g_malloc(len); snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); qemu_opt_set(opts, "path", q_ptsname(master_fd)); -fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd)); +label = g_strdup(qemu_opts_id(opts)); +fprintf(stderr, "char device %s redirected to %s\n", label, q_ptsname(master_fd)); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; -- Lei
[Qemu-devel] [PATCH V2] qemu-char: Inherit ptys and improve output from -serial pty
Changes since V1: - Avoid crashing since qemu_opts_id() may return null on some systems according to Markus's suggestion. When controlling a qemu instance from another program, it's hard to know which serial port or monitor device is redirected to which pty. With more than one device using "pty" a lot of guesswork is involved. $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device redirected to /dev/pts/5 char device redirected to /dev/pts/6 char device redirected to /dev/pts/7 Although we can find out what everything else is connected to by the "info chardev" with "-monitor stdio" in the command line, It'd be very useful to be able to have qemu inherit pseudo-tty file descriptors so they could just be specified on the command line like: $ ./x86_64-softmmu/qemu-system-x86_64 -serial pty -serial pty -monitor pty char device compat_monitor0 redirected to /dev/pts/5 char device serial0 redirected to /dev/pts/6 char device serial1 redirected to /dev/pts/7 Referred link: https://bugs.launchpad.net/qemu/+bug/938552 Signed-off-by: Lei Li --- qemu-char.c |8 +++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 6113d0a..31388be 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -980,6 +980,7 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) CharDriverState *chr; PtyCharDriver *s; struct termios tty; +const char *label; int master_fd, slave_fd, len; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; @@ -1005,7 +1006,12 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) chr->filename = g_malloc(len); snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); qemu_opt_set(opts, "path", q_ptsname(master_fd)); -fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd)); + +label = qemu_opts_id(opts); +fprintf(stderr, "char device%s%s redirected to %s\n", +label ? " " : "", +label ?: "", +q_ptsname(master_fd)); s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; -- 1.7.7.6
[Qemu-devel] [RFC] Time resync by qemu-ga
Hi guys, I am working on the time drift issue as background info here. http://mid.gmane.org/87pq5r5otp@codemonkey.ws As Anthony proposed, one part of the solutions is that we want to add a qemu-ga command to resync the guest clock by reading the wallclock time when the tick overflow the limit to the number of missed ticks. When I implement on the qemu-ga side, I got some problems and I'd like to send this RFC out and ask for suggestions and comments to make sure I am in the right direction before I send out the code. The proposed interface for qemu-ga command as link here: http://wiki.qemu.org/Features/GuestAgent/UsefulCommands This maybe easier to implement, but I think this interface may have problem, like the delta issue. So I am trying to implement it like below. The interface looks like: { 'command': 'guest-resync-time' } The way it works: When calling this command in the host, qemu-ga would try to get the wallclock time in the host by handling guest->host commands, and then write this time to the guest hardware. But looks like we don't have the way to get host information now, since qemu-ga just handle host->guest commands (Correct me if I am wrong). To solve this, we should add support to get host information by handling guest->host commands by qemu-ga first. Please correct me if I am wrong. And suggestions and comments are very appreciated, specifically the way to handle quest->host commands by qemu-ga since I am still struggling with this. Thanks! -- Lei
Re: [Qemu-devel] [RESEND PATCH for 1.4 v10 0/3] char: Add CirMemCharDriver and provide QMP interface
On 01/23/2013 11:47 PM, Luiz Capitulino wrote: On Wed, 23 Jan 2013 17:06:09 +0800 Lei Li wrote: Hi Anthony, Resubmit this series with your comments squashed in and Luiz's new comments fixed up. I will push console command part in another thread. There are two bugs in this series. QEMU started with: # qemu [...] -chardev memory,id=foo,maxcapacity=4 This explodes: (qemu) memchar_read foo 10 I'd expect to read '3uiz' in the steps below: (qemu) memchar_write foo luiz3 (qemu) memchar_read foo 10 uiz3, (qemu) Hi Luiz, Thanks very much for your time to test this series! I think the result shows how this algorithm works... Initialize the buffer with maxcapacity 4, CirMemCharDriver->size = 4; This first time you write 5 bytes to the buffer, so producer = 5; consumer = 0; but (producer - consumer) > size: -> consumer = (producer - size) = 1; Then the read action, want to read 10 bytes, but the real size would be adjusted to 4 since in qmp_memchar_read will check if the size wanted to read is larger than the current count in the buffer. (count = producer - consumer) So the read would start at the second bytes in this buffer for that the consumer's value is 1, which is 'u' as the oldest data (Which is circular buffer does, always read (consumer) the oldest data in the buffer). At last, read 4 bytes and return 'uiz3'. - | 3 | u | i | z | - ^ | prod cons I hope that I make it clear. :) Also, please, wait for a discussion to settle before sending a new version, otherwise we'll reach V50 and this won't be merged... -- Lei
Re: [Qemu-devel] [PATCH 1/3] qemu-char: Add new char backend CirMemCharDriver
On 01/23/2013 11:31 PM, Luiz Capitulino wrote: On Wed, 23 Jan 2013 11:15:40 +0800 Lei Li wrote: +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; +} Is the above checks really needed? I don't see other char drivers doing that. No answer. Sorry for that... Yes, I think this check is needed for that we should avoid passing len with negative value and buf with NULL. And other char drivers also did this although not exactly the same, like fd_chr_read, pty_chr_read... + +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]; You never reset d->prod, can't it overflow? +if ((d->prod - d->cons) > d->size) { +d->cons = d->prod - d->size; +} Why is the the if block above needed? This algorithm is Anthony's suggestion which I squashed it in. I think there is no overflow for that we use unsigned, Actually, it can overflow. However, at least on my system the overflow reset the variable to 0. Not sure if this will always be the case, but that's the fix I'd propose anyway. and this if block will adjust the index of product and consumer. I can see that, I asked why it's needed. I'm seeing a bug that might be related to it: (qemu) memchar_write foo luiz3 (qemu) memchar_read foo 10 uiz3, (qemu) I'd expect to read '3uiz' back. -- Lei
Re: [Qemu-devel] [PATCH 3/3] QAPI: Introduce memchar-read QMP command
On 01/23/2013 11:42 PM, Luiz Capitulino wrote: On Wed, 23 Jan 2013 17:06:12 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 21 + hmp.c| 17 + hmp.h|1 + qapi-schema.json | 36 qemu-char.c | 48 qmp-commands.hx | 33 + 6 files changed, 156 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index bcfea11..bdd48f3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -858,6 +858,27 @@ 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 647316a..1f1df5d 100644 --- a/hmp.c +++ b/hmp.c @@ -697,6 +697,23 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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; +} + +monitor_printf(mon, "%s, \n", meminfo->data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 06d6ea2..076d8cf 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 8202311..ad4e276 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -363,6 +363,42 @@ '*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: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index dbd1a7c..c45397a 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2790,6 +2790,54 @@ void qmp_memchar_write(const char *device, int64_t size, } } +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)
Re: [Qemu-devel] [RESEND PATCH for 1.4 v10 0/3] char: Add CirMemCharDriver and provide QMP interface
On 01/24/2013 08:48 PM, Luiz Capitulino wrote: On Thu, 24 Jan 2013 15:18:04 +0800 Lei Li wrote: On 01/23/2013 11:47 PM, Luiz Capitulino wrote: On Wed, 23 Jan 2013 17:06:09 +0800 Lei Li wrote: Hi Anthony, Resubmit this series with your comments squashed in and Luiz's new comments fixed up. I will push console command part in another thread. There are two bugs in this series. QEMU started with: # qemu [...] -chardev memory,id=foo,maxcapacity=4 This explodes: (qemu) memchar_read foo 10 I'd expect to read '3uiz' in the steps below: (qemu) memchar_write foo luiz3 (qemu) memchar_read foo 10 uiz3, (qemu) Hi Luiz, Thanks very much for your time to test this series! I think the result shows how this algorithm works... [...] - | 3 | u | i | z | - ^ | prod cons I hope that I make it clear. :) Yes, you're right. You're returning old data first, and that's correct and I was wrong. Sorry for that. Now, you do have two bugs to fix: the segfaul and the ',' which was Yes. the segfault is caused by the null return of qmp_memchar_read which replace the error check for cirmem_chr_is_empty(chr) in this version, and the ',' is my mistake in monitor_printf... returned by memchar_read in my example. Apart from that, there's my last comment about returning a full MemCharRead type when the buffer is empty. Sure, I will submit the new version with these fixed very soon. Then, if no new issues are introduced, this will be good to be merged. Thanks! -- Lei
[Qemu-devel] [PATCH 1/3] qemu-char: Add new char backend CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 114 +++ qemu-options.hx | 10 + 2 files changed, 124 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 9ba0573..8045869 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -98,6 +98,7 @@ #include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 +#define CBUFF_SIZE 65536 /***/ /* character device */ @@ -2643,6 +2644,110 @@ 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; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2697,6 +2802,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 +2906,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 +3166,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, +},{ +.name = "maxcapacity", +.type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 4e2b499..2d44137 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -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=
[Qemu-devel] [RESEND PATCH for 1.4 v11 0/3] char: Add CirMemCharDriver and provide QMP interface
Hi Anthony, Resubmit this series with your comments squashed in and Luiz's new comments fixed up. I will push console command part in another thread. Thanks. This patch series attempts to add new char backend CirMemCharDriver with a circular buffer and expose it to users by introducing QMP interface memchar-write and memchar-read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'device': 'str', 'size': 'int', 'data': 'str', '*format': 'str' } } { 'command': 'memchar-read', 'data': {'device': 'str', 'size': 'int', '*format': 'str' }, 'returns': 'MemCharRead' } Expose CirMemCharDriver via the command line like: qemu -chardev memory,id=foo,maxcapacity=65536 -serial chardev:foo Note: Now all of the feature were implemented, and the pervious comments are fixed up too. Since this patch series have been for mailing list for some time and missed 1.3, rebase it with minor fix. Changes since v10: - Fixup comments from Luiz includes: - Fix the segfault casued by null return of qmp_memchar_read changed since v9 in hmp command memchar_read. Return full MemCharRead type when the buffer is empty. - Fix the wrong print ',' in hmp command. Changes since v9: - Fixup comments from Luiz includes: - Error report and doc improvment. - Get rid of cirmem_chr_is_full check and just return an empty string instead of an read error. - Add support to return the number of bytes read. Changes since v8: - Squashed in the comments from Anthony. - Split the console command part out to another thread. Changes since v7: - Rebase the code and fix the format error pointed by Eric. - Modify the version info. Changes since v6: - Improve the document based on Luiz's comments. - Keep pointing to the right position in cbuf for the case producer and consumer might overflow for long running VMs pointed by Luiz. - Limit the size of read_data to the amount of bytes available in the circular buffer. - Other fixups from Luiz. Changes since v5: - Avoid writing the IAC information to the queue. - Grammar of the doc for command line options improved from Eric. Changes since v4: - Get rid of all CongestionControl bits, and assume a dropping behavior based on Luiz's suggestion for now. Will add it when we add async support to QMP. - Squashed the patches about CirMemCharDriver in one. - Other fixups from Luiz. Changes since v3: - Improve the algorithm of circular buffer based on Anthony's suggestion. - Some changes suggested by Luiz and Blue. - And other fixups. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (3): qemu-char: Add new char backend CirMemCharDriver QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command hmp-commands.hx | 39 ++ hmp.c| 34 + hmp.h|2 + qapi-schema.json | 74 qemu-char.c | 204 ++ qemu-options.hx | 10 +++ qmp-commands.hx | 66 + 7 files changed, 429 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 3/3] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 21 + hmp.c| 21 + hmp.h|1 + qapi-schema.json | 36 qemu-char.c | 48 qmp-commands.hx | 33 + 6 files changed, 160 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index bcfea11..bdd48f3 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -858,6 +858,27 @@ 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 + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index 647316a..6092359 100644 --- a/hmp.c +++ b/hmp.c @@ -697,6 +697,27 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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->data) { +monitor_printf(mon, "%s\n", meminfo->data); +} + +qapi_free_MemCharRead(meminfo); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 06d6ea2..076d8cf 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 8202311..ad4e276 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -363,6 +363,42 @@ '*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: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index dbd1a7c..9d63fe7 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2790,6 +2790,54 @@ void qmp_memchar_write(const char *device, int64_t size, } } +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 <
[Qemu-devel] [PATCH 2/3] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 18 ++ hmp.c| 13 + hmp.h|1 + qapi-schema.json | 38 ++ qemu-char.c | 42 ++ qmp-commands.hx | 33 + 6 files changed, 145 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 0934b9b..bcfea11 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -837,6 +837,24 @@ 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 { diff --git a/hmp.c b/hmp.c index c7b6ba0..647316a 100644 --- a/hmp.c +++ b/hmp.c @@ -684,6 +684,19 @@ 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); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 44be683..06d6ea2 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index 6d7252b..8202311 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -325,6 +325,44 @@ { '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'} } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index 8045869..dbd1a7c 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2748,6 +2748,48 @@ fail: 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;
[Qemu-devel] [PATCH 0/2 v2] Time resync support by qemu-ga
This patch series attempts to add time resync support to qemu-ga by introducing qemu-ga commands guest-get-time and guest-set-time. Right now, when a guest is paused or migrated to a file then loaded from that file, the guest OS has no idea that there was a big gap in the time. Depending on how long the gap was, NTP might not be able to resynchronize the guest. So adding new guest-agent command that is called any time a guest is resumed and which tells the guest to update its own wall clock time based on the information from the host will make it easier for a guest to resynchronize without waiting for NTP. The previous RFC send for discussion and suggestion as link here: http://article.gmane.org/gmane.comp.emulators.qemu/186126 The interface for these commands like: { 'command': 'guest-get-time', 'returns': 'TimeInfo' } { 'command': 'guest-set-time', 'data': { '*seconds': 'int', '*microseconds': 'int', '*utc-offset': 'int' } } Notes: I planed to send out this version with the implementition of win32-specific commands, but got some environment issue which I am working on. I will send it out very soon. To avoid wasting of time, I'd like to send this out first with the previous comments fixed to have more suggestions. Suggestions and comments are welcome! Changes since v1: - Squashed patches add support to get host time and add guest-get-time command into one. - Documents improvment based on the suggestions from Eric and Mike. - Change the name of 'HostTimeInfo' to 'TimeInfo'. - Better use-case and logic for 'guest-set-time' command suggested by Eric. - Error handel improvment from Luiz. Lei Li (2): qga: add guest-get-time command qga: add guest-set-time command include/qapi/qmp/qerror.h |3 + qga/commands-posix.c | 106 + qga/qapi-schema.json | 80 ++ 3 files changed, 189 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
Signed-off-by: Lei Li --- include/qapi/qmp/qerror.h |3 +++ qga/commands-posix.c | 30 ++ qga/qapi-schema.json | 38 ++ 3 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 6c0a18d..0baf1a4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -129,6 +129,9 @@ void assert_no_error(Error *err); #define QERR_FEATURE_DISABLED \ ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" +#define QERR_GET_TIME_FAILED \ +ERROR_CLASS_GENERIC_ERROR, "Failed to get time" + #define QERR_INVALID_BLOCK_FORMAT \ ERROR_CLASS_GENERIC_ERROR, "Invalid block format '%s'" diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0ad73f3..2fef2b6 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -119,6 +119,36 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* succeded */ } +static TimeInfo *get_host_time(Error **errp) +{ +int ret; +qemu_timeval tq; +TimeInfo *time_info; + +ret = qemu_gettimeofday(&tq); +if (ret < 0) { +error_set_errno(errp, errno, QERR_GET_TIME_FAILED); +return NULL; +} + +time_info = g_malloc0(sizeof(TimeInfo)); +time_info->seconds = tq.tv_sec; +time_info->microseconds = tq.tv_usec; + +return time_info; +} + +struct TimeInfo *qmp_guest_get_time(Error **errp) +{ +TimeInfo *time_info = get_host_time(errp); + +if (!time_info) { +return NULL; +} + +return time_info; +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d91d903..d067fa5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -83,6 +83,44 @@ { 'command': 'guest-ping' } ## +# @TimeInfo +# +# Time Information. It is relative to the Epoch of 1970-01-01. +# +# @seconds: "seconds" time unit. +# +# @microseconds: "microseconds" time unit. +# +# @utc-offset: Information about utc offset. Represented as: +# ±[] based at a minimum on minutes, with +# negative values are west and positive values +# are east of UTC. The bounds of @utc-offset is +# at most 24 hours away from UTC. +# +# Since: 1.4 +## +{ 'type': 'TimeInfo', + 'data': { 'seconds': 'int', 'microseconds': 'int', +'utc-offset': 'int' } } + +## +# @guest-get-time: +# +# Get the information about host time in UTC and the +# UTC offset. +# +# This command tries to get the host time which is +# presumably correct, since we need to be able to +# resynchronize guest clock to host's in guest. +# +# Returns: @TimeInfo on success. +# +# Since 1.4 +## +{ 'command': 'guest-get-time', + 'returns': 'TimeInfo' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.7.6
[Qemu-devel] [PATCH 0/2 v2] Time resync support by qemu-ga
This patch series attempts to add time resync support to qemu-ga by introducing qemu-ga commands guest-get-time and guest-set-time. Right now, when a guest is paused or migrated to a file then loaded from that file, the guest OS has no idea that there was a big gap in the time. Depending on how long the gap was, NTP might not be able to resynchronize the guest. So adding new guest-agent command that is called any time a guest is resumed and which tells the guest to update its own wall clock time based on the information from the host will make it easier for a guest to resynchronize without waiting for NTP. The previous RFC send for discussion and suggestion as link here: http://article.gmane.org/gmane.comp.emulators.qemu/186126 The interface for these commands like: { 'command': 'guest-get-time', 'returns': 'TimeInfo' } { 'command': 'guest-set-time', 'data': { '*seconds': 'int', '*microseconds': 'int', '*utc-offset': 'int' } } Notes: I planed to send out this version with the implementition of win32-specific commands, but got some environment issue which I am working on. I will send it out very soon. To avoid wasting of time, I'd like to send this out first with the previous comments fixed to have more suggestions. Suggestions and comments are welcome! Changes since v1: - Squashed patches add support to get host time and add guest-get-time command into one. - Documents improvment based on the suggestions from Eric and Mike. - Change the name of 'HostTimeInfo' to 'TimeInfo'. - Better use-case and logic for 'guest-set-time' command suggested by Eric. - Error handel improvment from Luiz. Lei Li (2): qga: add guest-get-time command qga: add guest-set-time command include/qapi/qmp/qerror.h |3 + qga/commands-posix.c | 106 + qga/qapi-schema.json | 80 ++ 3 files changed, 189 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
Signed-off-by: Lei Li --- include/qapi/qmp/qerror.h |3 +++ qga/commands-posix.c | 30 ++ qga/qapi-schema.json | 38 ++ 3 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 6c0a18d..0baf1a4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -129,6 +129,9 @@ void assert_no_error(Error *err); #define QERR_FEATURE_DISABLED \ ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" +#define QERR_GET_TIME_FAILED \ +ERROR_CLASS_GENERIC_ERROR, "Failed to get time" + #define QERR_INVALID_BLOCK_FORMAT \ ERROR_CLASS_GENERIC_ERROR, "Invalid block format '%s'" diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0ad73f3..2fef2b6 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -119,6 +119,36 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* succeded */ } +static TimeInfo *get_host_time(Error **errp) +{ +int ret; +qemu_timeval tq; +TimeInfo *time_info; + +ret = qemu_gettimeofday(&tq); +if (ret < 0) { +error_set_errno(errp, errno, QERR_GET_TIME_FAILED); +return NULL; +} + +time_info = g_malloc0(sizeof(TimeInfo)); +time_info->seconds = tq.tv_sec; +time_info->microseconds = tq.tv_usec; + +return time_info; +} + +struct TimeInfo *qmp_guest_get_time(Error **errp) +{ +TimeInfo *time_info = get_host_time(errp); + +if (!time_info) { +return NULL; +} + +return time_info; +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d91d903..d067fa5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -83,6 +83,44 @@ { 'command': 'guest-ping' } ## +# @TimeInfo +# +# Time Information. It is relative to the Epoch of 1970-01-01. +# +# @seconds: "seconds" time unit. +# +# @microseconds: "microseconds" time unit. +# +# @utc-offset: Information about utc offset. Represented as: +# ±[] based at a minimum on minutes, with +# negative values are west and positive values +# are east of UTC. The bounds of @utc-offset is +# at most 24 hours away from UTC. +# +# Since: 1.4 +## +{ 'type': 'TimeInfo', + 'data': { 'seconds': 'int', 'microseconds': 'int', +'utc-offset': 'int' } } + +## +# @guest-get-time: +# +# Get the information about host time in UTC and the +# UTC offset. +# +# This command tries to get the host time which is +# presumably correct, since we need to be able to +# resynchronize guest clock to host's in guest. +# +# Returns: @TimeInfo on success. +# +# Since 1.4 +## +{ 'command': 'guest-get-time', + 'returns': 'TimeInfo' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.7.6
[Qemu-devel] [PATCH 2/2] qga: add guest-set-time command
Signed-off-by: Lei Li --- qga/commands-posix.c | 76 ++ qga/qapi-schema.json | 42 +++ 2 files changed, 118 insertions(+), 0 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 2fef2b6..5424c50 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -149,6 +149,82 @@ struct TimeInfo *qmp_guest_get_time(Error **errp) return time_info; } +void qmp_guest_set_time(bool has_seconds, int64_t seconds, +bool has_microseconds, int64_t microseconds, +bool has_utc_offset, int64_t utc_offset, Error **errp) +{ +int ret; +int status; +pid_t pid, rpid; +struct timeval tv; +TimeInfo *time_info; + +/* year-2038 will overflow in case time_t is 32bit */ +if (sizeof(time_t) == 4) { +return; +} + +if (!has_seconds) { +if (has_microseconds || has_utc_offset) { +error_setg(errp, "field 'seconds' missing"); +return; +} else { +/* Grab time from the host if no arguments given. */ +time_info = get_host_time(errp); +if (!time_info) { +return; +} + +tv.tv_sec = time_info->seconds; +tv.tv_usec = time_info->microseconds; +} +} else { +if (abs(utc_offset) > (24 * 60)) { +error_setg(errp, "'utc_offset' shoud be less than 24 hours"); +return; +} + +if (microseconds > 100) { +error_setg(errp, "'microseconds' overflow"); +return; +} + +tv.tv_sec = (time_t) seconds + (has_utc_offset ? utc_offset * 60 : 0); +tv.tv_usec = has_microseconds ? (time_t) microseconds : 0; +} + + +ret = settimeofday(&tv, NULL); +if (ret < 0) { +error_set(errp, QERR_QGA_COMMAND_FAILED, strerror(errno)); +return; +} + +/* Set the Hardware Clock to the current System Time. */ +pid = fork(); +if (pid == 0) { +setsid(); +reopen_fd_to_null(0); +reopen_fd_to_null(1); +reopen_fd_to_null(2); + +execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); +_exit(EXIT_FAILURE); +} else if (pid < 0) { +goto exit_err; +} + +do { +rpid = waitpid(pid, &status, 0); +} while (rpid == -1 && errno == EINTR); +if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { +return; +} + +exit_err: +error_set(errp, QERR_UNDEFINED_ERROR); +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d067fa5..6eba625 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -121,6 +121,48 @@ 'returns': 'TimeInfo' } ## +# @guest-set-time: +# +# Set guest time. If no arguments were given, will set +# host time to guest. +# +# Right now, when a guest is paused or migrated to a file +# then loaded from that file, the guest OS has no idea that +# there was a big gap in the time. Depending on how long +# the gap was, NTP might not be able to resynchronize the +# guest. +# +# This command tries to set guest time based on the information +# from host or an absolute value given by management app, and +# set the Hardware Clock to the current System Time. This +# will make it easier for a guest to resynchronize without +# waiting for NTP. +# +# @seconds: #optional "seconds" time. +# +# @microseconds: #optional "microseconds" time. +# +# @utc-offset: #optional utc offset. +# +# Returns: Nothing on success. +# +# Notes: If no arguments were given, will grab time from +#the host. +# +#@microseconds and @utc-offset must not be provided +#unless @seconds is present. +# +#If just @seconds provided, other fields would be +#set to zero. +# +# present +# Since: 1.4 +## +{ 'command': 'guest-set-time', + 'data': { '*seconds': 'int', '*microseconds': 'int', +'*utc-offset': 'int' } } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.7.6
[Qemu-devel] [PATCH] qga: cast to int for DWORD type
This patch fixes a compiler warning when cross-build: qga/service-win32.c: In function 'printf_win_error': qga/service-win32.c:32:5: warning: format '%d' expects argument of type 'int', but argument 3 has type 'DWORD' [-Wformat] Signed-off-by: Lei Li --- qga/service-win32.c |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/qga/service-win32.c b/qga/service-win32.c index 0905456..843398a 100644 --- a/qga/service-win32.c +++ b/qga/service-win32.c @@ -29,7 +29,7 @@ static int printf_win_error(const char *text) MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *)&message, 0, NULL); -n = printf("%s. (Error: %d) %s", text, err, message); +n = printf("%s. (Error: %d) %s", text, (int)err, message); LocalFree(message); return n; -- 1.7.7.6
Re: [Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
On 01/29/2013 02:29 AM, Eric Blake wrote: On 01/27/2013 11:14 AM, Lei Li wrote: Signed-off-by: Lei Li --- include/qapi/qmp/qerror.h |3 +++ qga/commands-posix.c | 30 ++ qga/qapi-schema.json | 38 ++ 3 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 6c0a18d..0baf1a4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -129,6 +129,9 @@ void assert_no_error(Error *err); #define QERR_FEATURE_DISABLED \ ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" +#define QERR_GET_TIME_FAILED \ +ERROR_CLASS_GENERIC_ERROR, "Failed to get time" + These days, you shouldn't be defining a new QERR wrapper. Instead,...[1] +static TimeInfo *get_host_time(Error **errp) This name is unusual...[2] Okay, I think there might be misunderstanding here. The current implementation of this 'guest-get-time' command is trying to get the* *_host_ time, since the guest time might be not correct. This qemu_gettimeofday should return the host time, or am I wrong? +{ +int ret; +qemu_timeval tq; +TimeInfo *time_info; + +ret = qemu_gettimeofday(&tq); +if (ret < 0) { +error_set_errno(errp, errno, QERR_GET_TIME_FAILED); [1]...use the right idiom here: error_setg_errno(errp, errno, "Failed to get time"); +return NULL; +} + +time_info = g_malloc0(sizeof(TimeInfo)); +time_info->seconds = tq.tv_sec; +time_info->microseconds = tq.tv_usec; Is microseconds the right unit to expose through JSON, or are we going to wish we had exposed nanoseconds down the road (you can still use qemu_gettimeofday() and scale tv_usec by 1000 before assigning to time_info->nanoseconds, if we desire the extra granularity). You aren't setting time_info->utc_offset. Is that intentional? Yes, since 'guest-get-time' is supposed to get host time in UTC, the utc_offset is zero. When guest want to set different time zone it can set the utc_offset through 'guest-set-time'. + +return time_info; +} + +struct TimeInfo *qmp_guest_get_time(Error **errp) +{ +TimeInfo *time_info = get_host_time(errp); [2]...given that it is only ever called from qmp_guest_get_time. + +if (!time_info) { +return NULL; +} These three lines can be deleted, + +return time_info; since this line will do the same thing if you let NULL time_info get this far. +++ b/qga/qapi-schema.json @@ -83,6 +83,44 @@ { 'command': 'guest-ping' } ## +# @TimeInfo +# +# Time Information. It is relative to the Epoch of 1970-01-01. +# +# @seconds: "seconds" time unit. +# +# @microseconds: "microseconds" time unit. +# +# @utc-offset: Information about utc offset. Represented as: +# ±[] based at a minimum on minutes, with s/based at a minimum on// This still doesn't state whether two hours east of UTC is represented as 120 or as 0200. +# negative values are west and positive values +# are east of UTC. The bounds of @utc-offset is +# at most 24 hours away from UTC. +# +# Since: 1.4 +## +{ 'type': 'TimeInfo', + 'data': { 'seconds': 'int', 'microseconds': 'int', +'utc-offset': 'int' } } + +## +# @guest-get-time: +# +# Get the information about host time in UTC and the s/host/guest/ +# UTC offset. +# +# This command tries to get the host time which is +# presumably correct, since we need to be able to +# resynchronize guest clock to host's in guest. This sentence doesn't make sense. Better would be: Get the guest's notion of the current time. +# +# Returns: @TimeInfo on success. +# +# Since 1.4 +## +{ 'command': 'guest-get-time', + 'returns': 'TimeInfo' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- Lei
Re: [Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
On 01/29/2013 04:24 AM, Anthony Liguori wrote: Eric Blake writes: On 01/27/2013 11:14 AM, Lei Li wrote: Signed-off-by: Lei Li --- include/qapi/qmp/qerror.h |3 +++ qga/commands-posix.c | 30 ++ qga/qapi-schema.json | 38 ++ 3 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 6c0a18d..0baf1a4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -129,6 +129,9 @@ void assert_no_error(Error *err); #define QERR_FEATURE_DISABLED \ ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" +#define QERR_GET_TIME_FAILED \ +ERROR_CLASS_GENERIC_ERROR, "Failed to get time" + These days, you shouldn't be defining a new QERR wrapper. Instead,...[1] +static TimeInfo *get_host_time(Error **errp) This name is unusual...[2] +{ +int ret; +qemu_timeval tq; +TimeInfo *time_info; + +ret = qemu_gettimeofday(&tq); +if (ret < 0) { +error_set_errno(errp, errno, QERR_GET_TIME_FAILED); [1]...use the right idiom here: error_setg_errno(errp, errno, "Failed to get time"); +return NULL; +} + +time_info = g_malloc0(sizeof(TimeInfo)); +time_info->seconds = tq.tv_sec; +time_info->microseconds = tq.tv_usec; Is microseconds the right unit to expose through JSON, or are we going to wish we had exposed nanoseconds down the road (you can still use qemu_gettimeofday() and scale tv_usec by 1000 before assigning to time_info->nanoseconds, if we desire the extra granularity). You aren't setting time_info->utc_offset. Is that intentional? Moreover, there's no need to specify microseconds and seconds. A 64-bit value for nanoseconds is sufficient. A signed value can represent 1678 -> 2262. If anyone is using qemu-ga in 2262, they'll have to introduce a new command for their quantum emulators :-) Setting time independent of date is a bit silly because time cannot be interpreted correctly without a date. A single value of nanoseconds since the epoch (interpreted as UTC/GMT time) is really the best strategy. The host shouldn't be involved in guest time zones. In a Cloud environment, it's pretty normal to have different guests using different time zones. Regards, Anthony Liguori + +return time_info; +} + +struct TimeInfo *qmp_guest_get_time(Error **errp) +{ +TimeInfo *time_info = get_host_time(errp); [2]...given that it is only ever called from qmp_guest_get_time. + +if (!time_info) { +return NULL; +} These three lines can be deleted, + +return time_info; since this line will do the same thing if you let NULL time_info get this far. +++ b/qga/qapi-schema.json @@ -83,6 +83,44 @@ { 'command': 'guest-ping' } ## +# @TimeInfo +# +# Time Information. It is relative to the Epoch of 1970-01-01. +# +# @seconds: "seconds" time unit. +# +# @microseconds: "microseconds" time unit. +# +# @utc-offset: Information about utc offset. Represented as: +# ±[] based at a minimum on minutes, with s/based at a minimum on// This still doesn't state whether two hours east of UTC is represented as 120 or as 0200. It should be 120. Yeah, I should make it clear. I am thinking if this 'utc_offset' can be made as a string, represented like: ±[hh]:[mm], +08:45 (8 hours and 45 minutes) for example. +# negative values are west and positive values +# are east of UTC. The bounds of @utc-offset is +# at most 24 hours away from UTC. +# +# Since: 1.4 +## +{ 'type': 'TimeInfo', + 'data': { 'seconds': 'int', 'microseconds': 'int', +'utc-offset': 'int' } } + +## +# @guest-get-time: +# +# Get the information about host time in UTC and the s/host/guest/ +# UTC offset. +# +# This command tries to get the host time which is +# presumably correct, since we need to be able to +# resynchronize guest clock to host's in guest. This sentence doesn't make sense. Better would be: Get the guest's notion of the current time. +# +# Returns: @TimeInfo on success. +# +# Since 1.4 +## +{ 'command': 'guest-get-time', + 'returns': 'TimeInfo' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- Eric Blake eblake redhat com+1-919-301-3266 Libvirt virtualization library http://libvirt.org -- Lei
Re: [Qemu-devel] [PATCH 2/2] qga: add guest-set-time command
On 01/29/2013 04:45 AM, Eric Blake wrote: On 01/27/2013 11:14 AM, Lei Li wrote: Signed-off-by: Lei Li --- qga/commands-posix.c | 76 ++ qga/qapi-schema.json | 42 +++ 2 files changed, 118 insertions(+), 0 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 2fef2b6..5424c50 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -149,6 +149,82 @@ struct TimeInfo *qmp_guest_get_time(Error **errp) return time_info; } +void qmp_guest_set_time(bool has_seconds, int64_t seconds, +bool has_microseconds, int64_t microseconds, +bool has_utc_offset, int64_t utc_offset, Error **errp) +{ +int ret; +int status; +pid_t pid, rpid; +struct timeval tv; +TimeInfo *time_info; + +/* year-2038 will overflow in case time_t is 32bit */ +if (sizeof(time_t) == 4) { +return; +} What? Silently giving up, without even seeing whether overflow happened? That just feels wrong. Make the change if it is in range, and be vocal about setting an error if there is overflow. Yes, this is my stupid mistake... + +if (!has_seconds) { +if (has_microseconds || has_utc_offset) { +error_setg(errp, "field 'seconds' missing"); +return; +} else { +/* Grab time from the host if no arguments given. */ +time_info = get_host_time(errp); Does this even make sense? Since this code will be executing in the guest, you are getting the guest's current notion of time (as I said in 1/2, get_host_time() is misnamed); and then setting the guest's time right back to the same value. Why bother? It seems like if the host is going to bother issuing the guest-set-time agent command, then the host should have a mandatory argument to tell the guest what to set its time to. Then again, reading the rest of your code, I can see one case where it _might_ make sense to allow the host to not pass in a time - namely, if the host wants to force the guest to call hwclock to write its notion of current system time (in RAM) back to the hardware. But that seems like a stretch. +if (!time_info) { +return; +} + +tv.tv_sec = time_info->seconds; +tv.tv_usec = time_info->microseconds; +} +} else { +if (abs(utc_offset) > (24 * 60)) { Won't work. abs() takes an int argument, so you can pass in int64_t values that will overflow the range of int and not be detected by this overflow check. Yes, you are right. +error_setg(errp, "'utc_offset' shoud be less than 24 hours"); s/shoud/should/ +return; +} + +if (microseconds > 100) { +error_setg(errp, "'microseconds' overflow"); +return; +} + +tv.tv_sec = (time_t) seconds + (has_utc_offset ? utc_offset * 60 : 0); +tv.tv_usec = has_microseconds ? (time_t) microseconds : 0; +} + + +ret = settimeofday(&tv, NULL); +if (ret < 0) { +error_set(errp, QERR_QGA_COMMAND_FAILED, strerror(errno)); Prefer error_setg_errno. +return; +} + +/* Set the Hardware Clock to the current System Time. */ +pid = fork(); +if (pid == 0) { +setsid(); +reopen_fd_to_null(0); +reopen_fd_to_null(1); +reopen_fd_to_null(2); + +execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); +_exit(EXIT_FAILURE); +} else if (pid < 0) { +goto exit_err; +} + +do { +rpid = waitpid(pid, &status, 0); +} while (rpid == -1 && errno == EINTR); +if (rpid == pid && WIFEXITED(status) && !WEXITSTATUS(status)) { +return; +} + +exit_err: +error_set(errp, QERR_UNDEFINED_ERROR); error_setg and a nicer message would be nicer to the end user. +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d067fa5..6eba625 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -121,6 +121,48 @@ 'returns': 'TimeInfo' } ## +# @guest-set-time: +# +# Set guest time. If no arguments were given, will set +# host time to guest. I don't see how omitted time arguments can possibly work. It makes sense from a management app point of view (tell me a time, and I'll call guest-set-time with that value on your behalf; or don't tell me a time, I'll get the host time, and then call guest-set-time with the right Sorry, I think I am supposed to make it work like this way? value); but this file is not documenting the management app, but the JSON interface that the guest sees. In other words, I think time has to be a
Re: [Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
Sorry, missing replied... It should be the reply to Eric here. On 01/30/2013 03:37 PM, Lei Li wrote: On 01/29/2013 04:24 AM, Anthony Liguori wrote: Eric Blake writes: On 01/27/2013 11:14 AM, Lei Li wrote: Signed-off-by: Lei Li --- include/qapi/qmp/qerror.h |3 +++ qga/commands-posix.c | 30 ++ qga/qapi-schema.json | 38 ++ 3 files changed, 71 insertions(+), 0 deletions(-) diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index 6c0a18d..0baf1a4 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -129,6 +129,9 @@ void assert_no_error(Error *err); #define QERR_FEATURE_DISABLED \ ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" +#define QERR_GET_TIME_FAILED \ +ERROR_CLASS_GENERIC_ERROR, "Failed to get time" + These days, you shouldn't be defining a new QERR wrapper. Instead,...[1] +static TimeInfo *get_host_time(Error **errp) This name is unusual...[2] +{ +int ret; +qemu_timeval tq; +TimeInfo *time_info; + +ret = qemu_gettimeofday(&tq); +if (ret < 0) { +error_set_errno(errp, errno, QERR_GET_TIME_FAILED); [1]...use the right idiom here: error_setg_errno(errp, errno, "Failed to get time"); +return NULL; +} + +time_info = g_malloc0(sizeof(TimeInfo)); +time_info->seconds = tq.tv_sec; +time_info->microseconds = tq.tv_usec; Is microseconds the right unit to expose through JSON, or are we going to wish we had exposed nanoseconds down the road (you can still use qemu_gettimeofday() and scale tv_usec by 1000 before assigning to time_info->nanoseconds, if we desire the extra granularity). You aren't setting time_info->utc_offset. Is that intentional? Moreover, there's no need to specify microseconds and seconds. A 64-bit value for nanoseconds is sufficient. A signed value can represent 1678 -> 2262. If anyone is using qemu-ga in 2262, they'll have to introduce a new command for their quantum emulators :-) Setting time independent of date is a bit silly because time cannot be interpreted correctly without a date. A single value of nanoseconds since the epoch (interpreted as UTC/GMT time) is really the best strategy. The host shouldn't be involved in guest time zones. In a Cloud environment, it's pretty normal to have different guests using different time zones. Regards, Anthony Liguori + +return time_info; +} + +struct TimeInfo *qmp_guest_get_time(Error **errp) +{ +TimeInfo *time_info = get_host_time(errp); [2]...given that it is only ever called from qmp_guest_get_time. + +if (!time_info) { +return NULL; +} These three lines can be deleted, + +return time_info; since this line will do the same thing if you let NULL time_info get this far. +++ b/qga/qapi-schema.json @@ -83,6 +83,44 @@ { 'command': 'guest-ping' } ## +# @TimeInfo +# +# Time Information. It is relative to the Epoch of 1970-01-01. +# +# @seconds: "seconds" time unit. +# +# @microseconds: "microseconds" time unit. +# +# @utc-offset: Information about utc offset. Represented as: +# ±[] based at a minimum on minutes, with s/based at a minimum on// This still doesn't state whether two hours east of UTC is represented as 120 or as 0200. It should be 120. Yeah, I should make it clear. I am thinking if this 'utc_offset' can be made as a string, represented like: ±[hh]:[mm], +08:45 (8 hours and 45 minutes) for example. +# negative values are west and positive values +# are east of UTC. The bounds of @utc-offset is +# at most 24 hours away from UTC. +# +# Since: 1.4 +## +{ 'type': 'TimeInfo', + 'data': { 'seconds': 'int', 'microseconds': 'int', +'utc-offset': 'int' } } + +## +# @guest-get-time: +# +# Get the information about host time in UTC and the s/host/guest/ +# UTC offset. +# +# This command tries to get the host time which is +# presumably correct, since we need to be able to +# resynchronize guest clock to host's in guest. This sentence doesn't make sense. Better would be: Get the guest's notion of the current time. +# +# Returns: @TimeInfo on success. +# +# Since 1.4 +## +{ 'command': 'guest-get-time', + 'returns': 'TimeInfo' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- Eric Blake eblake redhat com+1-919-301-3266 Libvirt virtualization library http://libvirt.org -- Lei
Re: [Qemu-devel] [PATCH 3/5] QAPI: Introduce memchar-write QMP command
On 09/20/2012 02:05 AM, Luiz Capitulino wrote: On Wed, 12 Sep 2012 19:57:24 +0800 Lei Li wrote: Signed-off-by: Lei Li --- hmp-commands.hx | 23 ++ hmp.c| 19 +++ hmp.h|1 + qapi-schema.json | 69 ++ qemu-char.c | 48 + qmp-commands.hx | 39 ++ 6 files changed, 199 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index ed67e99..fe11926 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -796,6 +796,29 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,size:i,data:s,format:s?,control:s?", +.params = "chardev size data [format] [control]", +.help = "Provide writing interface for CirMemCharDriver. Write" + "'data' to it with size 'size'", +.mhandler.cmd = hmp_memchar_write, +}, That's a too low level command for hmp. I'd actually consider dropping it in favor of the console command, but if you really want to have this then I suggest making the following changes: o drop 'size', as this can be calculated from the user input string o drop 'format', as base64 doesn't make much sense for hmp o turn 'control' into a flag, like -b for block and -d for drop Also applies for memchar_read. + +STEXI +@item memchar_write @var{chardev} @var{size} @var{data} [@var{format}] [@var{control}] +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device with size @var{size}. + +@var{format} is the format of the data write to CirMemCharDriver. +Support 'utf8' and 'base64', by default is 'utf8'. + +@var{control} is option('block', 'drop') for read and write command +that specifies behavior when the queue is full/empty. By default is +'drop'. Note that the 'block' option is not supported now. +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ba6fbd3..97f5058 100644 --- a/hmp.c +++ b/hmp.c @@ -671,6 +671,25 @@ 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 = qdict_get_int(qdict, "size"); +const char *chardev = qdict_get_str(qdict, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +int val = qdict_get_try_bool(qdict, "base64", 0); +enum DataFormat format; +int con = qdict_get_try_bool(qdict, "block", 0); +enum CongestionControl control; +Error *errp = NULL; + +format = val ? DATA_FORMAT_BASE64 : DATA_FORMAT_UTF8; +control = con ? CONGESTION_CONTROL_BLOCK : CONGESTION_CONTROL_DROP; +qmp_memchar_write(chardev, size, data, true, format, + true, control, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 48b9c59..44b6463 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index a9f465a..371239a 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -236,6 +236,75 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format write to or read from +# memchardev. The default value would be utf8. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', will support +# other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @CongestionControl +# +# An enumeration of options for the read and write command that +# specifies behavior when the queue is full/empty. The default +# option would be dropped. +# +# @drop: Would result in reads returning empty strings and writes +#
Re: [Qemu-devel] [PATCH 2/6] monitor: Adjust qmp_human_monitor_command to new MemCharDriver
On 08/31/2012 02:51 AM, Luiz Capitulino wrote: On Thu, 23 Aug 2012 13:14:22 +0800 Lei Li wrote: Signed-off-by: Lei Li --- monitor.c |8 +++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/monitor.c b/monitor.c index 480f583..ab4650b 100644 --- a/monitor.c +++ b/monitor.c @@ -642,7 +642,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, CharDriverState mchar; memset(&hmp, 0, sizeof(hmp)); -qemu_chr_init_mem(&mchar); + +/* Since the backend of MemCharDriver convert to a circular + * buffer with fixed size, so should indicate the init memory + * size. + * + * XXX: is 4096 as init memory enough for this? */ +qemu_chr_init_mem(&mchar, 4096); I'm not sure I like this. The end result will be that hmp commands writing more than 4096 bytes will simply fail or return garbage (if the circular buffer is changed to allow writing more than it supports) today they would just work. Although it's always possible to increase the buffer size, we would only realize this is needed when the bug is triggered, which means it has a high chance of happening in production. IOW, this would be a regression. The only solution I can think of is to make the circular buffer and the current MemoryDriver live in parallel. Actually, you really seem to be adding something else. Hi Luiz, Thanks for your review. Yes, I agree with that it's not a good solution for the old user human command. Make the circular buffer and the current MemoryDriver live in parallel, do you mean don't change the current MemoryDriver, choose to expose a new char device backend with circular buffer? hmp.chr = &mchar; old_mon = cur_mon; -- Lei
[Qemu-devel] [PATCH 1/5] qemu-char: Add new char device CirMemCharDriver
Signed-off-by: Lei Li --- qemu-char.c | 84 +++ 1 files changed, 84 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 767da93..0470085 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2591,6 +2591,90 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr) return d->outbuf_size; } +/*/ +/*CircularMemory chardev*/ + +typedef struct { +size_t cbuf_capacity; +size_t cbuf_in; +size_t cbuf_out; +size_t cbuf_count; +uint8_t *cbuf; +} CircMemCharDriver; + +static int cirmem_chr_is_empty(CharDriverState *chr) +{ +CircMemCharDriver *d = chr->opaque; + +return d->cbuf_count == 0; +} + +static int cirmem_chr_is_full(CharDriverState *chr) +{ +CircMemCharDriver *d = chr->opaque; + +return d->cbuf_count == d->cbuf_capacity; +} + +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ +CircMemCharDriver *d = chr->opaque; +int left; + +if (d->cbuf_capacity < len) { +return -1; +} + +left = d->cbuf_capacity - d->cbuf_count % d->cbuf_capacity; + +/* Some of cbuf need to be overwrited */ +if (left < len) { +memcpy(d->cbuf + d->cbuf_in, buf, left); +memcpy(d->cbuf + d->cbuf_out, buf + left, len - left); +d->cbuf_out = (d->cbuf_out + len - left) % d->cbuf_capacity; +d->cbuf_count = d->cbuf_count + left; +} else { +/* Completely overwrite */ +if (cirmem_chr_is_full(chr)) { +d->cbuf_out = (d->cbuf_out + len) % d->cbuf_capacity; +} else { +/* Enough cbuf to write */ +d->cbuf_count += len; +} +memcpy(d->cbuf + d->cbuf_in, buf, len); +} + +d->cbuf_in = (d->cbuf_in + len) % d->cbuf_capacity; + +return len; +} + +static void cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) +{ +CircMemCharDriver *d = chr->opaque; +int left; + +if (cirmem_chr_is_empty(chr)) { +return; +} + +left = d->cbuf_capacity - d->cbuf_count % d->cbuf_capacity; + +if (d->cbuf_capacity < len) { +len = d->cbuf_capacity; +} + +if (left < len) { +memcpy(buf, d->cbuf + d->cbuf_out, left); +memcpy(buf + left, d->cbuf + d->cbuf_out + left, len - left); +} else { +memcpy(buf, d->cbuf + d->cbuf_out, len); +} + +d->cbuf_out = (d->cbuf_out + len) % d->cbuf_capacity; +d->cbuf_count -= len; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; -- 1.7.7.6
[Qemu-devel] [RFC v3 ATCH 0/5] char: expose CirMemCharDriver and provide QMP interface
This RFC series attempts to convert the MemCharDriver to use a circular buffer for input and output, expose it to users by introducing QMP commands memchar_write and memchar_read and via the command line like the other CharDriverStates. Serial ports in qemu always use CharDriverStates as there backends, Right now, all of our backends always try to write the data from the guest to a socket or file. The concern from OpenStack is that this could lead to unbounded disk space usage since they log the serial output. For more detail of the background info: https://bugs.launchpad.net/nova/+bug/832507 So we want to use a circular buffer in QEMU instead, and then OpenStack can periodically read the buffer in QEMU and log it. The QMP commands introduced like: { 'command': 'memchar-write', 'data': {'chardev': 'str', 'size': 'int', 'data': 'str', 'format': 'str', 'control': 'str' } } { 'command': 'memchar-read', 'data': {'chardev': 'str', 'size': 'int', 'format': 'str', 'control': 'str' }, 'returns': 'str' } Expose CirMemCharDriver via the command line like: qemu -chardev memchr,id=foo,maxcapacity=640k -serial chardev:foo Introduce HMP command 'console' like: (qemu) console foo Note: Now all of the feature were implemented, this series is just a sketch and not completely tested. Please comment and let me know if this seems like the direction we should be headed, your suggestion would be very appreciated! Known issues: There are still some problems I am fixing, - The circularMemoryDriver need to be improved. - For the 'block' option as sync command, will support it later when we gain the necessary infrastructure. - The HMP command 'console' still have problems to be fixed. Changes since v2: - Add congestion mechanism. For the 'block' option as sync command, will support it later when we gain the necessary infrastructure enhancement. - Add HMP 'console' command so that can interact with multiple chardevs via a single monitor socket. - Make the circular buffer backend and the current MemCharDriver live in parallel, expose a new char backend with circular buffer CirMemCharDriver suggested by Luiz. - Other fixs from Eric and Markus. Changes since v1: - Exposing the MemCharDriver via command line. - Support base64 data format suggested by Anthony and Eric. - Follow the new rule for the name of qmp command from Eric. Lei Li (5): qemu-char: Add new char device CirMemCharDriver Expose CirMemCharDriver via command line QAPI: Introduce memchar-write QMP command QAPI: Introduce memchar-read QMP command HMP: Introduce console command hmp-commands.hx | 72 ++ hmp.c| 79 hmp.h|3 + monitor.c| 18 + monitor.h|2 + qapi-schema.json | 96 qemu-char.c | 212 ++ qemu-config.c|3 + qemu-options.hx | 10 +++ qmp-commands.hx | 76 +++ 10 files changed, 571 insertions(+), 0 deletions(-)
[Qemu-devel] [PATCH 4/5] QAPI: Introduce memchar-read QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 25 + hmp.c| 18 ++ hmp.h|1 + qapi-schema.json | 27 +++ qemu-char.c | 48 qmp-commands.hx | 37 + 6 files changed, 156 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index fe11926..1d2fccc 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -816,6 +816,31 @@ Support 'utf8' and 'base64', by default is 'utf8'. @var{control} is option('block', 'drop') for read and write command that specifies behavior when the queue is full/empty. By default is 'drop'. Note that the 'block' option is not supported now. + +ETEXI + +{ +.name = "memchar_read", +.args_type = "chardev:s,size:i,format:s?,control:s?", +.params = "chardev size [format] [control]", +.help = "Provide read interface for CirMemCharDriver. Read from" + "it and return 'size' of the data", +.mhandler.cmd = hmp_memchar_read, +}, + +STEXI +@item memchar_read @var{chardev} @var{size} +@findex memchar_read +Provide read interface for CirMemCharDriver. Read from cirmemchr +char device and return @var{size} of the data. + +@var{format} is the format of the data read from CirMemCharDriver. +Support 'utf8' and 'base64', by default is 'utf8'. + +@var{control} is option['block', 'drop'] for read and write command +that specifies behavior when the queue is full/empty. By default is +'drop'. Note that the 'block' option is not supported now. + ETEXI { diff --git a/hmp.c b/hmp.c index 97f5058..4397981 100644 --- a/hmp.c +++ b/hmp.c @@ -690,6 +690,24 @@ void hmp_memchar_write(Monitor *mon, const QDict *qdict) 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, "chardev"); +char *data; +int val = qdict_get_try_bool(qdict, "base64", 0); +enum DataFormat format; +int con = qdict_get_try_bool(qdict, "block", 0); +enum CongestionControl control; +Error *errp = NULL; + +format = val ? DATA_FORMAT_BASE64 : DATA_FORMAT_UTF8; +control = con ? CONGESTION_CONTROL_BLOCK : CONGESTION_CONTROL_DROP; +data = qmp_memchar_read(chardev, size, true, format, +true, control, &errp); +monitor_printf(mon, "%s\n", data); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 44b6463..ed2cda4 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ 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); diff --git a/qapi-schema.json b/qapi-schema.json index 371239a..5274b86 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -305,6 +305,33 @@ '*control': 'CongestionControl'} } ## +# @memchar-read: +# +# Provide read interface for CirMemCharDriver. Read from cirmemchar +# char device and return the data. +# +# @chardev: the name of the cirmemchar char device. +# +# @size: the size to read in bytes. +# +# @format: #optional the format of the data want to read from +# CirMemCharDriver, by default is 'utf8'. +# +# @control: #optional options for read and write command that specifies +# behavior when the queue is full/empty. +# +# Returns: The data read from cirmemchar as string. +# If @chardev is not a valid memchr device, DeviceNotFound +# If an I/O error occurs while reading, IOError +# +# Since: 1.3 +## +{ 'command': 'memchar-read', + 'data': {'chardev': 'str', 'size': 'int', '*format': 'DataFormat', + '*control': 'CongestionControl'}, + 'returns': 'str' } + +## # @CommandInfo: # # Information about a QMP command diff --git a/qemu-char.c b/qemu-char.c index be1d79a..bb3ddb9 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2748,6 +2748,54 @@ void qmp_memchar_write(const char *chardev, int64_t size, g_free(write_data); } +char *qmp_memchar_read(const char *chardev, int64_t size, + bool ha
[Qemu-devel] [PATCH 2/5] Expose CirMemCharDriver via command line
Signed-off-by: Lei Li --- qemu-char.c | 31 +++ qemu-config.c |3 +++ qemu-options.hx | 10 ++ 3 files changed, 44 insertions(+), 0 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 0470085..6e84acc 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2675,6 +2675,31 @@ static void cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len) d->cbuf_count -= len; } +static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts) +{ +CharDriverState *chr; +CircMemCharDriver *d; + +chr = g_malloc0(sizeof(CharDriverState)); +d = g_malloc(sizeof(*d)); + +d->cbuf_capacity = qemu_opt_get_number(opts, "maxcapacity", 0); +if (d->cbuf_capacity == 0) { +d->cbuf_capacity = CBUFF_SIZE; +} + +d->cbuf_in = 0; +d->cbuf_out = 0; +d->cbuf_count = 0; +d->cbuf = g_malloc0(d->cbuf_capacity); + +memset(chr, 0, sizeof(*chr)); +chr->opaque = d; +chr->chr_write = mem_chr_write; + +return chr; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2739,6 +2764,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "path", p); return opts; } +if (strstart(filename, "memchr", &p)) { +qemu_opt_set(opts, "backend", "memchr"); +qemu_opt_set(opts, "maxcapacity", p); +return opts; +} if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { @@ -2812,6 +2842,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 = "memchr",.open = qemu_chr_open_cirmemchr }, #ifdef _WIN32 { .name = "file", .open = qemu_chr_open_win_file_out }, { .name = "pipe", .open = qemu_chr_open_win_pipe }, diff --git a/qemu-config.c b/qemu-config.c index eba977e..5cb6dcb 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -213,6 +213,9 @@ static QemuOptsList qemu_chardev_opts = { },{ .name = "debug", .type = QEMU_OPT_NUMBER, +},{ +.name = "maxcapacity", +.type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, diff --git a/qemu-options.hx b/qemu-options.hx index 804a2d1..3a7384d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1666,6 +1666,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 memchr,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 @@ -1704,6 +1705,7 @@ Backend is one of: @option{udp}, @option{msmouse}, @option{vc}, +@option{memchr}, @option{file}, @option{pipe}, @option{console}, @@ -1810,6 +1812,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 memchr ,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} specify the max capacity of the size of circular buffer +want to create. + @item -chardev file ,id=@var{id} ,path=@var{path} Log all traffic received from the guest to a file. -- 1.7.7.6
[Qemu-devel] [PATCH 5/5] HMP: Introduce console command
Signed-off-by: Lei Li --- hmp.c | 42 ++ monitor.c | 18 ++ monitor.h |2 ++ 3 files changed, 62 insertions(+), 0 deletions(-) diff --git a/hmp.c b/hmp.c index 4397981..a016a5c 100644 --- a/hmp.c +++ b/hmp.c @@ -1205,3 +1205,45 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict) qmp_screendump(filename, &err); hmp_handle_error(mon, &err); } + +int console_escape_char = 0x1d; /* ctrl-] is used for escape */ + +static void hmp_read_console(Monitor *mon, const char *data, + void *opaque) +{ +CharDriverState *chr = opaque; +uint32_t size = strlen(data); +enum DataFormat format = DATA_FORMAT_UTF8; +enum CongestionControl control = CONGESTION_CONTROL_DROP; + +Error *err = NULL; + +if (*data == console_escape_char) { +monitor_resume(mon); +return; +} + +qmp_memchar_write(chr->label, size, data, 0, format, + 0, control, &err); +monitor_read_command(mon, 1); +} + +void hmp_console(Monitor *mon, const QDict *qdict) +{ +const char *device = qdict_get_str(qdict, "chardev"); +CharDriverState *chr; +Error *err = NULL; + +chr = qemu_chr_find(device); + +if (!chr) { +error_set(&err, QERR_DEVICE_NOT_FOUND, device); +hmp_handle_error(mon, &err); +return; +} + +if (monitor_read_console(mon, device, hmp_read_console, chr) < 0) { +monitor_printf(mon, "Connect to console %s failed\n", device); +} +g_free(chr); +} diff --git a/monitor.c b/monitor.c index 67064e2..285dc7b 100644 --- a/monitor.c +++ b/monitor.c @@ -256,6 +256,24 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, } } +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque) +{ +char prompt[60]; + +if (!mon->rs) +return -1; + +if (monitor_ctrl_mode(mon)) { +qerror_report(QERR_MISSING_PARAMETER, "console"); +return -EINVAL; +} + +snprintf(prompt, sizeof(prompt), "%s: ", device); +readline_start(mon->rs, prompt, 0, readline_func, opaque); +return 0; +} + void monitor_flush(Monitor *mon) { if (mon && mon->outbuf_index != 0 && !mon->mux_out) { diff --git a/monitor.h b/monitor.h index 64c1561..924a042 100644 --- a/monitor.h +++ b/monitor.h @@ -84,6 +84,8 @@ void monitor_read_command(Monitor *mon, int show_prompt); ReadLineState *monitor_get_rs(Monitor *mon); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); +int monitor_read_console(Monitor *mon, const char *device, + ReadLineFunc *readline_func, void *opaque); int qmp_qom_set(Monitor *mon, const QDict *qdict, QObject **ret); -- 1.7.7.6
[Qemu-devel] [PATCH 3/5] QAPI: Introduce memchar-write QMP command
Signed-off-by: Lei Li --- hmp-commands.hx | 23 ++ hmp.c| 19 +++ hmp.h|1 + qapi-schema.json | 69 ++ qemu-char.c | 48 + qmp-commands.hx | 39 ++ 6 files changed, 199 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index ed67e99..fe11926 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -796,6 +796,29 @@ Inject an NMI on the given CPU (x86 only). ETEXI { +.name = "memchar_write", +.args_type = "chardev:s,size:i,data:s,format:s?,control:s?", +.params = "chardev size data [format] [control]", +.help = "Provide writing interface for CirMemCharDriver. Write" + "'data' to it with size 'size'", +.mhandler.cmd = hmp_memchar_write, +}, + +STEXI +@item memchar_write @var{chardev} @var{size} @var{data} [@var{format}] [@var{control}] +@findex memchar_write +Provide writing interface for CirMemCharDriver. Write @var{data} +to cirmemchr char device with size @var{size}. + +@var{format} is the format of the data write to CirMemCharDriver. +Support 'utf8' and 'base64', by default is 'utf8'. + +@var{control} is option('block', 'drop') for read and write command +that specifies behavior when the queue is full/empty. By default is +'drop'. Note that the 'block' option is not supported now. +ETEXI + +{ .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .params = "[-d] [-b] [-i] uri", diff --git a/hmp.c b/hmp.c index ba6fbd3..97f5058 100644 --- a/hmp.c +++ b/hmp.c @@ -671,6 +671,25 @@ 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 = qdict_get_int(qdict, "size"); +const char *chardev = qdict_get_str(qdict, "chardev"); +const char *data = qdict_get_str(qdict, "data"); +int val = qdict_get_try_bool(qdict, "base64", 0); +enum DataFormat format; +int con = qdict_get_try_bool(qdict, "block", 0); +enum CongestionControl control; +Error *errp = NULL; + +format = val ? DATA_FORMAT_BASE64 : DATA_FORMAT_UTF8; +control = con ? CONGESTION_CONTROL_BLOCK : CONGESTION_CONTROL_DROP; +qmp_memchar_write(chardev, size, data, true, format, + true, control, &errp); + +hmp_handle_error(mon, &errp); +} + static void hmp_cont_cb(void *opaque, int err) { if (!err) { diff --git a/hmp.h b/hmp.h index 48b9c59..44b6463 100644 --- a/hmp.h +++ b/hmp.h @@ -43,6 +43,7 @@ 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_cont(Monitor *mon, const QDict *qdict); void hmp_system_wakeup(Monitor *mon, const QDict *qdict); void hmp_inject_nmi(Monitor *mon, const QDict *qdict); diff --git a/qapi-schema.json b/qapi-schema.json index a9f465a..371239a 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -236,6 +236,75 @@ { 'command': 'query-chardev', 'returns': ['ChardevInfo'] } ## +# @DataFormat: +# +# An enumeration of data format write to or read from +# memchardev. The default value would be utf8. +# +# @utf8: The data format is 'utf8'. +# +# @base64: The data format is 'base64'. +# +# Note: The data format start with 'utf8' and 'base64', will support +# other data format as well. +# +# Since: 1.3 +## +{ 'enum': 'DataFormat' + 'data': [ 'utf8', 'base64' ] } + +## +# @CongestionControl +# +# An enumeration of options for the read and write command that +# specifies behavior when the queue is full/empty. The default +# option would be dropped. +# +# @drop: Would result in reads returning empty strings and writes +#dropping queued data. +# +# @block: Would make the session block until data was available +# or the queue had space available. +# +# Note: The option 'block' is not supported now due to the miss +# feature in qmp. Will add it later when we gain the necessary +# infrastructure enhancement. +# +# Since: 1.3 +## +{'enum': 'CongestionControl' + 'data': [ 'drop', 'block' ] } + +## +# @memchar-write: +# +# Provide writing interface for CirMemCharDriver. Write data to cirmemchar +# char device. +# +# @chardev: the
[Qemu-devel] [PATCH 0/2 v3] Time resync support by qemu-ga
This patch series attempts to add time resync support to qemu-ga by introducing qemu-ga commands guest-get-time and guest-set-time. Right now, when a guest is paused or migrated to a file then loaded from that file, the guest OS has no idea that there was a big gap in the time. Depending on how long the gap was, NTP might not be able to resynchronize the guest. So adding new guest-agent command that is called any time a guest is resumed and which tells the guest to update its own wall clock time based on the information from the host will make it easier for a guest to resynchronize without waiting for NTP. The previous RFC send for discussion and suggestion as link here: http://article.gmane.org/gmane.comp.emulators.qemu/186126 The interface for these commands like: { 'command': 'guest-get-time', 'returns': 'int' } { 'command': 'guest-set-time', 'data': { 'time': int } } Notes: For the implementition of win32-specific commands, I plan to send it out in another thread later. Suggestions and comments are welcome! Changes since v2: - Get rid of utc-offset, and make it just pass single nanoseconds relative to the Epoch in UTC/GMT according to Anthony and Eric's comments. - Make time argument mandatory. - Fix the overflow check for year-2038 problem. - Error handel improvment from Eric. Changes since v1: - Squashed patches add support to get host time and add guest-get-time command into one. - Documents improvment based on the suggestions from Eric and Mike. - Change the name of 'HostTimeInfo' to 'TimeInfo'. - Better use-case and logic for 'guest-set-time' command suggested by Eric. - Error handel improvment from Luiz. Lei Li (2): qga: add guest-get-time command qga: add guest-set-time command
[Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
Signed-off-by: Lei Li --- qga/commands-posix.c | 16 qga/qapi-schema.json | 16 2 files changed, 32 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0ad73f3..f159e25 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -119,6 +119,22 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* succeded */ } +int64_t qmp_guest_get_time(Error **errp) +{ + int ret; + qemu_timeval tq; + int64_t time_ns; + + ret = qemu_gettimeofday(&tq); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to get time"); + return -1; + } + + time_ns = tq.tv_sec * 10LL + tq.tv_usec * 1000; + return time_ns; +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d91d903..563600c 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -83,6 +83,22 @@ { 'command': 'guest-ping' } ## +# @guest-get-time: +# +# Get the information about guest time relative to the Epoch +# of 1970-01-01 in UTC/GMT. +# +# This command try to get the guest's notion of the current +# time. +# +# Returns: Time in nanoseconds on success. +# +# Since 1.5 +## +{ 'command': 'guest-get-time', + 'returns': 'int' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.11.7
[Qemu-devel] [PATCH 2/2] qga: add guest-set-time command
Signed-off-by: Lei Li --- qga/commands-posix.c | 55 qga/qapi-schema.json | 27 ++ 2 files changed, 82 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index f159e25..e246a0d 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -135,6 +135,61 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } +void qmp_guest_set_time(int64_t time_ns, Error **errp) +{ +int ret; +int status; +pid_t pid; +Error *local_err = NULL; +struct timeval tv; + +/* year-2038 will overflow in case time_t is 32bit */ +if ((sizeof(time_t) <= 4) && ((unsigned long) time_ns & (1ul << 31))) { +error_setg_errno(errp, errno, "Invalid time %ld for overflow", + time_ns); +} + +tv.tv_sec = time_ns / 10; +tv.tv_usec = (time_ns % 10) / 1000; + +ret = settimeofday(&tv, NULL); +if (ret < 0) { +error_setg_errno(errp, errno, "Failed to set time to guest"); +return; +} + +/* Set the Hardware Clock to the current System Time. */ +pid = fork(); +if (pid == 0) { +setsid(); +reopen_fd_to_null(0); +reopen_fd_to_null(1); +reopen_fd_to_null(2); + +execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); +_exit(EXIT_FAILURE); +} else if (pid < 0) { +error_setg_errno(errp, errno, "failed to create child process"); +return; +} + +ga_wait_child(pid, &status, &local_err); +if (error_is_set(&local_err)) { +error_propagate(errp, local_err); +return; +} + +if (!WIFEXITED(status)) { +error_setg(errp, "child process has terminated abnormally"); +return; +} + +if (WEXITSTATUS(status)) { +error_setg(errp, "hwclock failed to set hardware clock to system time"); +return; +} +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 563600c..5eba324 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -99,6 +99,33 @@ 'returns': 'int' } ## +# @guest-set-time: +# +# Set guest time. +# +# Right now, when a guest is paused or migrated to a file +# then loaded from that file, the guest OS has no idea that +# there was a big gap in the time. Depending on how long +# the gap was, NTP might not be able to resynchronize the +# guest. +# +# This command tries to set guest time based on the information +# from host or an absolute value given by management app, and +# set the Hardware Clock to the current System Time. This +# will make it easier for a guest to resynchronize without +# waiting for NTP. +# +# @time: time of nanoseconds, relative to the Epoch of +#1970-01-01 in UTC/GMT. +# +# Returns: Nothing on success. +# +# Since: 1.5 +## +{ 'command': 'guest-set-time', + 'data': { 'time': 'int' } } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.11.7
Re: [Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
On 03/02/2013 01:02 AM, Eric Blake wrote: On 03/01/2013 02:32 AM, Lei Li wrote: Signed-off-by: Lei Li --- qga/commands-posix.c | 16 qga/qapi-schema.json | 16 2 files changed, 32 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0ad73f3..f159e25 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -119,6 +119,22 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* succeded */ } +int64_t qmp_guest_get_time(Error **errp) +{ + int ret; + qemu_timeval tq; + int64_t time_ns; + + ret = qemu_gettimeofday(&tq); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to get time"); + return -1; + } + + time_ns = tq.tv_sec * 10LL + tq.tv_usec * 1000; Is it worth a sanity check that the tv_sec scaling doesn't overflow? Of course, that won't happen until far into the future (well beyond the 2038 overflow of 32-bit seconds since Epoch), so it won't hit in OUR lifetime, so I can look the other way. ## +# @guest-get-time: +# +# Get the information about guest time relative to the Epoch +# of 1970-01-01 in UTC/GMT. UTC and GMT are not the same thing. I'd drop the '/GMT'. http://www.diffen.com/difference/GMT_vs_UTC Oh, good to know! :) Thanks. +# +# This command try to get the guest's notion of the current +# time. This sentence is redundant with the first one, and has grammar issues. Drop it. Sure. +# +# Returns: Time in nanoseconds on success. +# +# Since 1.5 +## +{ 'command': 'guest-get-time', + 'returns': 'int' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- Lei
Re: [Qemu-devel] [PATCH 2/2] qga: add guest-set-time command
On 03/02/2013 01:38 AM, Eric Blake wrote: On 03/01/2013 02:33 AM, Lei Li wrote: Signed-off-by: Lei Li --- qga/commands-posix.c | 55 qga/qapi-schema.json | 27 ++ 2 files changed, 82 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index f159e25..e246a0d 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -135,6 +135,61 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } +void qmp_guest_set_time(int64_t time_ns, Error **errp) +{ +int ret; +int status; +pid_t pid; +Error *local_err = NULL; +struct timeval tv; + +/* year-2038 will overflow in case time_t is 32bit */ +if ((sizeof(time_t) <= 4) && ((unsigned long) time_ns & (1ul << 31))) { Wrong. If 'unsigned long' and 'time_t' are both 32 bits, but time_ns is 0x1000, then the cast truncates to 0 and you don't report overflow. Conversely, if time_ns is 0x8000, you report overflow, even though this value fits in 32-bit time_t after you do division from nanoseconds back to seconds. What you WANT is: Yes, you are right.. thanks. if (time_ns / 10 != (time_t)(time_ns / 10)) { +error_setg_errno(errp, errno, "Invalid time %ld for overflow", + time_ns); That wording sounds awkward. Worse, errno is NOT set to anything sane, so you do NOT want error_setg_errno. And %ld is wrong for int64_t on 32-bit platforms. How about: error_setg(errp, "Time %" PRI64D " is too large", time_ns); Sure, will address it as the your later reply. +++ b/qga/qapi-schema.json @@ -99,6 +99,33 @@ 'returns': 'int' } ## +# @guest-set-time: +# +# Set guest time. +# +# Right now, when a guest is paused or migrated to a file s/Right now, when/When/ +# then loaded from that file, the guest OS has no idea that +# there was a big gap in the time. Depending on how long +# the gap was, NTP might not be able to resynchronize the +# guest. +# +# This command tries to set guest time based on the information +# from host or an absolute value given by management app, and +# set the Hardware Clock to the current System Time. This +# will make it easier for a guest to resynchronize without +# waiting for NTP. +# +# @time: time of nanoseconds, relative to the Epoch of +#1970-01-01 in UTC/GMT. drop '/GMT' +# +# Returns: Nothing on success. +# +# Since: 1.5 +## +{ 'command': 'guest-set-time', + 'data': { 'time': 'int' } } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- Lei
[Qemu-devel] [PATCH 0/2 v4] Time resync support by qemu-ga
This patch series attempts to add time resync support to qemu-ga by introducing qemu-ga commands guest-get-time and guest-set-time. Right now, when a guest is paused or migrated to a file then loaded from that file, the guest OS has no idea that there was a big gap in the time. Depending on how long the gap was, NTP might not be able to resynchronize the guest. So adding new guest-agent command that is called any time a guest is resumed and which tells the guest to update its own wall clock time based on the information from the host will make it easier for a guest to resynchronize without waiting for NTP. The previous RFC send for discussion and suggestion as link here: http://article.gmane.org/gmane.comp.emulators.qemu/186126 The interface for these commands like: { 'command': 'guest-get-time', 'returns': 'int' } { 'command': 'guest-set-time', 'data': { 'time': int } } Notes: For the implementition of win32-specific commands, I plan to send it out in another thread later. Suggestions and comments are welcome! Changes since v3: - Doc improvement based on Eric's suggestions. - Overflow check improve from Eric. Changes since v2: - Get rid of utc-offset, and make it just pass single nanoseconds relative to the Epoch in UTC/GMT according to Anthony and Eric's comments. - Make time argument mandatory. - Fix the overflow check for year-2038 problem. - Error handel improvment from Eric. Changes since v1: - Squashed patches add support to get host time and add guest-get-time command into one. - Documents improvment based on the suggestions from Eric and Mike. - Change the name of 'HostTimeInfo' to 'TimeInfo'. - Better use-case and logic for 'guest-set-time' command suggested by Eric. - Error handel improvment from Luiz. Lei Li (2): qga: add guest-get-time command qga: add guest-set-time command
[Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
Signed-off-by: Lei Li --- qga/commands-posix.c | 16 qga/qapi-schema.json | 13 + 2 files changed, 29 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0ad73f3..6fc6003 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -119,6 +119,22 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* succeded */ } +int64_t qmp_guest_get_time(Error **errp) +{ + int ret; + qemu_timeval tq; + int64_t time_ns; + + ret = qemu_gettimeofday(&tq); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to get time"); + return -1; + } + + time_ns = tq.tv_sec * 10LL + tq.tv_usec * 1000; + return time_ns; +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d91d903..52bb091 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -83,6 +83,19 @@ { 'command': 'guest-ping' } ## +# @guest-get-time: +# +# Get the information about guest time relative to the Epoch +# of 1970-01-01 in UTC. +# +# Returns: Time in nanoseconds on success. +# +# Since 1.5 +## +{ 'command': 'guest-get-time', + 'returns': 'int' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.11.7
[Qemu-devel] [PATCH 2/2] qga: add guest-set-time command
Signed-off-by: Lei Li --- qga/commands-posix.c | 54 qga/qapi-schema.json | 27 ++ 2 files changed, 81 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 6fc6003..0515f5f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -135,6 +135,60 @@ int64_t qmp_guest_get_time(Error **errp) return time_ns; } +void qmp_guest_set_time(int64_t time_ns, Error **errp) +{ +int ret; +int status; +pid_t pid; +Error *local_err = NULL; +struct timeval tv; + +/* year-2038 will overflow in case time_t is 32bit */ +if (time_ns / 10 != (time_t)(time_ns / 10)) { +error_setg(errp, "Time %" PRId64 " is too large", time_ns); +} + +tv.tv_sec = time_ns / 10; +tv.tv_usec = (time_ns % 10) / 1000; + +ret = settimeofday(&tv, NULL); +if (ret < 0) { +error_setg_errno(errp, errno, "Failed to set time to guest"); +return; +} + +/* Set the Hardware Clock to the current System Time. */ +pid = fork(); +if (pid == 0) { +setsid(); +reopen_fd_to_null(0); +reopen_fd_to_null(1); +reopen_fd_to_null(2); + +execle("/sbin/hwclock", "hwclock", "-w", NULL, environ); +_exit(EXIT_FAILURE); +} else if (pid < 0) { +error_setg_errno(errp, errno, "failed to create child process"); +return; +} + +ga_wait_child(pid, &status, &local_err); +if (error_is_set(&local_err)) { +error_propagate(errp, local_err); +return; +} + +if (!WIFEXITED(status)) { +error_setg(errp, "child process has terminated abnormally"); +return; +} + +if (WEXITSTATUS(status)) { +error_setg(errp, "hwclock failed to set hardware clock to system time"); +return; +} +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 52bb091..ce964e9 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -96,6 +96,33 @@ 'returns': 'int' } ## +# @guest-set-time: +# +# Set guest time. +# +# When a guest is paused or migrated to a file then loaded +# from that file, the guest OS has no idea that there +# was a big gap in the time. Depending on how long the +# gap was, NTP might not be able to resynchronize the +# guest. +# +# This command tries to set guest time based on the information +# from host or an absolute value given by management app, and +# set the Hardware Clock to the current System Time. This +# will make it easier for a guest to resynchronize without +# waiting for NTP. +# +# @time: time of nanoseconds, relative to the Epoch of +#1970-01-01 in UTC. +# +# Returns: Nothing on success. +# +# Since: 1.5 +## +{ 'command': 'guest-set-time', + 'data': { 'time': 'int' } } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.11.7
[Qemu-devel] [PATCH 1/2] qga: add guest-get-time command
Signed-off-by: Lei Li --- qga/commands-posix.c | 16 qga/qapi-schema.json | 13 + 2 files changed, 29 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7a0202e..1426262 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -119,6 +119,22 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) /* succeded */ } +int64_t qmp_guest_get_time(Error **errp) +{ + int ret; + qemu_timeval tq; + int64_t time_ns; + + ret = qemu_gettimeofday(&tq); + if (ret < 0) { + error_setg_errno(errp, errno, "Failed to get time"); + return -1; + } + + time_ns = tq.tv_sec * 10LL + tq.tv_usec * 1000; + return time_ns; +} + typedef struct GuestFileHandle { uint64_t id; FILE *fh; diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d91d903..bb0f75e 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -83,6 +83,19 @@ { 'command': 'guest-ping' } ## +# @guest-get-time: +# +# Get the information about guest time relative to the Epoch +# of 1970-01-01 in UTC. +# +# Returns: Time in nanoseconds. +# +# Since 1.5 +## +{ 'command': 'guest-get-time', + 'returns': 'int' } + +## # @GuestAgentCommandInfo: # # Information about guest agent commands. -- 1.7.11.7
[Qemu-devel] [PATCH 0/2 v5] Time resync support by qemu-ga
This patch series attempts to add time resync support to qemu-ga by introducing qemu-ga commands guest-get-time and guest-set-time. Right now, when a guest is paused or migrated to a file then loaded from that file, the guest OS has no idea that there was a big gap in the time. Depending on how long the gap was, NTP might not be able to resynchronize the guest. So adding new guest-agent command that is called any time a guest is resumed and which tells the guest to update its own wall clock time based on the information from the host will make it easier for a guest to resynchronize without waiting for NTP. The previous RFC send for discussion and suggestion as link here: http://article.gmane.org/gmane.comp.emulators.qemu/186126 The interface for these commands like: { 'command': 'guest-get-time', 'returns': 'int' } { 'command': 'guest-set-time', 'data': { 'time': int } } Notes: For the implementition of win32-specific commands, I plan to send it out in another thread later. Suggestions and comments are welcome! Changes since v4: - Fix the missing error exit pointed by Eric. - Doc improvement from Eric. Changes since v3: - Doc improvement based on Eric's suggestions. - Overflow check improve from Eric. Changes since v2: - Get rid of utc-offset, and make it just pass single nanoseconds relative to the Epoch in UTC/GMT according to Anthony and Eric's comments. - Make time argument mandatory. - Fix the overflow check for year-2038 problem. - Error handel improvment from Eric. Changes since v1: - Squashed patches add support to get host time and add guest-get-time command into one. - Documents improvment based on the suggestions from Eric and Mike. - Change the name of 'HostTimeInfo' to 'TimeInfo'. - Better use-case and logic for 'guest-set-time' command suggested by Eric. - Error handel improvment from Luiz. Lei Li (2): qga: add guest-get-time command qga: add guest-set-time command