From: Anthony PERARD <anthony.perard@citrix.com>
To: Ian Jackson <ian.jackson@citrix.com>
Cc: xen-devel@lists.xenproject.org, Wei Liu <wei.liu2@citrix.com>
Subject: Re: [PATCH v5 03/15] libxl_qmp: Implement fd callback and read data [and 1 more messages]
Date: Mon, 29 Oct 2018 15:52:31 +0000 [thread overview]
Message-ID: <20181029155231.GC6481@perard.uk.xensource.com> (raw)
In-Reply-To: <23492.49624.248349.953540@mariner.uk.xensource.com>
On Mon, Oct 15, 2018 at 05:35:36PM +0100, Ian Jackson wrote:
> > +static int qmp_error_class_to_libxl_error_code(const libxl__qmp_error_class c)
> > +{
> > + switch (c) {
> > + case LIBXL__QMP_ERROR_CLASS_GENERICERROR:
> > + return ERROR_QMP_GENERIC_ERROR;
> > + case LIBXL__QMP_ERROR_CLASS_COMMANDNOTFOUND:
> > + return ERROR_QMP_COMMAND_NOT_FOUND;
> > + case LIBXL__QMP_ERROR_CLASS_DEVICENOTFOUND:
> > + return ERROR_QMP_DEVICE_NOT_FOUND;
>
> Urgh. The slightly different names means that this can't be
> macro-ified. Without that, it would be really easy for someone in the
> future to accidentally write something like this:
>
> > + case LIBXL__QMP_ERROR_CLASS_GENERICERROR:
> > + return ERROR_QMP_GENERIC_ERROR;
> > + case LIBXL__QMP_ERROR_CLASS_COMMANDNOTFOUND:
> > + return ERROR_QMP_GENERIC_ERROR;
> > + case LIBXL__QMP_ERROR_CLASS_DEVICENOTFOUND:
> > + return ERROR_QMP_DEVICE_NOT_FOUND;
>
> and it's hard to spot.
>
> What are LIBXL__QMP_ERROR_CLASSes and why are they even different from
> ERROR_* values ? Maybe one of them is the QMP integer values and one
> of them is the corresponding libxl integer values ?
The issue here is that, in QMP, the error is a string and in camel case,
e.g. "GenericError". I've use the idl to generate an enum from the
different error strings.
> Anyway, can we not make this less open-coded somehow.
I could remove some underscores '_' from the libxl ERROR_* integer
values, e.g. ERROR_QMP_GENERICERROR. But if you have a better suggestion
on how to transform "CommandNotFound" into a ERROR_QMP_*, I'll take it.
> > + default:
> > + abort();
>
> But what happens if qemu invents a new error code ? I don't think
> aborting is likely to be right.
If qemu invent a new error code, libxl should not call
qmp_error_class_to_libxl_error_code(), because libxl would not have been
able to generate a libxl__qmp_error_class from the new error code.
But I could add an assert(false) and return ERROR_FAIL instead of the
abort.
> > +/* return 1 when a user callback as been called */
> > +static int qmp_ev_handle_response(libxl__egc *egc,
> > + libxl__ev_qmp *ev,
> > + const libxl__json_object *resp,
> > + libxl__qmp_message_type type)
> > +{
> > + EGC_GC;
> > + const libxl__json_object *response;
> > + const libxl__json_object *o;
> > + int rc;
> > + int id;
> > +
> > + o = libxl__json_map_get("id", resp, JSON_INTEGER);
> > + if (!o) {
> > + const char *error_desc;
> > +
> > + /* unexpected message, attempt to find an error description */
> > + o = libxl__json_map_get("error", resp, JSON_MAP);
> > + o = libxl__json_map_get("desc", o, JSON_STRING);
>
> What is the dead store from "error" doing ?
It's not dead, I reuse "o", as I get "desc" from "o".
I could just create new variables here instead of reusing the same one
(o) over and over again, e.g. obj_id, obj_error, obj_desc.
Should I add here that we are parsing: {"error":{"desc": X }} ?
> > + switch (type) {
> > + case LIBXL__QMP_MESSAGE_TYPE_RETURN: {
> > + response = libxl__json_map_get("return", resp, JSON_ANY);
> > + rc = 0;
> > + break;
> > + }
> > + case LIBXL__QMP_MESSAGE_TYPE_ERROR: {
> > + const char *s;
> > + const libxl__json_object *err;
> > + libxl__qmp_error_class error_class;
> > +
> > + rc = ERROR_FAIL;
> > + response = NULL;
> > +
> > + err = libxl__json_map_get("error", resp, JSON_MAP);
> > + o = libxl__json_map_get("class", err, JSON_STRING);
> > + s = libxl__json_object_get_string(o);
> > + if (s && !libxl__qmp_error_class_from_string(s, &error_class))
> > + rc = qmp_error_class_to_libxl_error_code(error_class);
> > +
> > + o = libxl__json_map_get("desc", err, JSON_STRING);
> > + s = libxl__json_object_get_string(o);
> > + if (s)
> > + LOGD(ERROR, ev->domid, "%s", s);
> > +
> > + break;
>
> Surely this should print more debugging (or maybe even error log
> messages) if the error json document was not in the expected form ?
Do you mean that if the QMP server doesn't respect the QMP protocol, I
should print more debug here?
If we are at this point in the programme, we know that the server sent:
{"error": X }
I'm tempted to say that if "class" or "desc" isn't present, we could say
so and declare the server broken (and disconnect).
> > + /* Findout how much can be parsed */
> > + size_t len = end - s;
> > +
> > + LOG_QMP("parsing %luB: '%.*s'", len, (int)len, s);
> > +
> > + /* Replace \n by \0 so that libxl__json_parse can use strlen */
> > + s[len - 1] = '\0';
>
> Doesn't this feed the \r to libxl__json_parse ?
>
> Also, why does this have to be a loop ? Does qemu really send
> multiple json documents end to end, and only sometimes with
> intervening \r\n ? Does it ever send a json document without \r\n at
> the end and then stop speaking for a while ?
QEMU will send two messages at once, e.g. when asking to eject or change
the cdrom, QEMU will often send the response to our command and an event
at the same time. So we need a loop to parse all messages received.
But there is always a \r\n at the end of every commands.
> > + ev->buf_consumed += len;
> > +
> > + if (ev->buf_consumed >= ev->buf_used) {
> > + free(ev->rx_buf);
>
> Surely buf_consumed <= buf_used ? Maybe you should assert that.
I'm attempting to detect here if there's something left in the buffer
that hasn't been parsed yet. If there is nothing left, we can free the
buffer.
Or do you mean that the condition should be buf_consumed == buf_used ?
> > +static int qmp_ev_callback_writable(libxl__gc *gc, libxl__ev_qmp *ev, int fd)
> > +{
> > + int rc;
> > + char *buf;
> > + size_t len;
> > + int send_fd = -1;
> > +
> > + /* No need to call qmp_ev_callback_writable again, everything that needs to
> > + * be send for now will be in this call. */
>
> So you guarantee never to send any message which is too large to fit
> into a socket buffer ? Do you know what that length is ?
I'm using libxl_write_exactly().
On the other end, I've just realize that I'm also using
libxl__sendmsg_fds(), which I guess may not send everything. But the
function also doesn't seems to the caller know how much have been sent.
Should I keep using libxl_write_exactly() ?
I'm going to need to be able to track how much data have been sent for
at least sendmsg() anyway, and try again later.
> > +static void qmp_ev_callback_error(libxl__egc *egc, libxl__ev_qmp *ev)
> > +{
> > + EGC_GC;
> > +
> > + LOGD(ERROR, ev->domid, "Error happend with the QMP connection to QEMU");
> > +
> > + /* On error, deallocate all private ressources */
> > + libxl__ev_qmp_dispose(gc, ev);
>
> This surely needs to set the state. Presumably that should be done in
> libxl__ev_qmp_dispose but AFAICT it isn't.
It is done by libxl__ev_qmp_init, which _dispose calls, itn't that
enough?
> > +void libxl__ev_qmp_init(libxl__ev_qmp *ev)
> > +{
> > + ev->id = -1;
> > +
> > + ev->qmp_cfd = NULL;
> > + libxl__ev_fd_init(&ev->qmp_efd);
> > + ev->qmp_state = qmp_state_disconnected;
> > + ev->last_id_used = QMP_CAPABILITY_NEGOTIATION_MSGID + 1;
>
> Going back a bit, earlier we had:
>
> > +#define QMP_CAPABILITY_NEGOTIATION_MSGID 1
>
> I recommend using a different value. Can we safely let this wrap ?
> If so maybe we could use 0x786c7100 which spells "xlq\0" or something.
> This can make it easier to see where rogue values are coming from, to
> distinguish arguments in debug output, etc.
ID can be string, or any json value. Maybe we could have
{"libxl-id": number} as "id"?
But 0x786c7100 as starting point would work fine as well.
I don't think it matter if the value wrap. Beside adding 1 to generate a
new id, and compare, I don't think the id should be use for anything
else.
Thanks,
--
Anthony PERARD
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xenproject.org
https://lists.xenproject.org/mailman/listinfo/xen-devel
next prev parent reply other threads:[~2018-10-29 15:52 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20181015151630.3887-1-ian.jackson@eu.citrix.com>
2018-09-07 15:10 ` [PATCH v5 00/15] libxl: Enable save/restore/migration of a restricted QEMU + libxl__ev_qmp_* Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 01/15] libxl: Design of an async API to issue QMP commands to QEMU Anthony PERARD
2018-10-10 15:18 ` Ian Jackson
2018-10-11 11:17 ` Anthony PERARD
2018-10-11 11:21 ` Ian Jackson
2018-10-10 23:49 ` Marek Marczykowski-Górecki
2018-10-11 14:29 ` Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 02/15] libxl_qmp: Connect to QMP socket Anthony PERARD
2018-10-10 15:29 ` Ian Jackson
2018-10-11 11:27 ` Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 03/15] libxl_qmp: Implement fd callback and read data Anthony PERARD
2018-10-10 15:47 ` Ian Jackson
2018-10-11 14:06 ` Anthony PERARD
2018-10-15 14:04 ` Ian Jackson
2018-10-15 16:35 ` [PATCH v5 03/15] libxl_qmp: Implement fd callback and read data [and 1 more messages] Ian Jackson
2018-10-29 15:52 ` Anthony PERARD [this message]
2018-10-29 17:31 ` Ian Jackson
2018-10-30 18:03 ` Anthony PERARD
2018-10-30 18:25 ` Ian Jackson
2018-09-07 15:10 ` [PATCH v5 04/15] libxl_qmp: Parse JSON input from QMP Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 05/15] libxl_qmp: Separate QMP message generation from qmp_send_prepare Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 06/15] libxl_qmp: Prepare the command to be sent Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 07/15] libxl_qmp: Handle write to QMP socket Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 08/15] libxl_qmp: Handle messages from QEMU Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 09/15] libxl_qmp: Respond to QMP greeting Anthony PERARD
2018-09-07 15:10 ` [PATCH v5 10/15] libxl_exec: Add libxl__spawn_initiate_failure Anthony PERARD
2018-10-16 14:02 ` Ian Jackson
2018-11-09 12:26 ` Anthony PERARD
2018-09-07 15:11 ` [PATCH v5 11/15] libxl_dm: Pre-open QMP socket for QEMU Anthony PERARD
2018-10-16 14:11 ` Ian Jackson
2018-11-12 14:52 ` Anthony PERARD
2018-11-12 15:14 ` Ian Jackson
2018-11-12 15:22 ` Anthony PERARD
2018-11-12 15:54 ` Ian Jackson
2018-09-07 15:11 ` [PATCH v5 12/15] libxl: QEMU startup sync based on QMP Anthony PERARD
2018-10-16 14:23 ` Ian Jackson
2018-11-12 15:00 ` Anthony PERARD
2018-11-12 15:17 ` Ian Jackson
2018-09-07 15:11 ` [PATCH v5 13/15] libxl_qmp: Store advertised QEMU version in libxl__ev_qmp Anthony PERARD
2018-10-16 14:25 ` Ian Jackson
2018-09-07 15:11 ` [PATCH v5 14/15] libxl: Change libxl__domain_suspend_device_model() to be async Anthony PERARD
2018-10-16 15:14 ` Ian Jackson
2018-09-07 15:11 ` [PATCH v5 15/15] libxl: Re-implement domain_suspend_device_model using libxl__ev_qmp Anthony PERARD
2018-10-16 15:28 ` Ian Jackson
2018-11-09 16:59 ` Anthony PERARD
2018-11-09 17:11 ` Ian Jackson
2018-11-09 17:30 ` Anthony PERARD
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20181029155231.GC6481@perard.uk.xensource.com \
--to=anthony.perard@citrix.com \
--cc=ian.jackson@citrix.com \
--cc=wei.liu2@citrix.com \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).