aboutsummaryrefslogtreecommitdiffstats
path: root/android
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@google.com>2011-10-11 03:02:41 +0200
committerVladimir Chtchetkine <vchtchetkine@google.com>2011-11-08 11:41:10 -0800
commit3e92c2d49cb0e8752ce8c9a3c879c84ac3299061 (patch)
tree2b098d2ce7857f14550b688c0678a53732430cbd /android
parentc53b475e5bf2301da452cd2c81fed7c1fea2ec2a (diff)
downloadexternal_qemu-3e92c2d49cb0e8752ce8c9a3c879c84ac3299061.zip
external_qemu-3e92c2d49cb0e8752ce8c9a3c879c84ac3299061.tar.gz
external_qemu-3e92c2d49cb0e8752ce8c9a3c879c84ac3299061.tar.bz2
Fix snapshot crash
- Add snapshot load/save support to QEMU Pipes This adds the ability to save and load QEMU Pipe connections with snapshots. Note that by default, all loaded pipe client connections are force-fully closed on load. We don't have a good way to save the state of network connections to persistent storage. Properly implements snapshot save / load for qemu pipe clients. Change-Id: Ie5767f8ce40c8341b958cc5844e724dd4fc1ed2b
Diffstat (limited to 'android')
-rw-r--r--android/hw-pipe-net.c6
-rw-r--r--android/hw-qemud.c203
2 files changed, 183 insertions, 26 deletions
diff --git a/android/hw-pipe-net.c b/android/hw-pipe-net.c
index 193d60b..c2a7e9e 100644
--- a/android/hw-pipe-net.c
+++ b/android/hw-pipe-net.c
@@ -442,6 +442,8 @@ static const GoldfishPipeFuncs netPipeTcp_funcs = {
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
+ NULL, /* we can't save these */
+ NULL, /* we can't load these */
};
#ifndef _WIN32
@@ -452,6 +454,8 @@ static const GoldfishPipeFuncs netPipeUnix_funcs = {
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
+ NULL, /* we can't save these */
+ NULL, /* we can't load these */
};
#endif
@@ -517,6 +521,8 @@ static const GoldfishPipeFuncs openglesPipe_funcs = {
netPipe_recvBuffers,
netPipe_poll,
netPipe_wakeOn,
+ NULL, /* we can't save these */
+ NULL, /* we can't load these */
};
void
diff --git a/android/hw-qemud.c b/android/hw-qemud.c
index 0820e4c..df9ab21 100644
--- a/android/hw-qemud.c
+++ b/android/hw-qemud.c
@@ -20,6 +20,7 @@
#include "qemu-char.h"
#include "charpipe.h"
#include "cbuffer.h"
+#include "utils/panic.h"
#define D(...) VERBOSE_PRINT(qemud,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(qemud)
@@ -927,16 +928,16 @@ static QemudClient* qemud_service_connect_client( QemudService *sv,
const char* client_param);
/* Saves the client state needed to re-establish connections on load.
+ * Note that we save only serial clients here. The pipe clients will be
+ * saved along with the pipe to which they are attached.
*/
static void
-qemud_client_save(QEMUFile* f, QemudClient* c)
+qemud_serial_client_save(QEMUFile* f, QemudClient* c)
{
/* save generic information */
qemud_service_save_name(f, c->service);
- qemu_put_be32(f, c->protocol);
- if (!_is_pipe_client(c)) {
- qemu_put_be32(f, c->ProtocolSelector.Serial.channel);
- }
+ qemu_put_string(f, c->param);
+ qemu_put_be32(f, c->ProtocolSelector.Serial.channel);
/* save client-specific state */
if (c->clie_save)
@@ -957,14 +958,16 @@ qemud_client_save(QEMUFile* f, QemudClient* c)
/* Loads client state from file, then starts a new client connected to the
* corresponding service.
+ * Note that we load only serial clients here. The pipe clients will be
+ * loaded along with the pipe to which they were attached.
*/
static int
-qemud_client_load(QEMUFile* f, QemudService* current_services, int version )
+qemud_serial_client_load(QEMUFile* f, QemudService* current_services, int version )
{
char *service_name = qemud_service_load_name(f);
if (service_name == NULL)
return -EIO;
-
+ char* param = qemu_get_string(f);
/* get current service instance */
QemudService *sv = qemud_service_find(current_services, service_name);
if (sv == NULL) {
@@ -973,18 +976,7 @@ qemud_client_load(QEMUFile* f, QemudService* current_services, int version )
return -EIO;
}
- int channel = -1;
-
- if (version >= 2) {
- /* get protocol. */
- QemudProtocol protocol = qemu_get_be32(f);
- /* get channel id */
- if (protocol == QEMUD_PROTOCOL_SERIAL) {
- channel = qemu_get_be32(f);
- }
- } else {
- channel = qemu_get_be32(f);
- }
+ int channel = qemu_get_be32(f);
if (channel == 0) {
D("%s: illegal snapshot: client for control channel must no be saved\n",
@@ -993,7 +985,7 @@ qemud_client_load(QEMUFile* f, QemudService* current_services, int version )
}
/* re-connect client */
- QemudClient* c = qemud_service_connect_client(sv, channel, NULL);
+ QemudClient* c = qemud_service_connect_client(sv, channel, param);
if(c == NULL)
return -EIO;
@@ -1717,8 +1709,9 @@ qemud_client_save_count(QEMUFile* f, QemudClient* c)
{
unsigned int client_count = 0;
for( ; c; c = c->next) // walk over linked list
- // skip control channel, which is not saved
- if (_is_pipe_client(c) || c->ProtocolSelector.Serial.channel > 0)
+ /* skip control channel, which is not saved, and pipe channels that
+ * are saved along with the pipe. */
+ if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel > 0)
client_count++;
qemu_put_be32(f, client_count);
@@ -1760,9 +1753,9 @@ qemud_save(QEMUFile* f, void* opaque)
qemud_client_save_count(f, m->clients);
QemudClient *c;
for (c = m->clients; c; c = c->next) {
- /* skip control channel client */
- if (_is_pipe_client(c) || c->ProtocolSelector.Serial.channel > 0) {
- qemud_client_save(f, c);
+ /* skip control channel, and pipe clients */
+ if (!_is_pipe_client(c) && c->ProtocolSelector.Serial.channel > 0) {
+ qemud_serial_client_save(f, c);
}
}
@@ -1806,7 +1799,7 @@ qemud_load_clients(QEMUFile* f, QemudMultiplexer* m, int version )
int client_count = qemu_get_be32(f);
int i, ret;
for (i = 0; i < client_count; i++) {
- if ((ret = qemud_client_load(f, m->services, version))) {
+ if ((ret = qemud_serial_client_load(f, m->services, version))) {
return ret;
}
}
@@ -1839,6 +1832,46 @@ qemud_load(QEMUFile *f, void* opaque, int version)
*
* ----------------------------------------------------------------------------*/
+/* Saves pending pipe message to the snapshot file. */
+static void
+_save_pipe_message(QEMUFile* f, QemudPipeMessage* msg)
+{
+ qemu_put_be32(f, msg->size);
+ qemu_put_be32(f, msg->offset);
+ qemu_put_buffer(f, msg->message, msg->size);
+}
+
+/* Loads pending pipe messages from the snapshot file.
+ * Return:
+ * List of pending pipe messages loaded from snapshot, or NULL if snapshot didn't
+ * contain saved messages.
+ */
+static QemudPipeMessage*
+_load_pipe_message(QEMUFile* f)
+{
+ QemudPipeMessage* ret = NULL;
+ QemudPipeMessage** next = &ret;
+
+ uint32_t size = qemu_get_be32(f);
+ while (size != 0) {
+ QemudPipeMessage* wrk;
+ ANEW0(wrk);
+ *next = wrk;
+ wrk->size = size;
+ wrk->offset = qemu_get_be32(f);
+ wrk->message = malloc(wrk->size);
+ if (wrk->message == NULL) {
+ APANIC("Unable to allocate buffer for pipe's pending message.");
+ }
+ qemu_get_buffer(f, wrk->message, wrk->size);
+ next = &wrk->next;
+ *next = NULL;
+ size = qemu_get_be32(f);
+ }
+
+ return ret;
+}
+
/* This is a callback that gets invoked when guest is connecting to the service.
*
* Here we will create a new client as well as pipe descriptor representing new
@@ -2039,6 +2072,122 @@ _qemudPipe_wakeOn(void* opaque, int flags)
D("%s: -> %X", __FUNCTION__, flags);
}
+static void
+_qemudPipe_save(void* opaque, QEMUFile* f )
+{
+ QemudPipe* qemud_pipe = (QemudPipe*)opaque;
+ QemudClient* c = qemud_pipe->client;
+ QemudPipeMessage* msg = c->ProtocolSelector.Pipe.messages;
+
+ /* save generic information */
+ qemud_service_save_name(f, c->service);
+ qemu_put_string(f, c->param);
+
+ /* Save pending messages. */
+ while (msg != NULL) {
+ _save_pipe_message(f, msg);
+ msg = msg->next;
+ }
+ /* End of pending messages. */
+ qemu_put_be32(f, 0);
+
+ /* save client-specific state */
+ if (c->clie_save)
+ c->clie_save(f, c, c->clie_opaque);
+
+ /* save framing configuration */
+ qemu_put_be32(f, c->framing);
+ if (c->framing) {
+ qemu_put_be32(f, c->need_header);
+ /* header sink always connected to c->header0, no need to save */
+ qemu_put_be32(f, FRAME_HEADER_SIZE);
+ qemu_put_buffer(f, c->header0, FRAME_HEADER_SIZE);
+ /* payload sink */
+ qemud_sink_save(f, c->payload);
+ qemu_put_buffer(f, c->payload->buff, c->payload->size);
+ }
+}
+
+static void*
+_qemudPipe_load(void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* f)
+{
+ QemudPipe* qemud_pipe = NULL;
+ char* param;
+ char *service_name = qemud_service_load_name(f);
+ if (service_name == NULL)
+ return NULL;
+ /* get service instance for the loading client*/
+ QemudService *sv = qemud_service_find(_multiplexer->services, service_name);
+ if (sv == NULL) {
+ D("%s: load failed: unknown service \"%s\"\n",
+ __FUNCTION__, service_name);
+ return NULL;
+ }
+
+ /* Load saved parameters. */
+ param = qemu_get_string(f);
+
+ /* re-connect client */
+ QemudClient* c = qemud_service_connect_client(sv, -1, param);
+ if(c == NULL)
+ return NULL;
+
+ /* Load pending messages. */
+ c->ProtocolSelector.Pipe.messages = _load_pipe_message(f);
+
+ /* load client-specific state */
+ if (c->clie_load && c->clie_load(f, c, c->clie_opaque)) {
+ /* load failure */
+ return NULL;
+ }
+
+ /* load framing configuration */
+ c->framing = qemu_get_be32(f);
+ if (c->framing) {
+
+ /* header buffer */
+ c->need_header = qemu_get_be32(f);
+ int header_size = qemu_get_be32(f);
+ if (header_size > FRAME_HEADER_SIZE) {
+ D("%s: load failed: payload buffer requires %d bytes, %d available\n",
+ __FUNCTION__, header_size, FRAME_HEADER_SIZE);
+ return NULL;
+ }
+ int ret;
+ if ((ret = qemu_get_buffer(f, c->header0, header_size)) != header_size) {
+ D("%s: frame header buffer load failed: expected %d bytes, got %d\n",
+ __FUNCTION__, header_size, ret);
+ return NULL;
+ }
+
+ /* payload sink */
+ if ((ret = qemud_sink_load(f, c->payload)))
+ return NULL;
+
+ /* replace payload buffer by saved data */
+ if (c->payload->buff) {
+ AFREE(c->payload->buff);
+ }
+ AARRAY_NEW(c->payload->buff, c->payload->size+1); /* +1 for terminating zero */
+ if ((ret = qemu_get_buffer(f, c->payload->buff, c->payload->size)) != c->payload->size) {
+ D("%s: frame payload buffer load failed: expected %d bytes, got %d\n",
+ __FUNCTION__, c->payload->size, ret);
+ AFREE(c->payload->buff);
+ return NULL;
+ }
+ }
+
+ /* Associate the client with the pipe. */
+ ANEW0(qemud_pipe);
+ qemud_pipe->hwpipe = hwpipe;
+ qemud_pipe->looper = pipeOpaque;
+ qemud_pipe->service = sv;
+ qemud_pipe->client = c;
+ c->ProtocolSelector.Pipe.qemud_pipe = qemud_pipe;
+
+ return qemud_pipe;
+}
+
/* QEMUD pipe functions.
*/
static const GoldfishPipeFuncs _qemudPipe_funcs = {
@@ -2048,6 +2197,8 @@ static const GoldfishPipeFuncs _qemudPipe_funcs = {
_qemudPipe_recvBuffers,
_qemudPipe_poll,
_qemudPipe_wakeOn,
+ _qemudPipe_save,
+ _qemudPipe_load,
};
/* Initializes QEMUD pipe interface.