Merge remote branch 'spice/spice.v29.pull' into staging

Conflicts:
	trace-events
This commit is contained in:
Anthony Liguori 2011-02-01 15:22:48 -06:00
commit 9363ee31ab
17 changed files with 391 additions and 80 deletions

View File

@ -383,6 +383,14 @@ S: Odd Fixes
F: gdbstub* F: gdbstub*
F: gdb-xml/ F: gdb-xml/
SPICE
M: Gerd Hoffmann <kraxel@redhat.com>
S: Supported
F: ui/qemu-spice.h
F: ui/spice-*.c
F: audio/spiceaudio.c
F: hw/qxl*
Graphics Graphics
M: Anthony Liguori <aliguori@us.ibm.com> M: Anthony Liguori <aliguori@us.ibm.com>
S: Maintained S: Maintained

View File

@ -105,7 +105,7 @@ common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
common-obj-$(CONFIG_WIN32) += version.o common-obj-$(CONFIG_WIN32) += version.o
common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
audio-obj-$(CONFIG_SDL) += sdlaudio.o audio-obj-$(CONFIG_SDL) += sdlaudio.o

6
configure vendored
View File

@ -2208,9 +2208,9 @@ if test "$spice" != "no" ; then
#include <spice.h> #include <spice.h>
int main(void) { spice_server_new(); return 0; } int main(void) { spice_server_new(); return 0; }
EOF EOF
spice_cflags=$($pkgconfig --cflags spice-protocol spice-server 2>/dev/null) spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null)
spice_libs=$($pkgconfig --libs spice-protocol spice-server 2>/dev/null) spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null)
if $pkgconfig --atleast-version=0.5.3 spice-server >/dev/null 2>&1 && \ if $pkg_config --atleast-version=0.5.3 spice-server >/dev/null 2>&1 && \
compile_prog "$spice_cflags" "$spice_libs" ; then compile_prog "$spice_cflags" "$spice_libs" ; then
spice="yes" spice="yes"
libs_softmmu="$libs_softmmu $spice_libs" libs_softmmu="$libs_softmmu $spice_libs"

View File

@ -833,6 +833,23 @@ ETEXI
.mhandler.cmd_new = do_snapshot_blkdev, .mhandler.cmd_new = do_snapshot_blkdev,
}, },
STEXI
@item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject}
@findex client_migrate_info
Set the spice/vnc connection info for the migration target. The spice/vnc
server will ask the spice/vnc client to automatically reconnect using the
new parameters (if specified) once the vm migration finished successfully.
ETEXI
{
.name = "client_migrate_info",
.args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
.params = "protocol hostname port tls-port cert-subject",
.help = "send migration info to spice/vnc client",
.user_print = monitor_user_noop,
.mhandler.cmd_new = client_migrate_info,
},
STEXI STEXI
@item snapshot_blkdev @item snapshot_blkdev
@findex snapshot_blkdev @findex snapshot_blkdev

View File

@ -866,7 +866,9 @@ static void qxl_destroy_primary(PCIQXLDevice *d)
dprint(d, 1, "%s\n", __FUNCTION__); dprint(d, 1, "%s\n", __FUNCTION__);
d->mode = QXL_MODE_UNDEFINED; d->mode = QXL_MODE_UNDEFINED;
qemu_mutex_unlock_iothread();
d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0); d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0);
qemu_mutex_lock_iothread();
} }
static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
@ -1418,43 +1420,10 @@ static int qxl_post_load(void *opaque, int version)
} }
dprint(d, 1, "%s: done\n", __FUNCTION__); dprint(d, 1, "%s: done\n", __FUNCTION__);
/* spice 0.4 compatibility -- accept but ignore */
qemu_free(d->worker_data);
d->worker_data = NULL;
d->worker_data_size = 0;
return 0; return 0;
} }
#define QXL_SAVE_VERSION 20 #define QXL_SAVE_VERSION 21
static bool qxl_test_worker_data(void *opaque, int version_id)
{
PCIQXLDevice* d = opaque;
if (d->revision != 1) {
return false;
}
if (!d->worker_data_size) {
return false;
}
if (!d->worker_data) {
d->worker_data = qemu_malloc(d->worker_data_size);
}
return true;
}
static bool qxl_test_spice04(void *opaque, int version_id)
{
PCIQXLDevice* d = opaque;
return d->revision == 1;
}
static bool qxl_test_spice06(void *opaque)
{
PCIQXLDevice* d = opaque;
return d->revision > 1;
}
static VMStateDescription qxl_memslot = { static VMStateDescription qxl_memslot = {
.name = "qxl-memslot", .name = "qxl-memslot",
@ -1486,24 +1455,6 @@ static VMStateDescription qxl_surface = {
} }
}; };
static VMStateDescription qxl_vmstate_spice06 = {
.name = "qxl/spice06",
.version_id = QXL_SAVE_VERSION,
.minimum_version_id = QXL_SAVE_VERSION,
.fields = (VMStateField []) {
VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice),
VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
qxl_memslot, struct guest_slots),
VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
qxl_surface, QXLSurfaceCreate),
VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
vmstate_info_uint64, uint64_t),
VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
VMSTATE_END_OF_LIST()
},
};
static VMStateDescription qxl_vmstate = { static VMStateDescription qxl_vmstate = {
.name = "qxl", .name = "qxl",
.version_id = QXL_SAVE_VERSION, .version_id = QXL_SAVE_VERSION,
@ -1519,25 +1470,17 @@ static VMStateDescription qxl_vmstate = {
VMSTATE_UINT32(last_release_offset, PCIQXLDevice), VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
VMSTATE_UINT32(mode, PCIQXLDevice), VMSTATE_UINT32(mode, PCIQXLDevice),
VMSTATE_UINT32(ssd.unique, PCIQXLDevice), VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice),
/* spice 0.4 sends/expects them */ VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
VMSTATE_VBUFFER_UINT32(vga.vram_ptr, PCIQXLDevice, 0, qxl_test_spice04, 0, qxl_memslot, struct guest_slots),
vga.vram_size), VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
VMSTATE_UINT32_TEST(worker_data_size, PCIQXLDevice, qxl_test_spice04), qxl_surface, QXLSurfaceCreate),
VMSTATE_VBUFFER_UINT32(worker_data, PCIQXLDevice, 0, qxl_test_worker_data, 0, VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
worker_data_size), VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
vmstate_info_uint64, uint64_t),
VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
VMSTATE_END_OF_LIST() VMSTATE_END_OF_LIST()
}, },
.subsections = (VMStateSubsection[]) {
{
/* additional spice 0.6 state */
.vmsd = &qxl_vmstate_spice06,
.needed = qxl_test_spice06,
},{
/* end of list */
},
},
}; };
static PCIDeviceInfo qxl_info_primary = { static PCIDeviceInfo qxl_info_primary = {

View File

@ -80,10 +80,6 @@ typedef struct PCIQXLDevice {
/* io bar */ /* io bar */
uint32_t io_base; uint32_t io_base;
/* spice 0.4 loadvm compatibility */
void *worker_data;
uint32_t worker_data_size;
} PCIQXLDevice; } PCIQXLDevice;
#define PANIC_ON(x) if ((x)) { \ #define PANIC_ON(x) if ((x)) { \

View File

@ -36,6 +36,9 @@ static int64_t max_throttle = (32 << 20);
static MigrationState *current_migration; static MigrationState *current_migration;
static NotifierList migration_state_notifiers =
NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
int qemu_start_incoming_migration(const char *uri) int qemu_start_incoming_migration(const char *uri)
{ {
const char *p; const char *p;
@ -121,6 +124,7 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
} }
current_migration = s; current_migration = s;
notifier_list_notify(&migration_state_notifiers);
return 0; return 0;
} }
@ -272,6 +276,7 @@ void migrate_fd_error(FdMigrationState *s)
{ {
DPRINTF("setting error state\n"); DPRINTF("setting error state\n");
s->state = MIG_STATE_ERROR; s->state = MIG_STATE_ERROR;
notifier_list_notify(&migration_state_notifiers);
migrate_fd_cleanup(s); migrate_fd_cleanup(s);
} }
@ -329,6 +334,7 @@ ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
monitor_resume(s->mon); monitor_resume(s->mon);
} }
s->state = MIG_STATE_ERROR; s->state = MIG_STATE_ERROR;
notifier_list_notify(&migration_state_notifiers);
} }
return ret; return ret;
@ -389,6 +395,7 @@ void migrate_fd_put_ready(void *opaque)
state = MIG_STATE_ERROR; state = MIG_STATE_ERROR;
} }
s->state = state; s->state = state;
notifier_list_notify(&migration_state_notifiers);
} }
} }
@ -408,6 +415,7 @@ void migrate_fd_cancel(MigrationState *mig_state)
DPRINTF("cancelling migration\n"); DPRINTF("cancelling migration\n");
s->state = MIG_STATE_CANCELLED; s->state = MIG_STATE_CANCELLED;
notifier_list_notify(&migration_state_notifiers);
qemu_savevm_state_cancel(s->mon, s->file); qemu_savevm_state_cancel(s->mon, s->file);
migrate_fd_cleanup(s); migrate_fd_cleanup(s);
@ -421,6 +429,7 @@ void migrate_fd_release(MigrationState *mig_state)
if (s->state == MIG_STATE_ACTIVE) { if (s->state == MIG_STATE_ACTIVE) {
s->state = MIG_STATE_CANCELLED; s->state = MIG_STATE_CANCELLED;
notifier_list_notify(&migration_state_notifiers);
migrate_fd_cleanup(s); migrate_fd_cleanup(s);
} }
qemu_free(s); qemu_free(s);
@ -452,3 +461,22 @@ int migrate_fd_close(void *opaque)
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
return s->close(s); return s->close(s);
} }
void add_migration_state_change_notifier(Notifier *notify)
{
notifier_list_add(&migration_state_notifiers, notify);
}
void remove_migration_state_change_notifier(Notifier *notify)
{
notifier_list_remove(&migration_state_notifiers, notify);
}
int get_migration_state(void)
{
if (current_migration) {
return migrate_fd_get_status(current_migration);
} else {
return MIG_STATE_ERROR;
}
}

View File

@ -16,6 +16,7 @@
#include "qdict.h" #include "qdict.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "notify.h"
#define MIG_STATE_ERROR -1 #define MIG_STATE_ERROR -1
#define MIG_STATE_COMPLETED 0 #define MIG_STATE_COMPLETED 0
@ -134,4 +135,8 @@ static inline FdMigrationState *migrate_to_fms(MigrationState *mig_state)
return container_of(mig_state, FdMigrationState, mig_state); return container_of(mig_state, FdMigrationState, mig_state);
} }
void add_migration_state_change_notifier(Notifier *notify);
void remove_migration_state_change_notifier(Notifier *notify);
int get_migration_state(void);
#endif #endif

View File

@ -1146,9 +1146,9 @@ static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
time_t when; time_t when;
int rc; int rc;
if (strcmp(whenstr, "now")) { if (strcmp(whenstr, "now") == 0) {
when = 0; when = 0;
} else if (strcmp(whenstr, "never")) { } else if (strcmp(whenstr, "never") == 0) {
when = TIME_MAX; when = TIME_MAX;
} else if (whenstr[0] == '+') { } else if (whenstr[0] == '+') {
when = time(NULL) + strtoull(whenstr+1, NULL, 10); when = time(NULL) + strtoull(whenstr+1, NULL, 10);
@ -1183,6 +1183,33 @@ static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
return -1; return -1;
} }
static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *hostname = qdict_get_str(qdict, "hostname");
const char *subject = qdict_get_try_str(qdict, "cert-subject");
int port = qdict_get_try_int(qdict, "port", -1);
int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
int ret;
if (strcmp(protocol, "spice") == 0) {
if (!using_spice) {
qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
return -1;
}
ret = qemu_spice_migrate_info(hostname, port, tls_port, subject);
if (ret != 0) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
}
return 0;
}
qerror_report(QERR_INVALID_PARAMETER, "protocol");
return -1;
}
static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data) static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data)
{ {
vga_hw_screen_dump(qdict_get_str(qdict, "filename")); vga_hw_screen_dump(qdict_get_str(qdict, "filename"));

View File

@ -97,6 +97,7 @@
#endif #endif
#include "qemu_socket.h" #include "qemu_socket.h"
#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096 #define READ_BUF_LEN 4096
@ -2495,6 +2496,9 @@ static const struct {
|| defined(__FreeBSD_kernel__) || defined(__FreeBSD_kernel__)
{ .name = "parport", .open = qemu_chr_open_pp }, { .name = "parport", .open = qemu_chr_open_pp },
#endif #endif
#ifdef CONFIG_SPICE
{ .name = "spicevmc", .open = qemu_chr_open_spice },
#endif
}; };
CharDriverState *qemu_chr_open_opts(QemuOpts *opts, CharDriverState *qemu_chr_open_opts(QemuOpts *opts,

View File

@ -146,6 +146,12 @@ static QemuOptsList qemu_chardev_opts = {
},{ },{
.name = "signal", .name = "signal",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
},{
.name = "name",
.type = QEMU_OPT_STRING,
},{
.name = "debug",
.type = QEMU_OPT_NUMBER,
}, },
{ /* end of list */ } { /* end of list */ }
}, },

View File

@ -1367,6 +1367,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
#endif #endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
"-chardev parport,id=id,path=path[,mux=on|off]\n" "-chardev parport,id=id,path=path[,mux=on|off]\n"
#endif
#if defined(CONFIG_SPICE)
"-chardev spicevmc,id=id,name=name[,debug=debug]\n"
#endif #endif
, QEMU_ARCH_ALL , QEMU_ARCH_ALL
) )
@ -1392,7 +1395,8 @@ Backend is one of:
@option{stdio}, @option{stdio},
@option{braille}, @option{braille},
@option{tty}, @option{tty},
@option{parport}. @option{parport},
@option{spicevmc}.
The specific backend will determine the applicable options. The specific backend will determine the applicable options.
All devices must have an id, which can be any string up to 127 characters long. All devices must have an id, which can be any string up to 127 characters long.
@ -1568,6 +1572,16 @@ Connect to a local parallel port.
@option{path} specifies the path to the parallel port device. @option{path} is @option{path} specifies the path to the parallel port device. @option{path} is
required. required.
#if defined(CONFIG_SPICE)
@item -chardev spicevmc ,id=@var{id} ,debug=@var{debug}, name=@var{name}
@option{debug} debug level for spicevmc
@option{name} name of spice channel to connect to
Connect to a spice virtual machine channel, such as vdiport.
#endif
@end table @end table
ETEXI ETEXI

View File

@ -502,6 +502,41 @@ EQMP
.mhandler.cmd_new = do_migrate_set_speed, .mhandler.cmd_new = do_migrate_set_speed,
}, },
SQMP
client_migrate_info
------------------
Set the spice/vnc connection info for the migration target. The spice/vnc
server will ask the spice/vnc client to automatically reconnect using the
new parameters (if specified) once the vm migration finished successfully.
Arguments:
- "protocol": protocol: "spice" or "vnc" (json-string)
- "hostname": migration target hostname (json-string)
- "port": spice/vnc tcp port for plaintext channels (json-int, optional)
- "tls-port": spice tcp port for tls-secured channels (json-int, optional)
- "cert-subject": server certificate subject (json-string, optional)
Example:
-> { "execute": "client_migrate_info",
"arguments": { "protocol": "spice",
"hostname": "virt42.lab.kraxel.org",
"port": 1234 } }
<- { "return": {} }
EQMP
{
.name = "client_migrate_info",
.args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
.params = "protocol hostname port tls-port cert-subject",
.help = "send migration info to spice/vnc client",
.user_print = monitor_user_noop,
.mhandler.cmd_new = client_migrate_info,
},
SQMP SQMP
migrate_set_speed migrate_set_speed
----------------- -----------------

190
spice-qemu-char.c Normal file
View File

@ -0,0 +1,190 @@
#include "config-host.h"
#include "trace.h"
#include "ui/qemu-spice.h"
#include <spice.h>
#include <spice-experimental.h>
#include "osdep.h"
#define dprintf(_scd, _level, _fmt, ...) \
do { \
static unsigned __dprintf_counter = 0; \
if (_scd->debug >= _level) { \
fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
} \
} while (0)
#define VMC_MAX_HOST_WRITE 2048
typedef struct SpiceCharDriver {
CharDriverState* chr;
SpiceCharDeviceInstance sin;
char *subtype;
bool active;
uint8_t *buffer;
uint8_t *datapos;
ssize_t bufsize, datalen;
uint32_t debug;
} SpiceCharDriver;
static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
{
SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
ssize_t out = 0;
ssize_t last_out;
uint8_t* p = (uint8_t*)buf;
while (len > 0) {
last_out = MIN(len, VMC_MAX_HOST_WRITE);
qemu_chr_read(scd->chr, p, last_out);
if (last_out > 0) {
out += last_out;
len -= last_out;
p += last_out;
} else {
break;
}
}
dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out);
trace_spice_vmc_write(out, len + out);
return out;
}
static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
{
SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
int bytes = MIN(len, scd->datalen);
dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen);
if (bytes > 0) {
memcpy(buf, scd->datapos, bytes);
scd->datapos += bytes;
scd->datalen -= bytes;
assert(scd->datalen >= 0);
if (scd->datalen == 0) {
scd->datapos = 0;
}
}
trace_spice_vmc_read(bytes, len);
return bytes;
}
static SpiceCharDeviceInterface vmc_interface = {
.base.type = SPICE_INTERFACE_CHAR_DEVICE,
.base.description = "spice virtual channel char device",
.base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
.base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
.write = vmc_write,
.read = vmc_read,
};
static void vmc_register_interface(SpiceCharDriver *scd)
{
if (scd->active) {
return;
}
dprintf(scd, 1, "%s\n", __func__);
scd->sin.base.sif = &vmc_interface.base;
qemu_spice_add_interface(&scd->sin.base);
scd->active = true;
trace_spice_vmc_register_interface(scd);
}
static void vmc_unregister_interface(SpiceCharDriver *scd)
{
if (!scd->active) {
return;
}
dprintf(scd, 1, "%s\n", __func__);
spice_server_remove_interface(&scd->sin.base);
scd->active = false;
trace_spice_vmc_unregister_interface(scd);
}
static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
SpiceCharDriver *s = chr->opaque;
dprintf(s, 2, "%s: %d\n", __func__, len);
vmc_register_interface(s);
assert(s->datalen == 0);
if (s->bufsize < len) {
s->bufsize = len;
s->buffer = qemu_realloc(s->buffer, s->bufsize);
}
memcpy(s->buffer, buf, len);
s->datapos = s->buffer;
s->datalen = len;
spice_server_char_device_wakeup(&s->sin);
return len;
}
static void spice_chr_close(struct CharDriverState *chr)
{
SpiceCharDriver *s = chr->opaque;
printf("%s\n", __func__);
vmc_unregister_interface(s);
qemu_free(s);
}
static void print_allowed_subtypes(void)
{
const char** psubtype;
int i;
fprintf(stderr, "allowed names: ");
for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
*psubtype != NULL; ++psubtype, ++i) {
if (i == 0) {
fprintf(stderr, "%s", *psubtype);
} else {
fprintf(stderr, ", %s", *psubtype);
}
}
fprintf(stderr, "\n");
}
CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
{
CharDriverState *chr;
SpiceCharDriver *s;
const char* name = qemu_opt_get(opts, "name");
uint32_t debug = qemu_opt_get_number(opts, "debug", 0);
const char** psubtype = spice_server_char_device_recognized_subtypes();
const char *subtype = NULL;
if (name == NULL) {
fprintf(stderr, "spice-qemu-char: missing name parameter\n");
print_allowed_subtypes();
return NULL;
}
for(;*psubtype != NULL; ++psubtype) {
if (strcmp(name, *psubtype) == 0) {
subtype = *psubtype;
break;
}
}
if (subtype == NULL) {
fprintf(stderr, "spice-qemu-char: unsupported name\n");
print_allowed_subtypes();
return NULL;
}
chr = qemu_mallocz(sizeof(CharDriverState));
s = qemu_mallocz(sizeof(SpiceCharDriver));
s->chr = chr;
s->debug = debug;
s->active = false;
s->sin.subtype = subtype;
chr->opaque = s;
chr->chr_write = spice_chr_write;
chr->chr_close = spice_chr_close;
qemu_chr_generic_open(chr);
return chr;
}

View File

@ -248,3 +248,9 @@ disable grlib_apbuart_unknown_register(const char *op, uint64_t val) "%s unknown
# hw/leon3.c # hw/leon3.c
disable leon3_set_irq(int intno) "Set CPU IRQ %d" disable leon3_set_irq(int intno) "Set CPU IRQ %d"
disable leon3_reset_irq(int intno) "Reset CPU IRQ %d" disable leon3_reset_irq(int intno) "Reset CPU IRQ %d"
# spice-qemu-char.c
disable spice_vmc_write(ssize_t out, int len) "spice wrottn %lu of requested %zd"
disable spice_vmc_read(int bytes, int len) "spice read %lu of requested %zd"
disable spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
disable spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"

View File

@ -24,6 +24,7 @@
#include "qemu-option.h" #include "qemu-option.h"
#include "qemu-config.h" #include "qemu-config.h"
#include "qemu-char.h"
extern int using_spice; extern int using_spice;
@ -35,15 +36,21 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin);
int qemu_spice_set_passwd(const char *passwd, int qemu_spice_set_passwd(const char *passwd,
bool fail_if_connected, bool disconnect_if_connected); bool fail_if_connected, bool disconnect_if_connected);
int qemu_spice_set_pw_expire(time_t expires); int qemu_spice_set_pw_expire(time_t expires);
int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
const char *subject);
void do_info_spice_print(Monitor *mon, const QObject *data); void do_info_spice_print(Monitor *mon, const QObject *data);
void do_info_spice(Monitor *mon, QObject **ret_data); void do_info_spice(Monitor *mon, QObject **ret_data);
CharDriverState *qemu_chr_open_spice(QemuOpts *opts);
#else /* CONFIG_SPICE */ #else /* CONFIG_SPICE */
#define using_spice 0 #define using_spice 0
#define qemu_spice_set_passwd(_p, _f1, _f2) (-1) #define qemu_spice_set_passwd(_p, _f1, _f2) (-1)
#define qemu_spice_set_pw_expire(_e) (-1) #define qemu_spice_set_pw_expire(_e) (-1)
static inline int qemu_spice_migrate_info(const char *h, int p, int t, const char *s)
{ return -1; }
#endif /* CONFIG_SPICE */ #endif /* CONFIG_SPICE */

View File

@ -30,11 +30,15 @@
#include "qbool.h" #include "qbool.h"
#include "qstring.h" #include "qstring.h"
#include "qjson.h" #include "qjson.h"
#include "notify.h"
#include "migration.h"
#include "monitor.h" #include "monitor.h"
#include "hw/hw.h"
/* core bits */ /* core bits */
static SpiceServer *spice_server; static SpiceServer *spice_server;
static Notifier migration_state;
static const char *auth = "spice"; static const char *auth = "spice";
static char *auth_passwd; static char *auth_passwd;
static time_t auth_expires = TIME_MAX; static time_t auth_expires = TIME_MAX;
@ -416,6 +420,24 @@ void do_info_spice(Monitor *mon, QObject **ret_data)
*ret_data = QOBJECT(server); *ret_data = QOBJECT(server);
} }
static void migration_state_notifier(Notifier *notifier)
{
int state = get_migration_state();
if (state == MIG_STATE_COMPLETED) {
#if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */
spice_server_migrate_switch(spice_server);
#endif
}
}
int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
const char *subject)
{
return spice_server_migrate_info(spice_server, hostname,
port, tls_port, subject);
}
static int add_channel(const char *name, const char *value, void *opaque) static int add_channel(const char *name, const char *value, void *opaque)
{ {
int security = 0; int security = 0;
@ -573,6 +595,9 @@ void qemu_spice_init(void)
spice_server_init(spice_server, &core_interface); spice_server_init(spice_server, &core_interface);
using_spice = 1; using_spice = 1;
migration_state.notify = migration_state_notifier;
add_migration_state_change_notifier(&migration_state);
qemu_spice_input_init(); qemu_spice_input_init();
qemu_spice_audio_init(); qemu_spice_audio_init();