aboutsummaryrefslogtreecommitdiff
path: root/accel
diff options
context:
space:
mode:
Diffstat (limited to 'accel')
-rw-r--r--accel/Makefile.objs2
-rw-r--r--accel/accel.c73
-rw-r--r--accel/kvm/kvm-all.c161
-rw-r--r--accel/tcg/tcg-all.c149
4 files changed, 290 insertions, 95 deletions
diff --git a/accel/Makefile.objs b/accel/Makefile.objs
index 8b498d39d8..17e5ac6061 100644
--- a/accel/Makefile.objs
+++ b/accel/Makefile.objs
@@ -1,4 +1,4 @@
-obj-$(CONFIG_SOFTMMU) += accel.o
+common-obj-$(CONFIG_SOFTMMU) += accel.o
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_TCG) += tcg/
diff --git a/accel/accel.c b/accel/accel.c
index 5fa31717b4..1c5c3a6abb 100644
--- a/accel/accel.c
+++ b/accel/accel.c
@@ -28,13 +28,7 @@
#include "hw/boards.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 "qemu/error-report.h"
-#include "qemu/option.h"
-#include "qapi/error.h"
static const TypeInfo accel_type = {
.name = TYPE_ACCEL,
@@ -44,7 +38,7 @@ static const TypeInfo accel_type = {
};
/* Lookup AccelClass from opt_name. Returns NULL if not found */
-static AccelClass *accel_find(const char *opt_name)
+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));
@@ -52,11 +46,9 @@ static AccelClass *accel_find(const char *opt_name)
return ac;
}
-static int accel_init_machine(AccelClass *acc, MachineState *ms)
+int accel_init_machine(AccelState *accel, MachineState *ms)
{
- ObjectClass *oc = OBJECT_CLASS(acc);
- const char *cname = object_class_get_name(oc);
- AccelState *accel = ACCEL(object_new(cname));
+ AccelClass *acc = ACCEL_GET_CLASS(accel);
int ret;
ms->accelerator = accel;
*(acc->allowed) = true;
@@ -71,65 +63,6 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
return ret;
}
-void configure_accelerator(MachineState *ms, const char *progname)
-{
- const char *accel;
- char **accel_list, **tmp;
- int ret;
- bool accel_initialised = false;
- bool init_failed = false;
- AccelClass *acc = NULL;
-
- accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
- if (accel == NULL) {
- /* Select the default accelerator */
- int pnlen = strlen(progname);
- if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
- /* If the program name ends with "kvm", we prefer KVM */
- accel = "kvm:tcg";
- } else {
-#if defined(CONFIG_TCG)
- accel = "tcg";
-#elif defined(CONFIG_KVM)
- accel = "kvm";
-#else
- error_report("No accelerator selected and"
- " no default accelerator available");
- exit(1);
-#endif
- }
- }
-
- accel_list = g_strsplit(accel, ":", 0);
-
- for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
- acc = accel_find(*tmp);
- if (!acc) {
- continue;
- }
- ret = accel_init_machine(acc, ms);
- if (ret < 0) {
- init_failed = true;
- error_report("failed to initialize %s: %s",
- acc->name, strerror(-ret));
- } else {
- accel_initialised = true;
- }
- }
- g_strfreev(accel_list);
-
- if (!accel_initialised) {
- if (!init_failed) {
- error_report("-machine accel=%s: No accelerator found", accel);
- }
- exit(1);
- }
-
- if (init_failed) {
- error_report("Back to %s accelerator", acc->name);
- }
-}
-
void accel_setup_post(MachineState *ms)
{
AccelState *accel = ms->accelerator;
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index ca00daa2f5..b2f1a5bcb5 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -41,6 +41,9 @@
#include "hw/irq.h"
#include "sysemu/sev.h"
#include "sysemu/balloon.h"
+#include "qapi/visitor.h"
+#include "qapi/qapi-types-common.h"
+#include "qapi/qapi-visit-common.h"
#include "hw/boards.h"
@@ -92,6 +95,10 @@ struct KVMState
int max_nested_state_len;
int many_ioeventfds;
int intx_set_mask;
+ int kvm_shadow_mem;
+ bool kernel_irqchip_allowed;
+ bool kernel_irqchip_required;
+ bool kernel_irqchip_split;
bool sync_mmu;
bool manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but
@@ -518,6 +525,27 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+/* Allocate the dirty bitmap for a slot */
+static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem)
+{
+ /*
+ * XXX bad kernel interface alert
+ * For dirty bitmap, kernel allocates array of size aligned to
+ * bits-per-long. But for case when the kernel is 64bits and
+ * the userspace is 32bits, userspace can't align to the same
+ * bits-per-long, since sizeof(long) is different between kernel
+ * and user space. This way, userspace will provide buffer which
+ * may be 4 bytes less than the kernel will use, resulting in
+ * userspace memory corruption (which is not detectable by valgrind
+ * too, in most cases).
+ * So for now, let's align to 64 instead of HOST_LONG_BITS here, in
+ * a hope that sizeof(long) won't become >8 any time soon.
+ */
+ hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
+ /*HOST_LONG_BITS*/ 64) / 8;
+ mem->dirty_bmap = g_malloc0(bitmap_size);
+}
+
/**
* kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
*
@@ -550,23 +578,9 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
goto out;
}
- /* XXX bad kernel interface alert
- * For dirty bitmap, kernel allocates array of size aligned to
- * bits-per-long. But for case when the kernel is 64bits and
- * the userspace is 32bits, userspace can't align to the same
- * bits-per-long, since sizeof(long) is different between kernel
- * and user space. This way, userspace will provide buffer which
- * may be 4 bytes less than the kernel will use, resulting in
- * userspace memory corruption (which is not detectable by valgrind
- * too, in most cases).
- * So for now, let's align to 64 instead of HOST_LONG_BITS here, in
- * a hope that sizeof(long) won't become >8 any time soon.
- */
if (!mem->dirty_bmap) {
- hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
- /*HOST_LONG_BITS*/ 64) / 8;
/* Allocate on the first log_sync, once and for all */
- mem->dirty_bmap = g_malloc0(bitmap_size);
+ kvm_memslot_init_dirty_bitmap(mem);
}
d.dirty_bitmap = mem->dirty_bmap;
@@ -1067,6 +1081,13 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
+ if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
+ /*
+ * Reallocate the bmap; it means it doesn't disappear in
+ * middle of a migrate.
+ */
+ kvm_memslot_init_dirty_bitmap(mem);
+ }
err = kvm_set_user_memory_region(kml, mem, true);
if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
@@ -1758,7 +1779,7 @@ void kvm_irqchip_set_qemuirq_gsi(KVMState *s, qemu_irq irq, int gsi)
g_hash_table_insert(s->gsimap, irq, GINT_TO_POINTER(gsi));
}
-static void kvm_irqchip_create(MachineState *machine, KVMState *s)
+static void kvm_irqchip_create(KVMState *s)
{
int ret;
@@ -1776,9 +1797,9 @@ static void kvm_irqchip_create(MachineState *machine, KVMState *s)
/* First probe and see if there's a arch-specific hook to create the
* in-kernel irqchip for us */
- ret = kvm_arch_irqchip_create(machine, s);
+ ret = kvm_arch_irqchip_create(s);
if (ret == 0) {
- if (machine_kernel_irqchip_split(machine)) {
+ if (s->kernel_irqchip_split) {
perror("Split IRQ chip mode not supported.");
exit(1);
} else {
@@ -2049,8 +2070,8 @@ static int kvm_init(MachineState *ms)
goto err;
}
- if (machine_kernel_irqchip_allowed(ms)) {
- kvm_irqchip_create(ms, s);
+ if (s->kernel_irqchip_allowed) {
+ kvm_irqchip_create(s);
}
if (kvm_eventfds_allowed) {
@@ -2940,6 +2961,93 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
return false;
}
+static void kvm_get_kvm_shadow_mem(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ KVMState *s = KVM_STATE(obj);
+ int64_t value = s->kvm_shadow_mem;
+
+ visit_type_int(v, name, &value, errp);
+}
+
+static void kvm_set_kvm_shadow_mem(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ KVMState *s = KVM_STATE(obj);
+ Error *error = NULL;
+ int64_t value;
+
+ visit_type_int(v, name, &value, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
+ s->kvm_shadow_mem = value;
+}
+
+static void kvm_set_kernel_irqchip(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ Error *err = NULL;
+ KVMState *s = KVM_STATE(obj);
+ OnOffSplit mode;
+
+ visit_type_OnOffSplit(v, name, &mode, &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ } else {
+ switch (mode) {
+ case ON_OFF_SPLIT_ON:
+ s->kernel_irqchip_allowed = true;
+ s->kernel_irqchip_required = true;
+ s->kernel_irqchip_split = false;
+ break;
+ case ON_OFF_SPLIT_OFF:
+ s->kernel_irqchip_allowed = false;
+ s->kernel_irqchip_required = false;
+ s->kernel_irqchip_split = false;
+ break;
+ case ON_OFF_SPLIT_SPLIT:
+ s->kernel_irqchip_allowed = true;
+ s->kernel_irqchip_required = true;
+ s->kernel_irqchip_split = true;
+ break;
+ default:
+ /* The value was checked in visit_type_OnOffSplit() above. If
+ * we get here, then something is wrong in QEMU.
+ */
+ abort();
+ }
+ }
+}
+
+bool kvm_kernel_irqchip_allowed(void)
+{
+ return kvm_state->kernel_irqchip_allowed;
+}
+
+bool kvm_kernel_irqchip_required(void)
+{
+ return kvm_state->kernel_irqchip_required;
+}
+
+bool kvm_kernel_irqchip_split(void)
+{
+ return kvm_state->kernel_irqchip_split;
+}
+
+static void kvm_accel_instance_init(Object *obj)
+{
+ KVMState *s = KVM_STATE(obj);
+
+ s->kvm_shadow_mem = -1;
+}
+
static void kvm_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
@@ -2947,11 +3055,24 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data)
ac->init_machine = kvm_init;
ac->has_memory = kvm_accel_has_memory;
ac->allowed = &kvm_allowed;
+
+ object_class_property_add(oc, "kernel-irqchip", "on|off|split",
+ NULL, kvm_set_kernel_irqchip,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, "kernel-irqchip",
+ "Configure KVM in-kernel irqchip", &error_abort);
+
+ object_class_property_add(oc, "kvm-shadow-mem", "int",
+ kvm_get_kvm_shadow_mem, kvm_set_kvm_shadow_mem,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, "kvm-shadow-mem",
+ "KVM shadow MMU size", &error_abort);
}
static const TypeInfo kvm_accel_type = {
.name = TYPE_KVM_ACCEL,
.parent = TYPE_ACCEL,
+ .instance_init = kvm_accel_instance_init,
.class_init = kvm_accel_class_init,
.instance_size = sizeof(KVMState),
};
diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c
index c59d5b0024..1dc384c8d2 100644
--- a/accel/tcg/tcg-all.c
+++ b/accel/tcg/tcg-all.c
@@ -30,8 +30,23 @@
#include "cpu.h"
#include "sysemu/cpus.h"
#include "qemu/main-loop.h"
+#include "tcg/tcg.h"
+#include "include/qapi/error.h"
+#include "include/qemu/error-report.h"
+#include "include/hw/boards.h"
+#include "qapi/qapi-builtin-visit.h"
-unsigned long tcg_tb_size;
+typedef struct TCGState {
+ AccelState parent_obj;
+
+ bool mttcg_enabled;
+ unsigned long tb_size;
+} TCGState;
+
+#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
+
+#define TCG_STATE(obj) \
+ OBJECT_CHECK(TCGState, (obj), TYPE_TCG_ACCEL)
/* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask)
@@ -58,27 +73,153 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
}
}
+/*
+ * We default to false if we know other options have been enabled
+ * which are currently incompatible with MTTCG. Otherwise when each
+ * guest (target) has been updated to support:
+ * - atomic instructions
+ * - memory ordering primitives (barriers)
+ * they can set the appropriate CONFIG flags in ${target}-softmmu.mak
+ *
+ * Once a guest architecture has been converted to the new primitives
+ * there are two remaining limitations to check.
+ *
+ * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
+ * - The host must have a stronger memory order than the guest
+ *
+ * It may be possible in future to support strong guests on weak hosts
+ * but that will require tagging all load/stores in a guest with their
+ * implicit memory order requirements which would likely slow things
+ * down a lot.
+ */
+
+static bool check_tcg_memory_orders_compatible(void)
+{
+#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO)
+ return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0;
+#else
+ return false;
+#endif
+}
+
+static bool default_mttcg_enabled(void)
+{
+ if (use_icount || TCG_OVERSIZED_GUEST) {
+ return false;
+ } else {
+#ifdef TARGET_SUPPORTS_MTTCG
+ return check_tcg_memory_orders_compatible();
+#else
+ return false;
+#endif
+ }
+}
+
+static void tcg_accel_instance_init(Object *obj)
+{
+ TCGState *s = TCG_STATE(obj);
+
+ s->mttcg_enabled = default_mttcg_enabled();
+}
+
static int tcg_init(MachineState *ms)
{
- tcg_exec_init(tcg_tb_size * 1024 * 1024);
+ TCGState *s = TCG_STATE(current_machine->accelerator);
+
+ tcg_exec_init(s->tb_size * 1024 * 1024);
cpu_interrupt_handler = tcg_handle_interrupt;
+ mttcg_enabled = s->mttcg_enabled;
return 0;
}
+static char *tcg_get_thread(Object *obj, Error **errp)
+{
+ TCGState *s = TCG_STATE(obj);
+
+ return g_strdup(s->mttcg_enabled ? "multi" : "single");
+}
+
+static void tcg_set_thread(Object *obj, const char *value, Error **errp)
+{
+ TCGState *s = TCG_STATE(obj);
+
+ if (strcmp(value, "multi") == 0) {
+ if (TCG_OVERSIZED_GUEST) {
+ error_setg(errp, "No MTTCG when guest word size > hosts");
+ } else if (use_icount) {
+ error_setg(errp, "No MTTCG when icount is enabled");
+ } else {
+#ifndef TARGET_SUPPORTS_MTTCG
+ warn_report("Guest not yet converted to MTTCG - "
+ "you may get unexpected results");
+#endif
+ if (!check_tcg_memory_orders_compatible()) {
+ warn_report("Guest expects a stronger memory ordering "
+ "than the host provides");
+ error_printf("This may cause strange/hard to debug errors\n");
+ }
+ s->mttcg_enabled = true;
+ }
+ } else if (strcmp(value, "single") == 0) {
+ s->mttcg_enabled = false;
+ } else {
+ error_setg(errp, "Invalid 'thread' setting %s", value);
+ }
+}
+
+static void tcg_get_tb_size(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ TCGState *s = TCG_STATE(obj);
+ uint32_t value = s->tb_size;
+
+ visit_type_uint32(v, name, &value, errp);
+}
+
+static void tcg_set_tb_size(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ TCGState *s = TCG_STATE(obj);
+ Error *error = NULL;
+ uint32_t value;
+
+ visit_type_uint32(v, name, &value, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
+ s->tb_size = value;
+}
+
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")
+ object_class_property_add_str(oc, "thread",
+ tcg_get_thread,
+ tcg_set_thread,
+ NULL);
+
+ object_class_property_add(oc, "tb-size", "int",
+ tcg_get_tb_size, tcg_set_tb_size,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, "tb-size",
+ "TCG translation block cache size", &error_abort);
+
+}
static const TypeInfo tcg_accel_type = {
.name = TYPE_TCG_ACCEL,
.parent = TYPE_ACCEL,
+ .instance_init = tcg_accel_instance_init,
.class_init = tcg_accel_class_init,
+ .instance_size = sizeof(TCGState),
};
static void register_accel_types(void)