qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 07/27] add memdev backend infrastructure


From: Igor Mammedov
Subject: [Qemu-devel] [PATCH 07/27] add memdev backend infrastructure
Date: Thu, 21 Nov 2013 03:38:28 +0100

Provides framework for splitting host RAM allocation/
policies into a separate backend that could be used
by devices. It would allow to separate host specific
options from device model like it's done for netdev & co.

It adds new:
   -memdev CLI option and corresponding memdev-add
    QMP/HMP commands.

Upon successful command execution an new memdev object
is added to QOM tree into "/backend/memdev" container.

Initially only legacy RAM backend is provided, which
uses memory_region_init_ram() allocator and compatible
with every CLI option that affects memory_region_init_ram().

memdev object is intendend for usage with Dimm device,
example:
 -memdev id=m1,size=1G -device dimm,id=dimm1,memdev=m1

Signed-off-by: Igor Mammedov <address@hidden>
---
 backends/Makefile.objs        |    2 +
 backends/hostmem.c            |  275 +++++++++++++++++++++++++++++++++++++++++
 backends/hostmem_compat_ram.c |   42 ++++++
 hmp-commands.hx               |   13 ++
 include/sysemu/hostmem.h      |  102 +++++++++++++++
 monitor.c                     |    1 +
 qapi-schema.json              |   18 +++
 qemu-options.hx               |    7 +
 qmp-commands.hx               |   27 ++++
 vl.c                          |   14 ++
 10 files changed, 501 insertions(+), 0 deletions(-)
 create mode 100644 backends/hostmem.c
 create mode 100644 backends/hostmem_compat_ram.c
 create mode 100644 include/sysemu/hostmem.h

diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 42557d5..01c4476 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -6,3 +6,5 @@ common-obj-$(CONFIG_BRLAPI) += baum.o
 $(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) 
 
 common-obj-$(CONFIG_TPM) += tpm.o
+
+common-obj-y += hostmem.o hostmem_compat_ram.o
diff --git a/backends/hostmem.c b/backends/hostmem.c
new file mode 100644
index 0000000..9948b63
--- /dev/null
+++ b/backends/hostmem.c
@@ -0,0 +1,275 @@
+/*
+ * QEMU Host Memory Backend
+ *
+ * Copyright (C) 2013 Red Hat Inc
+ *
+ * Authors:
+ *   Igor Mammedov <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "sysemu/hostmem.h"
+#include "sysemu/sysemu.h"
+#include "qapi/visitor.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/config-file.h"
+
+QemuOptsList qemu_memdev_opts = {
+    .name = "memdev",
+    .implied_opt_name = "type",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_memdev_opts.head),
+    .desc = {
+        { /* end of list */ }
+    },
+};
+
+static int set_object_prop(const char *name, const char *value, void *opaque)
+{
+    Error *local_err = NULL;
+    Object *obj = OBJECT(opaque);
+
+    object_property_parse(obj, value, name, &local_err);
+    if (error_is_set(&local_err)) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+        return -1;
+    }
+    return 0;
+}
+
+void memdev_add(QemuOpts *opts, Error **errp)
+{
+    Error *local_err = NULL;
+    HostMemoryBackendClass *bc;
+    Object *obj = NULL;
+    ObjectClass *oc;
+    const char *type;
+
+    type = qemu_opt_get(opts, "type");
+    if (!type) {
+        type = TYPE_COMPAT_RAM_MEMORY_BACKEND;
+    }
+
+    oc = object_class_by_name(type);
+    if (!oc) {
+        error_setg(&local_err, "Unknown memdev type: %s", type);
+        goto out;
+    }
+    if (object_class_is_abstract(oc)) {
+        error_setg(&local_err, "Can't create abstract memdev type: %s", type);
+        goto out;
+    }
+
+    obj = object_new(type);
+    if (!object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
+        error_setg(&local_err, "Invalid memdev type: %s", type);
+        goto out;
+    }
+
+    if (qemu_opt_foreach(opts, set_object_prop, obj, true)) {
+        error_setg(&local_err, "failed to create memdev");
+        goto out;
+    }
+    object_property_parse(obj, qemu_opts_id(opts), "id", &local_err);
+    if (error_is_set(&local_err)) {
+        goto out;
+    }
+
+    /* verify properties correctnes and initialize backend */
+    bc = MEMORY_BACKEND_GET_CLASS(obj);
+    if (bc->get_memory) {
+        HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+        if (!bc->get_memory(backend, &local_err)) {
+            goto out;
+        }
+    }
+
+    /* make backend available to the world via QOM tree */
+    object_property_add_child(container_get(qemu_get_backend(), "/memdev"),
+                              qemu_opts_id(opts), obj, &local_err);
+
+out:
+    if (error_is_set(&local_err)) {
+        error_propagate(errp, local_err);
+        if (obj) {
+            object_unref(obj);
+        }
+    }
+}
+
+int qmp_memdev_add(Monitor *mon, const QDict *qdict, QObject **ret)
+{
+    Error *local_err = NULL;
+    QemuOptsList *opts_list;
+    QemuOpts *opts = NULL;
+
+    opts_list = qemu_find_opts_err("memdev", &local_err);
+    if (error_is_set(&local_err)) {
+        goto out;
+    }
+
+    opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
+    if (error_is_set(&local_err)) {
+        goto out;
+    }
+
+    memdev_add(opts, &local_err);
+
+out:
+    if (opts) {
+        qemu_opts_del(opts);
+    }
+    if (error_is_set(&local_err)) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+        return -1;
+    }
+    return 0;
+}
+
+static void
+hostmemory_backend_get_size(Object *obj, Visitor *v, void *opaque,
+                            const char *name, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    uint64_t value = backend->size;
+
+    visit_type_size(v, &value, name, errp);
+}
+
+static void
+hostmemory_backend_set_size(Object *obj, Visitor *v, void *opaque,
+                            const char *name, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    uint64_t value;
+
+    visit_type_size(v, &value, name, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+    if (!value) {
+        error_setg(errp, "Property '%s.%s' doesn't take value '%" PRIu64 "'",
+                   object_get_typename(obj), name , value);
+        return;
+    }
+    backend->size = value;
+}
+
+static void
+hostmemory_backend_get_id(Object *obj, Visitor *v, void *opaque,
+                          const char *name, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    visit_type_str(v, &backend->id, name, errp);
+}
+
+static void
+hostmemory_backend_set_id(Object *obj, Visitor *v, void *opaque,
+                          const char *name, Error **errp)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+    Error *local_err = NULL;
+    Object *dup_obj;
+    char *str;
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    dup_obj = object_resolve_path(str, NULL);
+    if (dup_obj) {
+        error_setg(errp, "Duplicate property [%s.%s] value: '%s'",
+                   object_get_typename(obj), name, str);
+        error_propagate(errp, local_err);
+        g_free(str);
+        return;
+    }
+
+    if (backend->id) {
+        g_free(backend->id);
+    }
+    backend->id = str;
+}
+
+static void hostmemory_backend_initfn(Object *obj)
+{
+    object_property_add(obj, "id", "string",
+                        hostmemory_backend_get_id,
+                        hostmemory_backend_set_id, NULL, NULL, NULL);
+    object_property_add(obj, "size", "int",
+                        hostmemory_backend_get_size,
+                        hostmemory_backend_set_size, NULL, NULL, NULL);
+}
+
+static void hostmemory_backend_finalize(Object *obj)
+{
+    HostMemoryBackend *backend = MEMORY_BACKEND(obj);
+
+    g_free(backend->id);
+    if (memory_region_size(&backend->mr)) {
+        memory_region_destroy(&backend->mr);
+    }
+}
+
+static void
+hostmemory_backend_memory_init(HostMemoryBackend *backend, Error **errp)
+{
+    error_setg(errp, "memory_init is not implemented for type [%s]",
+               object_get_typename(OBJECT(backend)));
+}
+
+static MemoryRegion *
+hostmemory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
+{
+    HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(backend);
+    Object *obj = OBJECT(backend);
+    char *id = backend->id;
+
+    if (!id || (*id == '\0')) {
+        error_setg(errp, "Invalid property [%s.id] value: '%s'",
+                   object_get_typename(obj), id ? id : "");
+        return NULL;
+    }
+
+    if (!backend->size) {
+        error_setg(errp, "Invalid property [%s.size] value: %" PRIu64,
+                   object_get_typename(obj), backend->size);
+        return NULL;
+    }
+
+    bc->memory_init(backend, errp);
+
+    return memory_region_size(&backend->mr) ? &backend->mr : NULL;
+}
+
+static void
+hostmemory_backend_class_init(ObjectClass *oc, void *data)
+{
+    HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
+
+    bc->memory_init = hostmemory_backend_memory_init;
+    bc->get_memory = hostmemory_backend_get_memory;
+}
+
+static const TypeInfo hostmemory_backend_info = {
+    .name = TYPE_MEMORY_BACKEND,
+    .parent = TYPE_OBJECT,
+    .abstract = true,
+    .class_size = sizeof(HostMemoryBackendClass),
+    .class_init = hostmemory_backend_class_init,
+    .instance_size = sizeof(HostMemoryBackend),
+    .instance_init = hostmemory_backend_initfn,
+    .instance_finalize = hostmemory_backend_finalize,
+};
+
+static void register_types(void)
+{
+    type_register_static(&hostmemory_backend_info);
+}
+
+type_init(register_types);
diff --git a/backends/hostmem_compat_ram.c b/backends/hostmem_compat_ram.c
new file mode 100644
index 0000000..af1bbe0
--- /dev/null
+++ b/backends/hostmem_compat_ram.c
@@ -0,0 +1,42 @@
+/*
+ * QEMU Host Memory Backend
+ *
+ * Copyright (C) 2013 Red Hat Inc
+ *
+ * Authors:
+ *   Igor Mammedov <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "sysemu/hostmem.h"
+
+static void
+compat_ram_backend_memory_init(HostMemoryBackend *backend, Error **errp)
+{
+    if (!memory_region_size(&backend->mr)) {
+        memory_region_init_ram(&backend->mr, OBJECT(backend),
+                               backend->id, backend->size);
+    }
+}
+
+static void
+compat_ram_backend_class_init(ObjectClass *oc, void *data)
+{
+    HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
+
+    bc->memory_init = compat_ram_backend_memory_init;
+}
+
+static const TypeInfo compat_ram_backend_info = {
+    .name = TYPE_COMPAT_RAM_MEMORY_BACKEND,
+    .parent = TYPE_MEMORY_BACKEND,
+    .class_init = compat_ram_backend_class_init,
+};
+
+static void register_types(void)
+{
+    type_register_static(&compat_ram_backend_info);
+}
+
+type_init(register_types);
diff --git a/hmp-commands.hx b/hmp-commands.hx
index caae5ad..4f35ffa 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1719,3 +1719,16 @@ ETEXI
 STEXI
 @end table
 ETEXI
+
+    {
+        .name       = "memdev-add",
+        .args_type  = "memdev:O",
+        .params     = "id size",
+        .help       = "add host memory",
+        .mhandler.cmd_new = qmp_memdev_add,
+    },
+
+STEXI
address@hidden memdev-add
address@hidden memdev-add
+Add host memory.
diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h
new file mode 100644
index 0000000..7437712
--- /dev/null
+++ b/include/sysemu/hostmem.h
@@ -0,0 +1,102 @@
+/*
+ * QEMU Host Memory Backend
+ *
+ * Copyright (C) 2013 Red Hat Inc
+ *
+ * Authors:
+ *   Igor Mammedov <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_RAM_H
+#define QEMU_RAM_H
+
+#include "qom/object.h"
+#include "qapi/error.h"
+#include "exec/memory.h"
+#include "qemu/option.h"
+
+#define TYPE_MEMORY_BACKEND "host-memory"
+#define MEMORY_BACKEND(obj) \
+    OBJECT_CHECK(HostMemoryBackend, (obj), TYPE_MEMORY_BACKEND)
+#define MEMORY_BACKEND_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(HostMemoryBackendClass, (obj), TYPE_MEMORY_BACKEND)
+#define MEMORY_BACKEND_CLASS(klass) \
+    OBJECT_CLASS_CHECK(HostMemoryBackendClass, (klass), TYPE_MEMORY_BACKEND)
+
+typedef struct HostMemoryBackend HostMemoryBackend;
+typedef struct HostMemoryBackendClass HostMemoryBackendClass;
+
+/**
+ * HostMemoryBackendClass:
+ * @parent_class: opaque parent class container
+ * @memory_init: hook for derived classes to perform memory allocation
+ * @get_memory: get #MemoryRegion backed by @backend and link @backend
+ *   with user @uobj to prevent backend disapearing while @uobj exists.
+ *   @uobj must have "backend" link property or @get_memory will fail.
+ *   retuns: pointer to intialized #MemoryRegion on success or
+ *           NULL on failure with error set in errp
+ *
+ */
+struct HostMemoryBackendClass {
+    ObjectClass parent_class;
+
+    void (*memory_init)(HostMemoryBackend *backend, Error **errp);
+    MemoryRegion* (*get_memory)(HostMemoryBackend *backend, Error **errp);
+};
+
+/**
+ * @HostMemoryBackend
+ *
+ * @parent: opaque parent object container
+ * @size: amount of memory backend provides
+ * @id: unique identification string in memdev namespace
+ * @mr: MemoryRegion representing host memory belonging to backend
+ */
+struct HostMemoryBackend {
+    /* private */
+    Object parent;
+
+    /* protected */
+    char *id;
+    uint64_t size;
+
+    MemoryRegion mr;
+};
+
+extern QemuOptsList qemu_memdev_opts;
+
+/**
+ * @qmp_memdev_add:
+ *   QMP/HMP memdev-add command handler
+ * returns 0 on success or -1 on failure
+ */
+int qmp_memdev_add(Monitor *mon, const QDict *qdict, QObject **ret);
+
+/**
+ * @memdev_add:
+ *   CLI "-memdev" option parser
+ * @opts: options for accossiated with -memdev
+ * @errp: returns an error if this function fails
+ */
+void memdev_add(QemuOpts *opts, Error **errp);
+
+/**
+ * @memdev_name:
+ * @id: backend identification string
+ *
+ * returns backend name in format "memdev[id]",
+ * caller is responsible for freeing returned value.
+ */
+char *memdev_name(const char *id);
+
+/* hostmem_compat_ram.c */
+/**
+ * @TYPE_COMPAT_RAM_MEMORY_BACKEND:
+ * name of backend that uses legacy RAM allocation methods
+ * implemented by #memory_region_init_ram
+ */
+#define TYPE_COMPAT_RAM_MEMORY_BACKEND "compat-ram-host-memory"
+
+#endif
diff --git a/monitor.c b/monitor.c
index 845f608..39b4d8b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -67,6 +67,7 @@
 #include "qmp-commands.h"
 #include "hmp.h"
 #include "qemu/thread.h"
+#include "sysemu/hostmem.h"
 
 /* for pic/irq_info */
 #if defined(TARGET_SPARC)
diff --git a/qapi-schema.json b/qapi-schema.json
index 76c98a7..c1a20d2 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4212,3 +4212,21 @@
 # Since: 1.7
 ##
 { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
+
+##
+# @memdev_add:
+#
+# Add a host memory backend.
+#
+# @id: the name of the new memory backend
+# @size: amount of memory backend should allocate
+# @type: backend type. [default: compat-ram-host-memory]
+#
+# Since: 1.8
+#
+# Returns: Nothing on success
+##
+{ 'command': 'memdev-add',
+  'data': {'id': 'str', 'size': 'size', '*type': 'str'},
+  'gen': 'no'
+}
diff --git a/qemu-options.hx b/qemu-options.hx
index fe4559b..314b731 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3125,3 +3125,10 @@ HXCOMM This is the last statement. Insert new options 
before this line!
 STEXI
 @end table
 ETEXI
+
+DEF("memdev", HAS_ARG, QEMU_OPTION_memdev,
+    "-memdev [backend-type,]id=str,size=value\n"
+    "                add host memory\n"
+    "                default backend-type: compat-ram-host-memory\n",
+    QEMU_ARCH_ALL)
+STEXI
diff --git a/qmp-commands.hx b/qmp-commands.hx
index fba15cd..f2a8998 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3291,7 +3291,34 @@ Example (2):
          }
        }
      }
+<- { "return": {} }
+
+EQMP
+
+    {
+        .name       = "memdev-add",
+        .args_type  = "memdev:O",
+        .mhandler.cmd_new = qmp_memdev_add,
+    },
+
+SQMP
+memdev-add
+----------
+
+Add host memory.
+
+Arguments:
+
+- "id": the device's ID, must be unique (json-string)
+- "size": amount of memory backend should allocate (json-int)
+- "type": backend type (json-string, optional), default: compat-ram-host-memory
+
+Example:
 
+-> { "execute": "memdev-add",
+     "arguments": { "id": "memdev1",
+                    "size": "1G",
+                    "type": "compat-ram-host-memory" } }
 <- { "return": {} }
 
 EQMP
diff --git a/vl.c b/vl.c
index 1de3d57..2e3a5a5 100644
--- a/vl.c
+++ b/vl.c
@@ -170,6 +170,7 @@ int main(int argc, char **argv)
 
 #include "ui/qemu-spice.h"
 #include "qapi/string-input-visitor.h"
+#include "sysemu/hostmem.h"
 
 //#define DEBUG_NET
 //#define DEBUG_SLIRP
@@ -2929,6 +2930,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_realtime_opts);
     qemu_add_opts(&qemu_msg_opts);
     qemu_add_opts(&qemu_mem_opts);
+    qemu_add_opts(&qemu_memdev_opts);
 
     runstate_init();
 
@@ -3859,6 +3861,18 @@ int main(int argc, char **argv, char **envp)
                 }
                 configure_msg(opts);
                 break;
+            case QEMU_OPTION_memdev:
+                {
+                    Error *local_err = NULL;
+                    opts = qemu_opts_parse(qemu_find_opts("memdev"), optarg, 
1);
+                    memdev_add(opts, &local_err);
+                    if (error_is_set(&local_err)) {
+                        qerror_report_err(local_err);
+                        error_free(local_err);
+                        exit(1);
+                    }
+                    break;
+                }
             default:
                 os_parse_cmd_args(popt->index, optarg);
             }
-- 
1.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]