Four changes here. Polling for reconnection of character devices,

the QOMification of accelerators, a fix for -kernel support on x86, and one
 for a recently-introduced virtio-scsi optimization.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQEcBAABAgAGBQJUNo9yAAoJEBRUblpOawnXQDkH/1M5DxmVwUv+SZtHEdpsT7Eq
 UGjRzfYXsYP/WkEqxVzYJmN0HJn9z8uJZin/dqwDPQLjCy8gf/xuaNCfoZqMuxHw
 iNaTgKpi9Uy0G0VWxjlZpRu8f5JjqHbJEP6aTT0hooBHaqQoBSm1fQh/pnCUvnpB
 qDQeHcOjrzIMkQJ3Ji8z2s+CapHaiIa63hJqRJztS5vbonPjngjj87dA54eIqDtQ
 RwXy58y+C8YMKqfpOG6lA+RxogESyyCfDBVUA1wwRDad1mOFKUMtEd4jAL39HUgR
 qINSKybG12V2bx8E+vNbaKB+68+NLdxcyR39JR2aun+a8yw+yDcF5BBiMXlqV0U=
 =4bqy
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging

Four changes here.  Polling for reconnection of character devices,
the QOMification of accelerators, a fix for -kernel support on x86, and one
for a recently-introduced virtio-scsi optimization.

# gpg: Signature made Thu 09 Oct 2014 14:36:50 BST using RSA key ID 4E6B09D7
# gpg: Good signature from "Paolo Bonzini <pbonzini@redhat.com>"
# gpg:                 aka "Paolo Bonzini <bonzini@gnu.org>"

* remotes/bonzini/tags/for-upstream: (28 commits)
  qemu-char: Fix reconnect socket error reporting
  qemu-sockets: Add error to non-blocking connect handler
  qemu-error: Add error_vreport()
  virtio-scsi: fix use-after-free of VirtIOSCSIReq
  linuxboot: compute initrd loading address
  kvm: Make KVMState be the TYPE_KVM_ACCEL instance struct
  accel: Create accel object when initializing machine
  accel: Pass MachineState object to accel init functions
  accel: Rename 'init' method to 'init_machine'
  accel: Move accel init/allowed code to separate function
  accel: Remove tcg_available() function
  accel: Move qtest accel registration to qtest.c
  accel: Move Xen registration code to xen-common.c
  accel: Move KVM accel registration to kvm-all.c
  accel: Report unknown accelerator as "not found" instead of "does not exist"
  accel: Make AccelClass.available() optional
  accel: Use QOM classes for accel types
  accel: Move accel name lookup to separate function
  accel: Simplify configure_accelerator() using AccelType *acc variable
  accel: Create AccelType typedef
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2014-10-09 15:09:05 +01:00
commit fcb2cd928f
29 changed files with 711 additions and 235 deletions

View File

@ -62,6 +62,7 @@ common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
common-obj-y += audio/
common-obj-y += hw/
common-obj-y += accel.o
common-obj-y += ui/
common-obj-y += bt-host.o bt-vhci.o

157
accel.c Normal file
View File

@ -0,0 +1,157 @@
/*
* QEMU System Emulator, accelerator interfaces
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (c) 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "sysemu/accel.h"
#include "hw/boards.h"
#include "qemu-common.h"
#include "sysemu/arch_init.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
#include "hw/xen/xen.h"
#include "qom/object.h"
#include "hw/boards.h"
int tcg_tb_size;
static bool tcg_allowed = true;
static int tcg_init(MachineState *ms)
{
tcg_exec_init(tcg_tb_size * 1024 * 1024);
return 0;
}
static const TypeInfo accel_type = {
.name = TYPE_ACCEL,
.parent = TYPE_OBJECT,
.class_size = sizeof(AccelClass),
.instance_size = sizeof(AccelState),
};
/* Lookup AccelClass from opt_name. Returns NULL if not found */
static AccelClass *accel_find(const char *opt_name)
{
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
g_free(class_name);
return ac;
}
static int accel_init_machine(AccelClass *acc, MachineState *ms)
{
ObjectClass *oc = OBJECT_CLASS(acc);
const char *cname = object_class_get_name(oc);
AccelState *accel = ACCEL(object_new(cname));
int ret;
ms->accelerator = accel;
*(acc->allowed) = true;
ret = acc->init_machine(ms);
if (ret < 0) {
ms->accelerator = NULL;
*(acc->allowed) = false;
object_unref(OBJECT(accel));
}
return ret;
}
int configure_accelerator(MachineState *ms)
{
const char *p;
char buf[10];
int ret;
bool accel_initialised = false;
bool init_failed = false;
AccelClass *acc = NULL;
p = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (p == NULL) {
/* Use the default "accelerator", tcg */
p = "tcg";
}
while (!accel_initialised && *p != '\0') {
if (*p == ':') {
p++;
}
p = get_opt_name(buf, sizeof(buf), p, ':');
acc = accel_find(buf);
if (!acc) {
fprintf(stderr, "\"%s\" accelerator not found.\n", buf);
continue;
}
if (acc->available && !acc->available()) {
printf("%s not supported for this target\n",
acc->name);
continue;
}
ret = accel_init_machine(acc, ms);
if (ret < 0) {
init_failed = true;
fprintf(stderr, "failed to initialize %s: %s\n",
acc->name,
strerror(-ret));
} else {
accel_initialised = true;
}
}
if (!accel_initialised) {
if (!init_failed) {
fprintf(stderr, "No accelerator found!\n");
}
exit(1);
}
if (init_failed) {
fprintf(stderr, "Back to %s accelerator.\n", acc->name);
}
return !accel_initialised;
}
static void tcg_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg";
ac->init_machine = tcg_init;
ac->allowed = &tcg_allowed;
}
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
static const TypeInfo tcg_accel_type = {
.name = TYPE_TCG_ACCEL,
.parent = TYPE_ACCEL,
.class_init = tcg_accel_class_init,
};
static void register_accel_types(void)
{
type_register_static(&accel_type);
type_register_static(&tcg_accel_type);
}
type_init(register_accel_types);

View File

@ -1337,11 +1337,6 @@ void cpudef_init(void)
#endif
}
int tcg_available(void)
{
return 1;
}
int kvm_available(void)
{
#ifdef CONFIG_KVM

View File

@ -545,11 +545,12 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req)
{
if (scsi_req_enqueue(req->sreq)) {
scsi_req_continue(req->sreq);
SCSIRequest *sreq = req->sreq;
if (scsi_req_enqueue(sreq)) {
scsi_req_continue(sreq);
}
bdrv_io_unplug(req->sreq->dev->conf.bs);
scsi_req_unref(req->sreq);
bdrv_io_unplug(sreq->dev->conf.bs);
scsi_req_unref(sreq);
}
static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq)

View File

@ -5,12 +5,11 @@
#include "qemu/typedefs.h"
#include "sysemu/blockdev.h"
#include "sysemu/accel.h"
#include "hw/qdev.h"
#include "qom/object.h"
typedef struct MachineState MachineState;
typedef void QEMUMachineInitFunc(MachineState *ms);
typedef void QEMUMachineResetFunc(void);
@ -135,6 +134,7 @@ struct MachineState {
char *kernel_cmdline;
char *initrd_filename;
const char *cpu_model;
AccelState *accelerator;
};
#endif

View File

@ -36,7 +36,6 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level);
qemu_irq *xen_interrupt_controller_init(void);
int xen_init(MachineClass *mc);
void xenstore_store_pv_console_info(int i, struct CharDriverState *chr);
#if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY)

View File

@ -38,6 +38,7 @@ void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0);
void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
void error_set_progname(const char *argv0);
void error_vreport(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0);
void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
const char *error_get_progname(void);
extern bool enable_timestamp_msg;

View File

@ -47,7 +47,7 @@ int recv_all(int fd, void *buf, int len1, bool single_read);
/* callback function for nonblocking connect
* valid fd on success, negative error code on failure
*/
typedef void NonBlockingConnectHandler(int fd, void *opaque);
typedef void NonBlockingConnectHandler(int fd, Error *errp, void *opaque);
InetSocketAddress *inet_parse(const char *str, Error **errp);
int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp);

View File

@ -32,6 +32,7 @@ typedef struct MemoryMappingList MemoryMappingList;
typedef struct QEMUMachine QEMUMachine;
typedef struct MachineClass MachineClass;
typedef struct MachineState MachineState;
typedef struct NICInfo NICInfo;
typedef struct HCIInfo HCIInfo;
typedef struct AudioState AudioState;

62
include/sysemu/accel.h Normal file
View File

@ -0,0 +1,62 @@
/* QEMU accelerator interfaces
*
* Copyright (c) 2014 Red Hat Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef HW_ACCEL_H
#define HW_ACCEL_H
#include "qemu/typedefs.h"
#include "qom/object.h"
typedef struct AccelState {
/*< private >*/
Object parent_obj;
} AccelState;
typedef struct AccelClass {
/*< private >*/
ObjectClass parent_class;
/*< public >*/
const char *opt_name;
const char *name;
int (*available)(void);
int (*init_machine)(MachineState *ms);
bool *allowed;
} AccelClass;
#define TYPE_ACCEL "accel"
#define ACCEL_CLASS_SUFFIX "-" TYPE_ACCEL
#define ACCEL_CLASS_NAME(a) (a ACCEL_CLASS_SUFFIX)
#define ACCEL_CLASS(klass) \
OBJECT_CLASS_CHECK(AccelClass, (klass), TYPE_ACCEL)
#define ACCEL(obj) \
OBJECT_CHECK(AccelState, (obj), TYPE_ACCEL)
#define ACCEL_GET_CLASS(obj) \
OBJECT_GET_CLASS(AccelClass, (obj), TYPE_ACCEL)
extern int tcg_tb_size;
int configure_accelerator(MachineState *ms);
#endif

View File

@ -33,7 +33,6 @@ void do_smbios_option(QemuOpts *opts);
void ram_mig_init(void);
void cpudef_init(void);
void audio_init(void);
int tcg_available(void);
int kvm_available(void);
int xen_available(void);

View File

@ -163,8 +163,6 @@ extern KVMState *kvm_state;
/* external API */
int kvm_init(MachineClass *mc);
int kvm_has_sync_mmu(void);
int kvm_has_vcpu_events(void);
int kvm_has_robust_singlestep(void);

View File

@ -26,7 +26,6 @@ static inline bool qtest_enabled(void)
bool qtest_driver(void);
int qtest_init_accel(MachineClass *mc);
void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp);
static inline int qtest_available(void)

View File

@ -25,6 +25,7 @@
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "sysemu/sysemu.h"
#include "sysemu/accel.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/s390x/adapter.h"
@ -70,8 +71,10 @@ typedef struct KVMSlot
typedef struct kvm_dirty_log KVMDirtyLog;
struct KVMState
typedef struct KVMState
{
AccelState parent_obj;
KVMSlot *slots;
int nr_slots;
int fd;
@ -104,7 +107,12 @@ struct KVMState
QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE];
bool direct_msi;
#endif
};
} KVMState;
#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm")
#define KVM_STATE(obj) \
OBJECT_CHECK(KVMState, (obj), TYPE_KVM_ACCEL)
KVMState *kvm_state;
bool kvm_kernel_irqchip;
@ -1377,8 +1385,9 @@ static int kvm_max_vcpus(KVMState *s)
return (ret) ? ret : kvm_recommended_vcpus(s);
}
int kvm_init(MachineClass *mc)
static int kvm_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
static const char upgrade_note[] =
"Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
"(see http://sourceforge.net/projects/kvm).\n";
@ -1397,7 +1406,7 @@ int kvm_init(MachineClass *mc)
int i, type = 0;
const char *kvm_type;
s = g_malloc0(sizeof(KVMState));
s = KVM_STATE(ms->accelerator);
/*
* On systems where the kernel can support different base page
@ -1586,7 +1595,6 @@ err:
close(s->fd);
}
g_free(s->slots);
g_free(s);
return ret;
}
@ -2225,3 +2233,25 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target)
}
return r;
}
static void kvm_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "KVM";
ac->init_machine = kvm_init;
ac->allowed = &kvm_allowed;
}
static const TypeInfo kvm_accel_type = {
.name = TYPE_KVM_ACCEL,
.parent = TYPE_ACCEL,
.class_init = kvm_accel_class_init,
.instance_size = sizeof(KVMState),
};
static void kvm_type_init(void)
{
type_register_static(&kvm_accel_type);
}
type_init(kvm_type_init);

View File

@ -35,11 +35,6 @@ int kvm_init_vcpu(CPUState *cpu)
return -ENOSYS;
}
int kvm_init(MachineClass *mc)
{
return -ENOSYS;
}
void kvm_flush_coalesced_mmio_buffer(void)
{
}

View File

@ -33,12 +33,12 @@
do { } while (0)
#endif
static void tcp_wait_for_connect(int fd, void *opaque)
static void tcp_wait_for_connect(int fd, Error *err, void *opaque)
{
MigrationState *s = opaque;
if (fd < 0) {
DPRINTF("migrate connect error\n");
DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
s->file = NULL;
migrate_fd_error(s);
} else {

View File

@ -33,12 +33,12 @@
do { } while (0)
#endif
static void unix_wait_for_connect(int fd, void *opaque)
static void unix_wait_for_connect(int fd, Error *err, void *opaque)
{
MigrationState *s = opaque;
if (fd < 0) {
DPRINTF("migrate connect error\n");
DPRINTF("migrate connect error: %s\n", error_get_pretty(err));
s->file = NULL;
migrate_fd_error(s);
} else {

Binary file not shown.

View File

@ -76,14 +76,45 @@ boot_kernel:
copy_kernel:
/* Compute initrd address */
mov $0xe801, %ax
xor %cx, %cx
xor %dx, %dx
int $0x15
/* Output could be in AX/BX or CX/DX */
or %cx, %cx
jnz 1f
or %dx, %dx
jnz 1f
mov %ax, %cx
mov %bx, %dx
1:
or %dx, %dx
jnz 2f
addw $1024, %cx /* add 1 MB */
movzwl %cx, %edi
shll $10, %edi /* convert to bytes */
jmp 3f
2:
addw $16777216 >> 16, %dx /* add 16 MB */
movzwl %dx, %edi
shll $16, %edi /* convert to bytes */
3:
read_fw FW_CFG_INITRD_SIZE
subl %eax, %edi
andl $-4096, %edi /* EDI = start of initrd */
/* We need to load the kernel into memory we can't access in 16 bit
mode, so let's get into 32 bit mode, write the kernel and jump
back again. */
/* Reserve space on the stack for our GDT descriptor. */
mov %esp, %ebp
sub $16, %esp
mov %esp, %ebp
sub $16, %esp
/* Now create the GDT descriptor */
movw $((3 * 8) - 1), -16(%bp)
@ -108,10 +139,18 @@ copy_kernel:
/* We're now running in 16-bit CS, but 32-bit ES! */
/* Load kernel and initrd */
pushl %edi
read_fw_blob_addr32_edi(FW_CFG_INITRD)
read_fw_blob_addr32(FW_CFG_KERNEL)
read_fw_blob_addr32(FW_CFG_INITRD)
read_fw_blob_addr32(FW_CFG_CMDLINE)
read_fw_blob_addr32(FW_CFG_SETUP)
read_fw FW_CFG_SETUP_ADDR
mov %eax, %edi
mov %eax, %ebx
read_fw_blob_addr32_edi(FW_CFG_SETUP)
/* Update the header with the initrd address we chose above */
popl %es:0x218(%ebx)
/* And now jump into Linux! */
mov $0, %eax

View File

@ -51,8 +51,6 @@
.endm
#define read_fw_blob_pre(var) \
read_fw var ## _ADDR; \
mov %eax, %edi; \
read_fw var ## _SIZE; \
mov %eax, %ecx; \
mov $var ## _DATA, %ax; \
@ -68,6 +66,8 @@
* Clobbers: %eax, %edx, %es, %ecx, %edi
*/
#define read_fw_blob(var) \
read_fw var ## _ADDR; \
mov %eax, %edi; \
read_fw_blob_pre(var); \
/* old as(1) doesn't like this insn so emit the bytes instead: \
rep insb (%dx), %es:(%edi); \
@ -80,7 +80,22 @@
*
* Clobbers: %eax, %edx, %es, %ecx, %edi
*/
#define read_fw_blob_addr32(var) \
#define read_fw_blob_addr32(var) \
read_fw var ## _ADDR; \
mov %eax, %edi; \
read_fw_blob_pre(var); \
/* old as(1) doesn't like this insn so emit the bytes instead: \
addr32 rep insb (%dx), %es:(%edi); \
*/ \
.dc.b 0x67,0xf3,0x6c
/*
* Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi.
* Requires _SIZE and _DATA values for the parameter.
*
* Clobbers: %eax, %edx, %edi, %es, %ecx
*/
#define read_fw_blob_addr32_edi(var) \
read_fw_blob_pre(var); \
/* old as(1) doesn't like this insn so emit the bytes instead: \
addr32 rep insb (%dx), %es:(%edi); \

View File

@ -2651,14 +2651,19 @@
# @nodelay: #optional set TCP_NODELAY socket option (default: false)
# @telnet: #optional enable telnet protocol on server
# sockets (default: false)
# @reconnect: #optional For a client socket, if a socket is disconnected,
# then attempt a reconnect after the given number of seconds.
# Setting this to zero disables this function. (default: 0)
# (Since: 2.2)
#
# Since: 1.4
##
{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
'*server' : 'bool',
'*wait' : 'bool',
'*nodelay' : 'bool',
'*telnet' : 'bool' } }
{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
'*server' : 'bool',
'*wait' : 'bool',
'*nodelay' : 'bool',
'*telnet' : 'bool',
'*reconnect' : 'int' } }
##
# @ChardevUdp:

View File

@ -28,6 +28,9 @@
#include "sysemu/char.h"
#include "hw/usb.h"
#include "qmp-commands.h"
#include "qapi/qmp-input-visitor.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi-visit.h"
#include <unistd.h>
#include <fcntl.h>
@ -84,6 +87,94 @@
#define READ_BUF_LEN 4096
#define READ_RETRIES 10
#define CHR_MAX_FILENAME_SIZE 256
/***********************************************************/
/* Socket address helpers */
static void qapi_copy_SocketAddress(SocketAddress **p_dest,
SocketAddress *src)
{
QmpOutputVisitor *qov;
QmpInputVisitor *qiv;
Visitor *ov, *iv;
QObject *obj;
*p_dest = NULL;
qov = qmp_output_visitor_new();
ov = qmp_output_get_visitor(qov);
visit_type_SocketAddress(ov, &src, NULL, &error_abort);
obj = qmp_output_get_qobject(qov);
qmp_output_visitor_cleanup(qov);
if (!obj) {
return;
}
qiv = qmp_input_visitor_new(obj);
iv = qmp_input_get_visitor(qiv);
visit_type_SocketAddress(iv, p_dest, NULL, &error_abort);
qmp_input_visitor_cleanup(qiv);
qobject_decref(obj);
}
static int SocketAddress_to_str(char *dest, int max_len,
const char *prefix, SocketAddress *addr,
bool is_listen, bool is_telnet)
{
switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET:
return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix,
is_telnet ? "telnet" : "tcp", addr->inet->host,
addr->inet->port, is_listen ? ",server" : "");
break;
case SOCKET_ADDRESS_KIND_UNIX:
return snprintf(dest, max_len, "%sunix:%s%s", prefix,
addr->q_unix->path, is_listen ? ",server" : "");
break;
case SOCKET_ADDRESS_KIND_FD:
return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str,
is_listen ? ",server" : "");
break;
default:
abort();
}
}
static int sockaddr_to_str(char *dest, int max_len,
struct sockaddr_storage *ss, socklen_t ss_len,
struct sockaddr_storage *ps, socklen_t ps_len,
bool is_listen, bool is_telnet)
{
char shost[NI_MAXHOST], sserv[NI_MAXSERV];
char phost[NI_MAXHOST], pserv[NI_MAXSERV];
const char *left = "", *right = "";
switch (ss->ss_family) {
#ifndef _WIN32
case AF_UNIX:
return snprintf(dest, max_len, "unix:%s%s",
((struct sockaddr_un *)(ss))->sun_path,
is_listen ? ",server" : "");
#endif
case AF_INET6:
left = "[";
right = "]";
/* fall through */
case AF_INET:
getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost),
sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV);
getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s",
is_telnet ? "telnet" : "tcp",
left, shost, right, sserv,
is_listen ? ",server" : "",
left, phost, right, pserv);
default:
return snprintf(dest, max_len, "unknown");
}
}
/***********************************************************/
/* character device */
@ -989,7 +1080,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
{
int fd_in, fd_out;
char filename_in[256], filename_out[256];
char filename_in[CHR_MAX_FILENAME_SIZE];
char filename_out[CHR_MAX_FILENAME_SIZE];
const char *filename = opts->device;
if (filename == NULL) {
@ -997,8 +1089,8 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts)
return NULL;
}
snprintf(filename_in, 256, "%s.in", filename);
snprintf(filename_out, 256, "%s.out", filename);
snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename);
snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename);
TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
if (fd_in < 0 || fd_out < 0) {
@ -1976,7 +2068,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
OVERLAPPED ov;
int ret;
DWORD size;
char openname[256];
char openname[CHR_MAX_FILENAME_SIZE];
s->fpipe = TRUE;
@ -2410,8 +2502,39 @@ typedef struct {
int read_msgfds_num;
int *write_msgfds;
int write_msgfds_num;
SocketAddress *addr;
bool is_listen;
bool is_telnet;
guint reconnect_timer;
int64_t reconnect_time;
bool connect_err_reported;
} TCPCharDriver;
static gboolean socket_reconnect_timeout(gpointer opaque);
static void qemu_chr_socket_restart_timer(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
assert(s->connected == 0);
s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
socket_reconnect_timeout, chr);
}
static void check_report_connect_error(CharDriverState *chr,
Error *err)
{
TCPCharDriver *s = chr->opaque;
if (!s->connect_err_reported) {
error_report("Unable to connect character device %s: %s",
chr->label, error_get_pretty(err));
s->connect_err_reported = true;
}
qemu_chr_socket_restart_timer(chr);
}
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
#ifndef _WIN32
@ -2690,7 +2813,12 @@ static void tcp_chr_disconnect(CharDriverState *chr)
s->chan = NULL;
closesocket(s->fd);
s->fd = -1;
SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
"disconnected:", s->addr, s->is_listen, s->is_telnet);
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
if (s->reconnect_time) {
qemu_chr_socket_restart_timer(chr);
}
}
static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
@ -2761,6 +2889,21 @@ static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
struct sockaddr_storage ss, ps;
socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps);
memset(&ss, 0, ss_len);
if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) {
snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
"Error in getsockname: %s\n", strerror(errno));
} else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) {
snprintf(chr->filename, CHR_MAX_FILENAME_SIZE,
"Error in getpeername: %s\n", strerror(errno));
} else {
sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE,
&ss, ss_len, &ps, ps_len,
s->is_listen, s->is_telnet);
}
s->connected = 1;
if (s->chan) {
@ -2859,6 +3002,12 @@ static void tcp_chr_close(CharDriverState *chr)
{
TCPCharDriver *s = chr->opaque;
int i;
if (s->reconnect_timer) {
g_source_remove(s->reconnect_timer);
s->reconnect_timer = 0;
}
qapi_free_SocketAddress(s->addr);
if (s->fd >= 0) {
remove_fd_in_watch(chr);
if (s->chan) {
@ -2889,79 +3038,15 @@ static void tcp_chr_close(CharDriverState *chr)
qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}
static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
bool is_listen, bool is_telnet,
bool is_waitconnect,
Error **errp)
static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd)
{
CharDriverState *chr = NULL;
TCPCharDriver *s = NULL;
char host[NI_MAXHOST], serv[NI_MAXSERV];
const char *left = "", *right = "";
struct sockaddr_storage ss;
socklen_t ss_len = sizeof(ss);
TCPCharDriver *s = chr->opaque;
memset(&ss, 0, ss_len);
if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) {
error_setg_errno(errp, errno, "getsockname");
return NULL;
}
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(TCPCharDriver));
s->connected = 0;
s->fd = -1;
s->listen_fd = -1;
s->read_msgfds = 0;
s->read_msgfds_num = 0;
s->write_msgfds = 0;
s->write_msgfds_num = 0;
chr->filename = g_malloc(256);
switch (ss.ss_family) {
#ifndef _WIN32
case AF_UNIX:
s->is_unix = 1;
snprintf(chr->filename, 256, "unix:%s%s",
((struct sockaddr_un *)(&ss))->sun_path,
is_listen ? ",server" : "");
break;
#endif
case AF_INET6:
left = "[";
right = "]";
/* fall through */
case AF_INET:
s->do_nodelay = do_nodelay;
getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host),
serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
snprintf(chr->filename, 256, "%s:%s%s%s:%s%s",
is_telnet ? "telnet" : "tcp",
left, host, right, serv,
is_listen ? ",server" : "");
break;
}
chr->opaque = s;
chr->chr_write = tcp_chr_write;
chr->chr_sync_read = tcp_chr_sync_read;
chr->chr_close = tcp_chr_close;
chr->get_msgfds = tcp_get_msgfds;
chr->set_msgfds = tcp_set_msgfds;
chr->chr_add_client = tcp_chr_add_client;
chr->chr_add_watch = tcp_chr_add_watch;
chr->chr_update_read_handler = tcp_chr_update_read_handler;
/* be isn't opened until we get a connection */
chr->explicit_be_open = true;
if (is_listen) {
if (s->is_listen) {
s->listen_fd = fd;
s->listen_chan = io_channel_from_socket(s->listen_fd);
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
if (is_telnet) {
s->do_telnetopt = 1;
}
s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN,
tcp_chr_accept, chr);
} else {
s->connected = 1;
s->fd = fd;
@ -2969,14 +3054,41 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
s->chan = io_channel_from_socket(s->fd);
tcp_chr_connect(chr);
}
}
if (is_listen && is_waitconnect) {
fprintf(stderr, "QEMU waiting for connection on: %s\n",
chr->filename);
tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
qemu_set_nonblock(s->listen_fd);
static void qemu_chr_socket_connected(int fd, Error *err, void *opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
if (fd < 0) {
check_report_connect_error(chr, err);
return;
}
return chr;
s->connect_err_reported = false;
qemu_chr_finish_socket_connection(chr, fd);
}
static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp)
{
TCPCharDriver *s = chr->opaque;
int fd;
if (s->is_listen) {
fd = socket_listen(s->addr, errp);
} else if (s->reconnect_time) {
fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr);
return fd >= 0;
} else {
fd = socket_connect(s->addr, errp, NULL, NULL);
}
if (fd < 0) {
return false;
}
qemu_chr_finish_socket_connection(chr, fd);
return true;
}
/*********************************************************/
@ -3396,6 +3508,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
const char *path = qemu_opt_get(opts, "path");
const char *host = qemu_opt_get(opts, "host");
const char *port = qemu_opt_get(opts, "port");
@ -3422,6 +3535,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
backend->socket->telnet = is_telnet;
backend->socket->has_wait = true;
backend->socket->wait = is_waitconnect;
backend->socket->has_reconnect = true;
backend->socket->reconnect = reconnect;
addr = g_new0(SocketAddress, 1);
if (path) {
@ -3820,6 +3935,9 @@ QemuOptsList qemu_chardev_opts = {
},{
.name = "delay",
.type = QEMU_OPT_BOOL,
},{
.name = "reconnect",
.type = QEMU_OPT_NUMBER,
},{
.name = "telnet",
.type = QEMU_OPT_BOOL,
@ -3964,26 +4082,95 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel,
#endif /* WIN32 */
static void socket_try_connect(CharDriverState *chr)
{
Error *err = NULL;
if (!qemu_chr_open_socket_fd(chr, &err)) {
check_report_connect_error(chr, err);
}
}
static gboolean socket_reconnect_timeout(gpointer opaque)
{
CharDriverState *chr = opaque;
TCPCharDriver *s = chr->opaque;
s->reconnect_timer = 0;
if (chr->be_open) {
return false;
}
socket_try_connect(chr);
return false;
}
static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
Error **errp)
{
CharDriverState *chr;
TCPCharDriver *s;
SocketAddress *addr = sock->addr;
bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
bool is_listen = sock->has_server ? sock->server : true;
bool is_telnet = sock->has_telnet ? sock->telnet : false;
bool is_waitconnect = sock->has_wait ? sock->wait : false;
int fd;
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
chr = qemu_chr_alloc();
s = g_malloc0(sizeof(TCPCharDriver));
s->fd = -1;
s->listen_fd = -1;
s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX;
s->is_listen = is_listen;
s->is_telnet = is_telnet;
s->do_nodelay = do_nodelay;
qapi_copy_SocketAddress(&s->addr, sock->addr);
chr->opaque = s;
chr->chr_write = tcp_chr_write;
chr->chr_sync_read = tcp_chr_sync_read;
chr->chr_close = tcp_chr_close;
chr->get_msgfds = tcp_get_msgfds;
chr->set_msgfds = tcp_set_msgfds;
chr->chr_add_client = tcp_chr_add_client;
chr->chr_add_watch = tcp_chr_add_watch;
chr->chr_update_read_handler = tcp_chr_update_read_handler;
/* be isn't opened until we get a connection */
chr->explicit_be_open = true;
chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE);
SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:",
addr, is_listen, is_telnet);
if (is_listen) {
fd = socket_listen(addr, errp);
} else {
fd = socket_connect(addr, errp, NULL, NULL);
if (is_telnet) {
s->do_telnetopt = 1;
}
} else if (reconnect > 0) {
s->reconnect_time = reconnect;
}
if (fd < 0) {
if (s->reconnect_time) {
socket_try_connect(chr);
} else if (!qemu_chr_open_socket_fd(chr, errp)) {
g_free(s);
g_free(chr->filename);
g_free(chr);
return NULL;
}
return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
is_telnet, is_waitconnect, errp);
if (is_listen && is_waitconnect) {
fprintf(stderr, "QEMU waiting for connection on: %s\n",
chr->filename);
tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
qemu_set_nonblock(s->listen_fd);
}
return chr;
}
static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp,

View File

@ -1930,9 +1930,9 @@ ETEXI
DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
"-chardev null,id=id[,mux=on|off]\n"
"-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay]\n"
" [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
"-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n"
"-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n"
" [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n"
"-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n"
"-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
" [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
"-chardev msmouse,id=id[,mux=on|off]\n"
@ -2004,7 +2004,7 @@ Options to each backend are described below.
A void device. This device will not emit any data, and will drop any data it
receives. The null backend does not take any options.
@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet]
@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}]
Create a two-way stream socket, which can be either a TCP or a unix socket. A
unix socket will be created if @option{path} is specified. Behaviour is
@ -2018,6 +2018,10 @@ connect to a listening socket.
@option{telnet} specifies that traffic on the socket should interpret telnet
escape sequences.
@option{reconnect} sets the timeout for reconnecting on non-server sockets when
the remote end goes away. qemu will delay this many seconds and then attempt
to reconnect. Zero disables reconnecting, and is the default.
TCP and unix socket options are given below:
@table @option
@ -2687,14 +2691,16 @@ telnet on port 5555 to access the QEMU port.
localhost 5555
@end table
@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay]
@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay][,reconnect=@var{seconds}]
The TCP Net Console has two modes of operation. It can send the serial
I/O to a location or wait for a connection from a location. By default
the TCP Net Console is sent to @var{host} at the @var{port}. If you use
the @var{server} option QEMU will wait for a client socket application
to connect to the port before continuing, unless the @code{nowait}
option was specified. The @code{nodelay} option disables the Nagle buffering
algorithm. If @var{host} is omitted, 0.0.0.0 is assumed. Only
algorithm. The @code{reconnect} option only applies if @var{noserver} is
set, if the connection goes down it will attempt to reconnect at the
given interval. If @var{host} is omitted, 0.0.0.0 is assumed. Only
one TCP connection at a time is accepted. You can use @code{telnet} to
connect to the corresponding character device.
@table @code
@ -2715,7 +2721,7 @@ MAGIC_SYSRQ sequence if you use a telnet that supports sending the break
sequence. Typically in unix telnet you do it with Control-] and then
type "send break" followed by pressing the enter key.
@item unix:@var{path}[,server][,nowait]
@item unix:@var{path}[,server][,nowait][,reconnect=@var{seconds}]
A unix domain socket is used instead of a tcp socket. The option works the
same as if you had specified @code{-serial tcp} except the unix domain socket
@var{path} is used for connections.

27
qtest.c
View File

@ -17,6 +17,7 @@
#include "exec/ioport.h"
#include "exec/memory.h"
#include "hw/irq.h"
#include "sysemu/accel.h"
#include "sysemu/sysemu.h"
#include "sysemu/cpus.h"
#include "qemu/config-file.h"
@ -519,7 +520,7 @@ static void configure_qtest_icount(const char *options)
qemu_opts_del(opts);
}
int qtest_init_accel(MachineClass *mc)
static int qtest_init_accel(MachineState *ms)
{
configure_qtest_icount("0");
return 0;
@ -557,3 +558,27 @@ bool qtest_driver(void)
{
return qtest_chr;
}
static void qtest_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "QTest";
ac->available = qtest_available;
ac->init_machine = qtest_init_accel;
ac->allowed = &qtest_allowed;
}
#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest")
static const TypeInfo qtest_accel_type = {
.name = TYPE_QTEST_ACCEL,
.parent = TYPE_ACCEL,
.class_init = qtest_accel_class_init,
};
static void qtest_type_init(void)
{
type_register_static(&qtest_accel_type);
}
type_init(qtest_type_init);

View File

@ -199,14 +199,13 @@ static void error_print_loc(void)
bool enable_timestamp_msg;
/*
* Print an error message to current monitor if we have one, else to stderr.
* Format arguments like sprintf(). The result should not contain
* Format arguments like vsprintf(). The result should not contain
* newlines.
* Prepend the current location and append a newline.
* It's wrong to call this in a QMP monitor. Use qerror_report() there.
*/
void error_report(const char *fmt, ...)
void error_vreport(const char *fmt, va_list ap)
{
va_list ap;
GTimeVal tv;
gchar *timestr;
@ -218,8 +217,22 @@ void error_report(const char *fmt, ...)
}
error_print_loc();
va_start(ap, fmt);
error_vprintf(fmt, ap);
va_end(ap);
error_printf("\n");
}
/*
* Print an error message to current monitor if we have one, else to stderr.
* Format arguments like sprintf(). The result should not contain
* newlines.
* Prepend the current location and append a newline.
* It's wrong to call this in a QMP monitor. Use qerror_report() there.
*/
void error_report(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error_vreport(fmt, ap);
va_end(ap);
}

View File

@ -234,6 +234,7 @@ static void wait_for_connect(void *opaque)
int val = 0, rc = 0;
socklen_t valsize = sizeof(val);
bool in_progress;
Error *err = NULL;
qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
@ -244,10 +245,12 @@ static void wait_for_connect(void *opaque)
/* update rc to contain error */
if (!rc && val) {
rc = -1;
errno = val;
}
/* connect error */
if (rc < 0) {
error_setg_errno(&err, errno, "Error connecting to socket");
closesocket(s->fd);
s->fd = rc;
}
@ -257,9 +260,14 @@ static void wait_for_connect(void *opaque)
while (s->current_addr->ai_next != NULL && s->fd < 0) {
s->current_addr = s->current_addr->ai_next;
s->fd = inet_connect_addr(s->current_addr, &in_progress, s, NULL);
if (s->fd < 0) {
error_free(err);
err = NULL;
error_setg_errno(&err, errno, "Unable to start socket connect");
}
/* connect in progress */
if (in_progress) {
return;
goto out;
}
}
@ -267,9 +275,11 @@ static void wait_for_connect(void *opaque)
}
if (s->callback) {
s->callback(s->fd, s->opaque);
s->callback(s->fd, err, s->opaque);
}
g_free(s);
out:
error_free(err);
}
static int inet_connect_addr(struct addrinfo *addr, bool *in_progress,
@ -401,7 +411,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp,
return sock;
} else {
if (callback) {
callback(sock, opaque);
callback(sock, NULL, opaque);
}
}
g_free(connect_state);
@ -769,7 +779,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp,
} else if (rc >= 0) {
/* non blocking socket immediate success, call callback */
if (callback != NULL) {
callback(sock, opaque);
callback(sock, NULL, opaque);
}
}
@ -919,7 +929,7 @@ int socket_connect(SocketAddress *addr, Error **errp,
fd = monitor_get_fd(cur_mon, addr->fd->str, errp);
if (fd >= 0 && callback) {
qemu_set_nonblock(fd);
callback(fd, opaque);
callback(fd, NULL, opaque);
}
break;

83
vl.c
View File

@ -61,6 +61,7 @@ int main(int argc, char **argv)
#include "qemu/sockets.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "sysemu/accel.h"
#include "hw/usb.h"
#include "hw/pcmcia.h"
#include "hw/i386/pc.h"
@ -213,11 +214,9 @@ static NotifierList exit_notifiers =
static NotifierList machine_init_done_notifiers =
NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers);
static bool tcg_allowed = true;
bool xen_allowed;
uint32_t xen_domid;
enum xen_mode xen_mode = XEN_EMULATE;
static int tcg_tb_size;
static int has_defaults = 1;
static int default_serial = 1;
@ -2681,84 +2680,6 @@ static MachineClass *machine_parse(const char *name)
exit(!name || !is_help_option(name));
}
static int tcg_init(MachineClass *mc)
{
tcg_exec_init(tcg_tb_size * 1024 * 1024);
return 0;
}
static struct {
const char *opt_name;
const char *name;
int (*available)(void);
int (*init)(MachineClass *mc);
bool *allowed;
} accel_list[] = {
{ "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },
{ "xen", "Xen", xen_available, xen_init, &xen_allowed },
{ "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },
{ "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed },
};
static int configure_accelerator(MachineClass *mc)
{
const char *p;
char buf[10];
int i, ret;
bool accel_initialised = false;
bool init_failed = false;
p = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (p == NULL) {
/* Use the default "accelerator", tcg */
p = "tcg";
}
while (!accel_initialised && *p != '\0') {
if (*p == ':') {
p++;
}
p = get_opt_name(buf, sizeof (buf), p, ':');
for (i = 0; i < ARRAY_SIZE(accel_list); i++) {
if (strcmp(accel_list[i].opt_name, buf) == 0) {
if (!accel_list[i].available()) {
printf("%s not supported for this target\n",
accel_list[i].name);
break;
}
*(accel_list[i].allowed) = true;
ret = accel_list[i].init(mc);
if (ret < 0) {
init_failed = true;
fprintf(stderr, "failed to initialize %s: %s\n",
accel_list[i].name,
strerror(-ret));
*(accel_list[i].allowed) = false;
} else {
accel_initialised = true;
}
break;
}
}
if (i == ARRAY_SIZE(accel_list)) {
fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf);
}
}
if (!accel_initialised) {
if (!init_failed) {
fprintf(stderr, "No accelerator found!\n");
}
exit(1);
}
if (init_failed) {
fprintf(stderr, "Back to %s accelerator.\n", accel_list[i].name);
}
return !accel_initialised;
}
void qemu_add_exit_notifier(Notifier *notify)
{
notifier_list_add(&exit_notifiers, notify);
@ -4264,7 +4185,7 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
configure_accelerator(machine_class);
configure_accelerator(current_machine);
if (qtest_chrdev) {
Error *local_err = NULL;

View File

@ -11,9 +11,3 @@
void xenstore_store_pv_console_info(int i, CharDriverState *chr)
{
}
int xen_init(MachineClass *mc)
{
return -ENOSYS;
}

View File

@ -11,6 +11,7 @@
#include "hw/xen/xen_backend.h"
#include "qmp-commands.h"
#include "sysemu/char.h"
#include "sysemu/accel.h"
//#define DEBUG_XEN
@ -109,7 +110,7 @@ static void xen_change_state_handler(void *opaque, int running,
}
}
int xen_init(MachineClass *mc)
static int xen_init(MachineState *ms)
{
xen_xc = xen_xc_interface_open(0, 0, 0);
if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
@ -121,3 +122,25 @@ int xen_init(MachineClass *mc)
return 0;
}
static void xen_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "Xen";
ac->init_machine = xen_init;
ac->allowed = &xen_allowed;
}
#define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen")
static const TypeInfo xen_accel_type = {
.name = TYPE_XEN_ACCEL,
.parent = TYPE_ACCEL,
.class_init = xen_accel_class_init,
};
static void xen_type_init(void)
{
type_register_static(&xen_accel_type);
}
type_init(xen_type_init);