diff mbox

[07/27] add memdev backend infrastructure

Message ID 1385001528-12003-8-git-send-email-imammedo@redhat.com
State New
Headers show

Commit Message

Igor Mammedov Nov. 21, 2013, 2:38 a.m. UTC
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 <imammedo@redhat.com>
---
 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

Comments

Paolo Bonzini Nov. 25, 2013, 12:54 p.m. UTC | #1
Il 21/11/2013 03:38, Igor Mammedov ha scritto:
> +
> +    /* 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;
> +        }
> +    }

So this is why you need a new command-line option.

I think we need a generic mechanism for post-initialization of whatever
is given on the command line.  Perhaps you can do that with an
interface, and get rid of -memdev and memdev_add altogether?

MemoryBackend's implementation of the interface's sole method would call
get_memory, of course.

Paolo

> +    /* 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);
Igor Mammedov Nov. 25, 2013, 4:01 p.m. UTC | #2
On Mon, 25 Nov 2013 13:54:10 +0100
Paolo Bonzini <pbonzini@redhat.com> wrote:

> Il 21/11/2013 03:38, Igor Mammedov ha scritto:
> > +
> > +    /* 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;
> > +        }
> > +    }
> 
> So this is why you need a new command-line option.
> 
> I think we need a generic mechanism for post-initialization of whatever
> is given on the command line.  Perhaps you can do that with an
> interface, and get rid of -memdev and memdev_add altogether?
> 
> MemoryBackend's implementation of the interface's sole method would call
> get_memory, of course.

What I would use instead of memdev_add in CLI/HMP/QMP?
Could you explain it a bit more, please?

> Paolo
> 
> > +    /* 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);
>
Paolo Bonzini Nov. 25, 2013, 4:09 p.m. UTC | #3
Il 25/11/2013 17:01, Igor Mammedov ha scritto:
>> > So this is why you need a new command-line option.
>> > 
>> > I think we need a generic mechanism for post-initialization of whatever
>> > is given on the command line.  Perhaps you can do that with an
>> > interface, and get rid of -memdev and memdev_add altogether?
>> > 
>> > MemoryBackend's implementation of the interface's sole method would call
>> > get_memory, of course.
> 
> What I would use instead of memdev_add in CLI/HMP/QMP?

We could add a new object_add command.

> Could you explain it a bit more, please?

The interface would look like

struct QOMCommandLineIface {
    void complete(Object *object, Error **errp);
    Object *get_base_path(void);
}

MemoryBackend could implement it like this:

void memory_backend_complete(Object *object, Error **errp)
{
    MemoryBackend *backend = MEMORY_BACKEND(object);
    MemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(obj);
    if (bc->get_memory) {
        bc->get_memory(backend, errp);
    }
}

Object *memory_backend_get_base_path(void);
{
    return container_get(qdev_get_backend(), "/memdev"),
}

A default implementation can be added to RNGBackend and TPMBackend.
vl.c can use the interface like this in object_create:

    obj = object_new(type);
    QOMCommandLineIface *cmdline_iface;

    if (IS_QOM_COMMAND_LINE(obj)) {
        object_unref(obj);
        error...
        return -1;
    }

    if (qemu_opt_foreach(opts, object_set_property, obj, 1) < 0) {
        object_unref(obj);
        return -1;
    }

    cmdline_iface = QOM_COMMAND_LINE_GET_IFACE(obj);
    cmdline_iface->complete(obj, &local_err);
    if (local_err)) {
        error_propagate(...)
        object_unref(obj);
        return -1;
    }

    object_property_add_child(cmdline_iface->get_base_path(),
                              id, obj, NULL);

Then you can just use -object instead of -memdev.
Igor Mammedov Nov. 27, 2013, 2:37 p.m. UTC | #4
On Mon, 25 Nov 2013 17:09:37 +0100
Paolo Bonzini <pbonzini@redhat.com> wrote:

> Il 25/11/2013 17:01, Igor Mammedov ha scritto:
> >> > So this is why you need a new command-line option.
> >> > 
> >> > I think we need a generic mechanism for post-initialization of whatever
> >> > is given on the command line.  Perhaps you can do that with an
> >> > interface, and get rid of -memdev and memdev_add altogether?
> >> > 
> >> > MemoryBackend's implementation of the interface's sole method would call
> >> > get_memory, of course.
> > 
> > What I would use instead of memdev_add in CLI/HMP/QMP?
> 
> We could add a new object_add command.
> 
> > Could you explain it a bit more, please?
> 
> The interface would look like
> 
> struct QOMCommandLineIface {
>     void complete(Object *object, Error **errp);
>     Object *get_base_path(void);
> }
> 
> MemoryBackend could implement it like this:
> 
> void memory_backend_complete(Object *object, Error **errp)
> {
>     MemoryBackend *backend = MEMORY_BACKEND(object);
>     MemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(obj);
>     if (bc->get_memory) {
>         bc->get_memory(backend, errp);
>     }
> }
> 
> Object *memory_backend_get_base_path(void);
> {
>     return container_get(qdev_get_backend(), "/memdev"),
> }
> 
> A default implementation can be added to RNGBackend and TPMBackend.
> vl.c can use the interface like this in object_create:
> 
>     obj = object_new(type);
>     QOMCommandLineIface *cmdline_iface;
> 
>     if (IS_QOM_COMMAND_LINE(obj)) {
>         object_unref(obj);
>         error...
>         return -1;
>     }
> 
>     if (qemu_opt_foreach(opts, object_set_property, obj, 1) < 0) {
>         object_unref(obj);
>         return -1;
>     }
> 
>     cmdline_iface = QOM_COMMAND_LINE_GET_IFACE(obj);
>     cmdline_iface->complete(obj, &local_err);
>     if (local_err)) {
>         error_propagate(...)
>         object_unref(obj);
>         return -1;
>     }
> 
>     object_property_add_child(cmdline_iface->get_base_path(),
>                               id, obj, NULL);
> 
> Then you can just use -object instead of -memdev.
It looks like "realize" for -object / object-add implemented via
an interface.

Maybe it should be renamed from QOMCommandLineIface to QOMRealizeIface
and s/complete/realize/ so anyone who knows about Device.realize would
get meaning without digging in complete() implementations.

Alternative would be to behave just like Rng/Tpm do, i.e. use -object
to do late initialization in a backend user (DimmDevice.realize).
Draw back of it would be user won't get error during the first command
"object-add" and only will get error when creating DimmDevice calling
"device_add".
Paolo Bonzini Nov. 27, 2013, 3:21 p.m. UTC | #5
Il 27/11/2013 15:37, Igor Mammedov ha scritto:
> It looks like "realize" for -object / object-add implemented via
> an interface.

It does---but without unrealize and with the additional get_base_path.

> Maybe it should be renamed from QOMCommandLineIface to QOMRealizeIface
> and s/complete/realize/ so anyone who knows about Device.realize would
> get meaning without digging in complete() implementations.

There is an important difference; realize is an internal method in
Device, the external interface is the property.  So perhaps it's the
other way round; if Device implements QOMCommandLineIface you could
start creating devices with -object.

> Alternative would be to behave just like Rng/Tpm do, i.e. use -object
> to do late initialization in a backend user (DimmDevice.realize).
> Draw back of it would be user won't get error during the first command
> "object-add" and only will get error when creating DimmDevice calling
> "device_add".

That's also a possibility.  But again, maybe it's the other way round
and Rng/Tpm could enjoy better error handling if we add the interface.

Paolo
Eric Blake Nov. 27, 2013, 3:25 p.m. UTC | #6
On 11/20/2013 07:38 PM, Igor Mammedov wrote:
> 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.

Just an interface review:

> +++ b/hmp-commands.hx
> @@ -1719,3 +1719,16 @@ ETEXI
>  STEXI
>  @end table
>  ETEXI
> +
> +    {
> +        .name       = "memdev-add",

Typically, we've been naming HMP commands with underscore: memdev_add

> +++ b/qapi-schema.json
> @@ -4212,3 +4212,21 @@
>  # Since: 1.7
>  ##
>  { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
> +
> +##
> +# @memdev_add:

Whereas QMP commands have a dash: memdev-add

> +#
> +# Add a host memory backend.
> +#
> +# @id: the name of the new memory backend
> +# @size: amount of memory backend should allocate

Maybe add "in bytes" somewhere in the phrase to make it clear

> +# @type: backend type. [default: compat-ram-host-memory]

That's the default, but what other types are valid?  I'd much rather
have an enum of valid types than an open-coded string.

> +#
> +# Since: 1.8
> +#
> +# Returns: Nothing on success
> +##
> +{ 'command': 'memdev-add',

Ah, you did name the QMP command with dash, so it's the docs above that
are wrong.

> +  'data': {'id': 'str', 'size': 'size', '*type': 'str'},
> +  'gen': 'no'
> +}

> +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",

I don't think this is valid QMP; you want to be passing sizes in bytes
as an integer, not as a string that requires further parsing.
Igor Mammedov Nov. 27, 2013, 3:57 p.m. UTC | #7
On Wed, 27 Nov 2013 16:21:23 +0100
Paolo Bonzini <pbonzini@redhat.com> wrote:

> Il 27/11/2013 15:37, Igor Mammedov ha scritto:
> > It looks like "realize" for -object / object-add implemented via
> > an interface.
> 
> It does---but without unrealize and with the additional get_base_path.
> 
> > Maybe it should be renamed from QOMCommandLineIface to QOMRealizeIface
> > and s/complete/realize/ so anyone who knows about Device.realize would
> > get meaning without digging in complete() implementations.
> 
> There is an important difference; realize is an internal method in
> Device, the external interface is the property.  So perhaps it's the
> other way round; if Device implements QOMCommandLineIface you could
> start creating devices with -object.
> 
> > Alternative would be to behave just like Rng/Tpm do, i.e. use -object
> > to do late initialization in a backend user (DimmDevice.realize).
> > Draw back of it would be user won't get error during the first command
> > "object-add" and only will get error when creating DimmDevice calling
> > "device_add".
> 
> That's also a possibility.  But again, maybe it's the other way round
> and Rng/Tpm could enjoy better error handling if we add the interface.

Sure, I'll try to do as you described, it's much better to get error
earlier and from command/object that throws it than via proxy.

Thanks for suggestion, looking at netdev-add I even haven't thought
about using -object.

> Paolo
Igor Mammedov Nov. 27, 2013, 4:09 p.m. UTC | #8
On Wed, 27 Nov 2013 08:25:22 -0700
Eric Blake <eblake@redhat.com> wrote:

> On 11/20/2013 07:38 PM, Igor Mammedov wrote:
> > 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.
> 
> Just an interface review:
> 
> > +++ b/hmp-commands.hx
> > @@ -1719,3 +1719,16 @@ ETEXI
> >  STEXI
> >  @end table
> >  ETEXI
> > +
> > +    {
> > +        .name       = "memdev-add",
> 
> Typically, we've been naming HMP commands with underscore: memdev_add
there is several DASH command in HMP and I thought we stopped to
do UNDERSCORE and that DASH is preffered way now.

> 
> > +++ b/qapi-schema.json
> > @@ -4212,3 +4212,21 @@
> >  # Since: 1.7
> >  ##
> >  { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
> > +
> > +##
> > +# @memdev_add:
> 
> Whereas QMP commands have a dash: memdev-add
I'll fix it.

> 
> > +#
> > +# Add a host memory backend.
> > +#
> > +# @id: the name of the new memory backend
> > +# @size: amount of memory backend should allocate
> 
> Maybe add "in bytes" somewhere in the phrase to make it clear
Sure 
> 
> > +# @type: backend type. [default: compat-ram-host-memory]
> 
> That's the default, but what other types are valid?  I'd much rather
> have an enum of valid types than an open-coded string.
there is only one so far, I plan to add additional hugepage backend later.

> 
> > +#
> > +# Since: 1.8
> > +#
> > +# Returns: Nothing on success
> > +##
> > +{ 'command': 'memdev-add',
> 
> Ah, you did name the QMP command with dash, so it's the docs above that
> are wrong.
> 
> > +  'data': {'id': 'str', 'size': 'size', '*type': 'str'},
> > +  'gen': 'no'
> > +}
> 
> > +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",
> 
> I don't think this is valid QMP; you want to be passing sizes in bytes
> as an integer, not as a string that requires further parsing.
I'll amend it.

Thanks!
diff mbox

Patch

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 <imammedo@redhat.com>
+ *
+ * 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 <imammedo@redhat.com>
+ *
+ * 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
+@item memdev-add
+@findex 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 <imammedo@redhat.com>
+ *
+ * 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);
             }