Skip to content

Commit

Permalink
backends/hostmem-file: Add "rom" property to support VM templating wi…
Browse files Browse the repository at this point in the history
…th R/O files

For now, "share=off,readonly=on" would always result in us opening the
file R/O and mmap'ing the opened file MAP_PRIVATE R/O -- effectively
turning it into ROM.

Especially for VM templating, "share=off" is a common use case. However,
that use case is impossible with files that lack write permissions,
because "share=off,readonly=on" will not give us writable RAM.

The sole user of ROM via memory-backend-file are R/O NVDIMMs, but as we
have users (Kata Containers) that rely on the existing behavior --
malicious VMs should not be able to consume COW memory for R/O NVDIMMs --
we cannot change the semantics of "share=off,readonly=on"

So let's add a new "rom" property with on/off/auto values. "auto" is
the default and what most people will use: for historical reasons, to not
change the old semantics, it defaults to the value of the "readonly"
property.

For VM templating, one can now use:
    -object memory-backend-file,share=off,readonly=on,rom=off,...

But we'll disallow:
    -object memory-backend-file,share=on,readonly=on,rom=off,...
because we would otherwise get an error when trying to mmap the R/O file
shared and writable. An explicit error message is cleaner.

We will also disallow for now:
    -object memory-backend-file,share=off,readonly=off,rom=on,...
    -object memory-backend-file,share=on,readonly=off,rom=on,...
It's not harmful, but also not really required for now.

Alternatives that were abandoned:
* Make "unarmed=on" for the NVDIMM set the memory region container
  readonly. We would still see a change of ROM->RAM and possibly run
  into memslot limits with vhost-user. Further, there might be use cases
  for "unarmed=on" that should still allow writing to that memory
  (temporary files, system RAM, ...).
* Add a new "readonly=on/off/auto" parameter for NVDIMMs. Similar issues
  as with "unarmed=on".
* Make "readonly" consume "on/off/file" instead of being a 'bool' type.
  This would slightly changes the behavior of the "readonly" parameter:
  values like true/false (as accepted by a 'bool'type) would no longer be
  accepted.

Message-ID: <20230906120503.359863-4-david@redhat.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
  • Loading branch information
davidhildenbrand committed Sep 19, 2023
1 parent 5c52a21 commit e92666b
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 3 deletions.
59 changes: 58 additions & 1 deletion backends/hostmem-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "sysemu/hostmem.h"
#include "qom/object_interfaces.h"
#include "qom/object.h"
#include "qapi/visitor.h"
#include "qapi/qapi-visit-common.h"

OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendFile, MEMORY_BACKEND_FILE)

Expand All @@ -31,6 +33,7 @@ struct HostMemoryBackendFile {
bool discard_data;
bool is_pmem;
bool readonly;
OnOffAuto rom;
};

static void
Expand All @@ -53,9 +56,33 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
return;
}

switch (fb->rom) {
case ON_OFF_AUTO_AUTO:
/* Traditionally, opening the file readonly always resulted in ROM. */
fb->rom = fb->readonly ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
break;
case ON_OFF_AUTO_ON:
if (!fb->readonly) {
error_setg(errp, "property 'rom' = 'on' is not supported with"
" 'readonly' = 'off'");
return;
}
break;
case ON_OFF_AUTO_OFF:
if (fb->readonly && backend->share) {
error_setg(errp, "property 'rom' = 'off' is incompatible with"
" 'readonly' = 'on' and 'share' = 'on'");
return;
}
break;
default:
assert(false);
}

name = host_memory_backend_get_name(backend);
ram_flags = backend->share ? RAM_SHARED : 0;
ram_flags |= fb->readonly ? RAM_READONLY | RAM_READONLY_FD : 0;
ram_flags |= fb->readonly ? RAM_READONLY_FD : 0;
ram_flags |= fb->rom == ON_OFF_AUTO_ON ? RAM_READONLY : 0;
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
ram_flags |= fb->is_pmem ? RAM_PMEM : 0;
ram_flags |= RAM_NAMED_FILE;
Expand Down Expand Up @@ -201,6 +228,32 @@ static void file_memory_backend_set_readonly(Object *obj, bool value,
fb->readonly = value;
}

static void file_memory_backend_get_rom(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj);
OnOffAuto rom = fb->rom;

visit_type_OnOffAuto(v, name, &rom, errp);
}

static void file_memory_backend_set_rom(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj);

if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property '%s' of %s.", name,
object_get_typename(obj));
return;
}

visit_type_OnOffAuto(v, name, &fb->rom, errp);
}

static void file_backend_unparent(Object *obj)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
Expand Down Expand Up @@ -243,6 +296,10 @@ file_backend_class_init(ObjectClass *oc, void *data)
object_class_property_add_bool(oc, "readonly",
file_memory_backend_get_readonly,
file_memory_backend_set_readonly);
object_class_property_add(oc, "rom", "OnOffAuto",
file_memory_backend_get_rom, file_memory_backend_set_rom, NULL, NULL);
object_class_property_set_description(oc, "rom",
"Whether to create Read Only Memory (ROM)");
}

static void file_backend_instance_finalize(Object *o)
Expand Down
17 changes: 16 additions & 1 deletion qapi/qom.json
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,20 @@
# @readonly: if true, the backing file is opened read-only; if false,
# it is opened read-write. (default: false)
#
# @rom: whether to create Read Only Memory (ROM) that cannot be modified
# by the VM. Any write attempts to such ROM will be denied. Most
# use cases want writable RAM instead of ROM. However, selected use
# cases, like R/O NVDIMMs, can benefit from ROM. If set to 'on',
# create ROM; if set to 'off', create writable RAM; if set to
# 'auto', the value of the @readonly property is used. This
# property is primarily helpful when we want to have proper RAM in
# configurations that would traditionally create ROM before this
# property was introduced: VM templating, where we want to open a
# file readonly (@readonly set to true) and mark the memory to be
# private for QEMU (@share set to false). For this use case, we need
# writable RAM instead of ROM, and want to set this property to 'off'.
# (default: auto, since 8.2)
#
# Since: 2.1
##
{ 'struct': 'MemoryBackendFileProperties',
Expand All @@ -677,7 +691,8 @@
'*discard-data': 'bool',
'mem-path': 'str',
'*pmem': { 'type': 'bool', 'if': 'CONFIG_LIBPMEM' },
'*readonly': 'bool' } }
'*readonly': 'bool',
'*rom': 'OnOffAuto' } }

##
# @MemoryBackendMemfdProperties:
Expand Down
16 changes: 15 additions & 1 deletion qemu-options.hx
Original file line number Diff line number Diff line change
Expand Up @@ -4995,7 +4995,7 @@ SRST
they are specified. Note that the 'id' property must be set. These
objects are placed in the '/objects' path.

``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,offset=offset,readonly=on|off``
``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,offset=offset,readonly=on|off,rom=on|off|auto``
Creates a memory file backend object, which can be used to back
the guest RAM with huge pages.

Expand Down Expand Up @@ -5085,6 +5085,20 @@ SRST
The ``readonly`` option specifies whether the backing file is opened
read-only or read-write (default).

The ``rom`` option specifies whether to create Read Only Memory
(ROM) that cannot be modified by the VM. Any write attempts to such
ROM will be denied. Most use cases want proper RAM instead of ROM.
However, selected use cases, like R/O NVDIMMs, can benefit from
ROM. If set to ``on``, create ROM; if set to ``off``, create
writable RAM; if set to ``auto`` (default), the value of the
``readonly`` option is used. This option is primarily helpful when
we want to have writable RAM in configurations that would
traditionally create ROM before the ``rom`` option was introduced:
VM templating, where we want to open a file readonly
(``readonly=on``) and mark the memory to be private for QEMU
(``share=off``). For this use case, we need writable RAM instead
of ROM, and want to also set ``rom=off``.

``-object memory-backend-ram,id=id,merge=on|off,dump=on|off,share=on|off,prealloc=on|off,size=size,host-nodes=host-nodes,policy=default|preferred|bind|interleave``
Creates a memory backend object, which can be used to back the
guest RAM. Memory backend objects offer more control than the
Expand Down

0 comments on commit e92666b

Please sign in to comment.