diff options
-rw-r--r-- | Makefile.objs | 1 | ||||
-rw-r--r-- | accel.c | 157 | ||||
-rw-r--r-- | arch_init.c | 5 | ||||
-rw-r--r-- | hw/scsi/virtio-scsi.c | 9 | ||||
-rw-r--r-- | include/hw/boards.h | 4 | ||||
-rw-r--r-- | include/hw/xen/xen.h | 1 | ||||
-rw-r--r-- | include/qemu/error-report.h | 1 | ||||
-rw-r--r-- | include/qemu/sockets.h | 2 | ||||
-rw-r--r-- | include/qemu/typedefs.h | 1 | ||||
-rw-r--r-- | include/sysemu/accel.h | 62 | ||||
-rw-r--r-- | include/sysemu/arch_init.h | 1 | ||||
-rw-r--r-- | include/sysemu/kvm.h | 2 | ||||
-rw-r--r-- | include/sysemu/qtest.h | 1 | ||||
-rw-r--r-- | kvm-all.c | 40 | ||||
-rw-r--r-- | kvm-stub.c | 5 | ||||
-rw-r--r-- | migration-tcp.c | 4 | ||||
-rw-r--r-- | migration-unix.c | 4 | ||||
-rw-r--r-- | pc-bios/linuxboot.bin | bin | 1024 -> 1024 bytes | |||
-rw-r--r-- | pc-bios/optionrom/linuxboot.S | 47 | ||||
-rw-r--r-- | pc-bios/optionrom/optionrom.h | 21 | ||||
-rw-r--r-- | qapi-schema.json | 15 | ||||
-rw-r--r-- | qemu-char.c | 359 | ||||
-rw-r--r-- | qemu-options.hx | 20 | ||||
-rw-r--r-- | qtest.c | 27 | ||||
-rw-r--r-- | util/qemu-error.c | 23 | ||||
-rw-r--r-- | util/qemu-sockets.c | 20 | ||||
-rw-r--r-- | vl.c | 83 | ||||
-rw-r--r-- | xen-common-stub.c | 6 | ||||
-rw-r--r-- | xen-common.c | 25 |
29 files changed, 711 insertions, 235 deletions
diff --git a/Makefile.objs b/Makefile.objs index 97db978d16..add83755be 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -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 diff --git a/accel.c b/accel.c new file mode 100644 index 0000000000..74e41daaa5 --- /dev/null +++ b/accel.c @@ -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); diff --git a/arch_init.c b/arch_init.c index c974f3fea9..9b3e25d805 100644 --- a/arch_init.c +++ b/arch_init.c @@ -1337,11 +1337,6 @@ void cpudef_init(void) #endif } -int tcg_available(void) -{ - return 1; -} - int kvm_available(void) { #ifdef CONFIG_KVM diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 203e62449a..6c02fe2b9a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -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) diff --git a/include/hw/boards.h b/include/hw/boards.h index 663f16acdd..e07c03f846 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -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 diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index f71f2d8963..b0ed04caa9 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -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) diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h index 000eae3957..7ab235590e 100644 --- a/include/qemu/error-report.h +++ b/include/qemu/error-report.h @@ -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; diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index fdbb196787..f47dae614a 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -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); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 5f20b0e263..04df51b6fc 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -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; diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h new file mode 100644 index 0000000000..997720f36c --- /dev/null +++ b/include/sysemu/accel.h @@ -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 diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 769ec069b7..54b36c16c4 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -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); diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 77ee240875..b0cd657f77 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -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); diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 95c9ade778..05473b75a5 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -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) @@ -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); diff --git a/kvm-stub.c b/kvm-stub.c index 8e7737caa9..43fc0dd01e 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -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) { } diff --git a/migration-tcp.c b/migration-tcp.c index 2e34517bb9..91c9cf381e 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -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 { diff --git a/migration-unix.c b/migration-unix.c index 0a5f8a1332..1cdadfbc83 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -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 { diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin Binary files differindex e7c36694f9..130103fb73 100644 --- a/pc-bios/linuxboot.bin +++ b/pc-bios/linuxboot.bin diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S index 748c831160..5bc0af08e0 100644 --- a/pc-bios/optionrom/linuxboot.S +++ b/pc-bios/optionrom/linuxboot.S @@ -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 diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h index ce436085d9..f1a9021ec1 100644 --- a/pc-bios/optionrom/optionrom.h +++ b/pc-bios/optionrom/optionrom.h @@ -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); \ diff --git a/qapi-schema.json b/qapi-schema.json index 2e9e261e0c..4f0d7e3250 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -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: diff --git a/qemu-char.c b/qemu-char.c index 8623c70964..bd0709bab5 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -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); - - 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; + TCPCharDriver *s = chr->opaque; - 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) { @@ -3821,6 +3936,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, diff --git a/qemu-options.hx b/qemu-options.hx index 365b56c424..22cf3b94ce 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -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. @@ -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); diff --git a/util/qemu-error.c b/util/qemu-error.c index 7b167fd06b..9bba5f53d8 100644 --- a/util/qemu-error.c +++ b/util/qemu-error.c @@ -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); +} diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 1eef590af5..a76bb3c913 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -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; @@ -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; diff --git a/xen-common-stub.c b/xen-common-stub.c index bd56ca2ce5..906f991f1e 100644 --- a/xen-common-stub.c +++ b/xen-common-stub.c @@ -11,9 +11,3 @@ void xenstore_store_pv_console_info(int i, CharDriverState *chr) { } - -int xen_init(MachineClass *mc) -{ - return -ENOSYS; -} - diff --git a/xen-common.c b/xen-common.c index f07b35e471..56359ca725 100644 --- a/xen-common.c +++ b/xen-common.c @@ -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); |