From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=38729 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Q3EzS-0002Vi-Nb for qemu-devel@nongnu.org; Fri, 25 Mar 2011 17:56:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Q3EzR-0006xS-8K for qemu-devel@nongnu.org; Fri, 25 Mar 2011 17:56:18 -0400 Received: from e5.ny.us.ibm.com ([32.97.182.145]:37084) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Q3EzR-0006x7-3t for qemu-devel@nongnu.org; Fri, 25 Mar 2011 17:56:17 -0400 Received: from d01dlp01.pok.ibm.com (d01dlp01.pok.ibm.com [9.56.224.56]) by e5.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p2PLUpV4022137 for ; Fri, 25 Mar 2011 17:30:51 -0400 Received: from d01relay02.pok.ibm.com (d01relay02.pok.ibm.com [9.56.227.234]) by d01dlp01.pok.ibm.com (Postfix) with ESMTP id 7F6C038C8039 for ; Fri, 25 Mar 2011 17:56:07 -0400 (EDT) Received: from d03av04.boulder.ibm.com (d03av04.boulder.ibm.com [9.17.195.170]) by d01relay02.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p2PLuCJq476722 for ; Fri, 25 Mar 2011 17:56:12 -0400 Received: from d03av04.boulder.ibm.com (loopback [127.0.0.1]) by d03av04.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p2PLuB9x017557 for ; Fri, 25 Mar 2011 15:56:12 -0600 Message-ID: <4D8D0F79.6030005@linux.vnet.ibm.com> Date: Fri, 25 Mar 2011 16:56:09 -0500 From: Michael Roth MIME-Version: 1.0 References: <1301082479-4058-1-git-send-email-mdroth@linux.vnet.ibm.com> <1301082479-4058-8-git-send-email-mdroth@linux.vnet.ibm.com> <4D8D08DE.8050805@us.ibm.com> In-Reply-To: <4D8D08DE.8050805@us.ibm.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: [Qemu-devel] Re: [RFC][PATCH v1 07/12] qmp proxy: core code for proxying qmp requests to guest List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Anthony Liguori Cc: aliguori@linux.vnet.ibm.com, agl@linux.vnet.ibm.com, qemu-devel@nongnu.org, Jes.Sorensen@redhat.com On 03/25/2011 04:27 PM, Anthony Liguori wrote: > On 03/25/2011 02:47 PM, Michael Roth wrote: >> This provides a QmpProxy class, 1 instance of which is shared by all QMP >> servers/sessions to send/receive QMP requests/responses between QEMU and >> the QEMU guest agent. >> >> A single qmp_proxy_send_request() is the only interface currently needed >> by a QMP session, QAPI/QMP's existing async support handles all the work >> of doing callbacks and routing responses to the proper session. >> >> Currently the class requires a path to a listening socket that either >> corresponds to the chardev that the guest agent is communicating >> through, or a local socket so we can communicate with a host-side >> "guest" agent for testing purposes. >> >> A subsequent patch will introduce a new chardev that sets up the >> socket chardev and initializes the QmpProxy instance to abstract this >> away from the user. Unifying this with local "guest" agent support may >> not be feasible, so another command-line option may be needed support >> host-side-only testing. >> >> Signed-off-by: Michael Roth >> --- >> qmp-core.c | 8 ++ >> qmp-core.h | 7 +- >> qmp-proxy-core.h | 20 ++++ >> qmp-proxy.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> vl.c | 1 + >> 5 files changed, 365 insertions(+), 6 deletions(-) >> create mode 100644 qmp-proxy-core.h >> create mode 100644 qmp-proxy.c >> >> diff --git a/qmp-core.c b/qmp-core.c >> index 9f3d182..dab50a1 100644 >> --- a/qmp-core.c >> +++ b/qmp-core.c >> @@ -937,7 +937,15 @@ void qmp_async_complete_command(QmpCommandState >> *cmd, QObject *retval, Error *er >> qemu_free(cmd); >> } >> >> +extern QmpProxy *qmp_proxy_default; >> + >> void qmp_guest_dispatch(const char *name, const QDict *args, Error >> **errp, >> QmpGuestCompletionFunc *cb, void *opaque) >> { >> + if (!qmp_proxy_default) { >> + /* TODO: should set errp here */ >> + fprintf(stderr, "qmp proxy: no guest proxy found\n"); >> + return; >> + } >> + qmp_proxy_send_request(qmp_proxy_default, name, args, errp, cb, >> opaque); >> } >> diff --git a/qmp-core.h b/qmp-core.h >> index b676020..114d290 100644 >> --- a/qmp-core.h >> +++ b/qmp-core.h >> @@ -4,6 +4,7 @@ >> #include "monitor.h" >> #include "qmp-marshal-types.h" >> #include "error_int.h" >> +#include "qmp-proxy-core.h" >> >> struct QmpCommandState >> { >> @@ -85,11 +86,5 @@ int qmp_state_get_fd(QmpState *sess); >> } \ >> } while(0) >> >> -typedef void (QmpGuestCompletionFunc)(void *opaque, QObject >> *ret_data, Error *err); >> - >> -void qmp_guest_dispatch(const char *name, const QDict *args, Error >> **errp, >> - QmpGuestCompletionFunc *cb, void *opaque); >> - >> - >> #endif >> >> diff --git a/qmp-proxy-core.h b/qmp-proxy-core.h >> new file mode 100644 >> index 0000000..47ac85d >> --- /dev/null >> +++ b/qmp-proxy-core.h >> @@ -0,0 +1,20 @@ >> +#ifndef QMP_PROXY_CORE_H >> +#define QMP_PROXY_CORE_H >> + >> +#define QMP_PROXY_PATH_DEFAULT "/tmp/qmp-proxy.sock" >> + >> +typedef void (QmpGuestCompletionFunc)(void *opaque, QObject *ret_data, >> + Error *err); >> + >> +void qmp_guest_dispatch(const char *name, const QDict *args, Error >> **errp, >> + QmpGuestCompletionFunc *cb, void *opaque); >> + >> +typedef struct QmpProxy QmpProxy; >> + >> +void qmp_proxy_send_request(QmpProxy *p, const char *name, >> + const QDict *args, Error **errp, >> + QmpGuestCompletionFunc *cb, void *opaque); >> +QmpProxy *qmp_proxy_new(const char *channel_path); >> +void qmp_proxy_close(QmpProxy *p); >> + >> +#endif >> diff --git a/qmp-proxy.c b/qmp-proxy.c >> new file mode 100644 >> index 0000000..eaa6e6e >> --- /dev/null >> +++ b/qmp-proxy.c >> @@ -0,0 +1,335 @@ >> +/* >> + * QMP definitions for communicating with guest agent >> + * >> + * Copyright IBM Corp. 2011 >> + * >> + * Authors: >> + * Michael Roth >> + * >> + * This work is licensed under the terms of the GNU GPL, version 2 or >> later. >> + * See the COPYING file in the top-level directory. >> + * >> + */ >> + >> +#include "qmp.h" >> +#include "qmp-core.h" >> +#include "qemu-queue.h" >> +#include "json-parser.h" >> +#include "json-streamer.h" >> +#include "qemu_socket.h" >> + >> +#define QMP_SENTINEL 0xFF >> + >> +typedef struct QmpProxyRequest { >> + const char *name; >> + const QDict *args; >> + QmpGuestCompletionFunc *cb; >> + void *opaque; >> + QString *json; >> + QTAILQ_ENTRY(QmpProxyRequest) entry; >> +} QmpProxyRequest; >> + >> +typedef struct QmpProxyWriteState { >> + QmpProxyRequest *current_request; >> + const char *buf; >> + size_t size; >> + size_t pos; >> + bool use_sentinel; >> +} QmpProxyWriteState; >> + >> +typedef struct QmpProxyReadState { >> + char *buf; >> + size_t size; >> + size_t pos; >> +} QmpProxyReadState; > > I suspect you should use GStrings for both the rx and tx buffers. See > the QmpUnixSession for an example. > >> +struct QmpProxy { >> + int fd; >> + const char *path; >> + QmpProxyWriteState write_state; >> + QmpProxyReadState read_state; >> + JSONMessageParser parser; >> + QTAILQ_HEAD(, QmpProxyRequest) sent_requests; >> + QTAILQ_HEAD(, QmpProxyRequest) queued_requests; >> + QString *xport_event; >> + QString *xport_event_sending; >> +}; >> + >> +static void qmp_proxy_read_handler(void *opaque); >> +static void qmp_proxy_write_handler(void *opaque); >> + >> +static int qmp_proxy_cancel_request(QmpProxy *p, QmpProxyRequest *r) >> +{ >> + if (r->name) { >> + if (r->cb) { >> + r->cb(r->opaque, NULL, NULL); >> + } >> + } >> + >> + return 0; >> +} >> + >> +static int qmp_proxy_cancel_all(QmpProxy *p) >> +{ >> + QmpProxyRequest *r, *tmp; >> + QTAILQ_FOREACH_SAFE(r,&p->queued_requests, entry, tmp) { >> + qmp_proxy_cancel_request(p, r); >> + QTAILQ_REMOVE(&p->queued_requests, r, entry); >> + } >> + QTAILQ_FOREACH_SAFE(r,&p->sent_requests, entry, tmp) { >> + qmp_proxy_cancel_request(p, r); >> + QTAILQ_REMOVE(&p->queued_requests, r, entry); >> + } >> + >> + return 0; >> +} >> + >> +static void qmp_proxy_send_host_ack(QmpProxy *p, int session_id) >> +{ >> + QDict *evt = qdict_new(); >> + >> + /* only the last ack matters, nuke any outstanding ones. need to >> rethink >> + * this approach if a host->guest reset event is added >> + */ >> + if (p->xport_event) { >> + QDECREF(p->xport_event); >> + } >> + >> + qdict_put_obj(evt, "_xport_event", >> QOBJECT(qstring_from_str("host_ack"))); >> + qdict_put_obj(evt, "_xport_arg_sid", >> QOBJECT(qint_from_int(session_id))); > > I don't quite follow what this is doing. > That's for the session negotiation so we can reset state when the guest agent restarts. The sequence is: guest -> host { "_xport_event": "guest_init", "_xport_arg_sid": } host -> guest { "_xport_event": "host_ack", "_xport_arg_sid": } Guest will ignore anything it gets until it sees an ack with the proper session id, host will cancel outstanding requests when it receives a guest init. If there's already an event waiting to be sent we clobber it since for the above exchange only the most recent event we sent matters. We send them as json objects, but they get handled at transport level and terminate there.