Machine queue, 2021-02-02

Feature:
 * nvdimm: read-only file support (Stefan Hajnoczi)
 -----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCAAyFiEEWjIv1avE09usz9GqKAeTb5hNxaYFAmAZp5kUHGVoYWJrb3N0
 QHJlZGhhdC5jb20ACgkQKAeTb5hNxaYSeBAAp2lr8eKwsEju6Qpmo+OGo6rG/ORM
 KWRtwwLhHfjj2iFipURAkoepOqmtD/xd7fwItdTwi6zZxzr2eNwKqdlc9T93onzX
 P1yXCNHrz0NmmDB8LbC9Bw2NwubghAYgkuWo+fWMxNU92r+ObnvEc3Otx7P7sGvb
 Lr0yFRANgbJBLw1JTB9kMcWVDhiJ8tMPO9drhUWiMjl4kOaLNyrjUpafNzaleIaO
 GRtIXWts2Waq8wIz3XgSqWDytr075v2vWfvbR4JeAQxFArFbplR3BUuDa8G4nf6O
 nB+DERSMw+fOB2f6ZSYaAqSNwCgIrtwjhkZID0EXzNr03GPuonRnKLRZXYHvlEPT
 0qLaQeeLX+RXr6vGIjPJceQ3PyPlgDMVgVAKPS6Fvx4a3vYzAKfIj6E85QDNhuUJ
 FD37bS3iHpGxMAiuw1Ju1xJdkdzpX11h3nruo8K3sFciMK0CjkfHQKwGcw4XtKE1
 U56PEp+hKmoPe3z9CT1QmlJXlAJ+3NCAAXp6yt5yFDMqU4qxD2ns9+ts/S75o3bo
 P9pTBiaSAlUyOF1a0+0tOMny/7yz+Eb+Dw4CszZ6cCErVC/wNuJXYsF3BCT2roPN
 0d5CKwA717Jh7JmxhSlGXAW4d5mSMn8RHbFnHd5ZKFiIAh4+wnKfXrD9vB+Vq4fr
 Nja+LAk7QoURKjc=
 =TJ6/
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/ehabkost-gl/tags/machine-next-pull-request' into staging

Machine queue, 2021-02-02

Feature:
* nvdimm: read-only file support (Stefan Hajnoczi)

# gpg: Signature made Tue 02 Feb 2021 19:27:21 GMT
# gpg:                using RSA key 5A322FD5ABC4D3DBACCFD1AA2807936F984DC5A6
# gpg:                issuer "ehabkost@redhat.com"
# gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>" [full]
# Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF  D1AA 2807 936F 984D C5A6

* remotes/ehabkost-gl/tags/machine-next-pull-request:
  nvdimm: check -object memory-backend-file, readonly=on option
  hostmem-file: add readonly=on|off option
  memory: add readonly support to memory_region_init_ram_from_file()

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-02-03 09:54:21 +00:00
commit 8360ebeb4f
11 changed files with 87 additions and 25 deletions

View File

@ -29,6 +29,7 @@ struct HostMemoryBackendFile {
uint64_t align; uint64_t align;
bool discard_data; bool discard_data;
bool is_pmem; bool is_pmem;
bool readonly;
}; };
static void static void
@ -56,7 +57,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
backend->size, fb->align, backend->size, fb->align,
(backend->share ? RAM_SHARED : 0) | (backend->share ? RAM_SHARED : 0) |
(fb->is_pmem ? RAM_PMEM : 0), (fb->is_pmem ? RAM_PMEM : 0),
fb->mem_path, errp); fb->mem_path, fb->readonly, errp);
g_free(name); g_free(name);
#endif #endif
} }
@ -151,6 +152,28 @@ static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
fb->is_pmem = value; fb->is_pmem = value;
} }
static bool file_memory_backend_get_readonly(Object *obj, Error **errp)
{
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(obj);
return fb->readonly;
}
static void file_memory_backend_set_readonly(Object *obj, bool value,
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 'readonly' of %s.",
object_get_typename(obj));
return;
}
fb->readonly = value;
}
static void file_backend_unparent(Object *obj) static void file_backend_unparent(Object *obj)
{ {
HostMemoryBackend *backend = MEMORY_BACKEND(obj); HostMemoryBackend *backend = MEMORY_BACKEND(obj);
@ -182,6 +205,9 @@ file_backend_class_init(ObjectClass *oc, void *data)
NULL, NULL); NULL, NULL);
object_class_property_add_bool(oc, "pmem", object_class_property_add_bool(oc, "pmem",
file_memory_backend_get_pmem, file_memory_backend_set_pmem); file_memory_backend_get_pmem, file_memory_backend_set_pmem);
object_class_property_add_bool(oc, "readonly",
file_memory_backend_get_readonly,
file_memory_backend_set_readonly);
} }
static void file_backend_instance_finalize(Object *o) static void file_backend_instance_finalize(Object *o)

View File

@ -17,8 +17,8 @@ following command line options:
-machine pc,nvdimm -machine pc,nvdimm
-m $RAM_SIZE,slots=$N,maxmem=$MAX_SIZE -m $RAM_SIZE,slots=$N,maxmem=$MAX_SIZE
-object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE -object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE,readonly=off
-device nvdimm,id=nvdimm1,memdev=mem1 -device nvdimm,id=nvdimm1,memdev=mem1,unarmed=off
Where, Where,
@ -31,9 +31,10 @@ Where,
of normal RAM devices and vNVDIMM devices, e.g. $MAX_SIZE should be of normal RAM devices and vNVDIMM devices, e.g. $MAX_SIZE should be
>= $RAM_SIZE + $NVDIMM_SIZE here. >= $RAM_SIZE + $NVDIMM_SIZE here.
- "object memory-backend-file,id=mem1,share=on,mem-path=$PATH,size=$NVDIMM_SIZE" - "object memory-backend-file,id=mem1,share=on,mem-path=$PATH,
creates a backend storage of size $NVDIMM_SIZE on a file $PATH. All size=$NVDIMM_SIZE,readonly=off" creates a backend storage of size
accesses to the virtual NVDIMM device go to the file $PATH. $NVDIMM_SIZE on a file $PATH. All accesses to the virtual NVDIMM device go
to the file $PATH.
"share=on/off" controls the visibility of guest writes. If "share=on/off" controls the visibility of guest writes. If
"share=on", then guest writes will be applied to the backend "share=on", then guest writes will be applied to the backend
@ -42,8 +43,17 @@ Where,
"share=off", then guest writes won't be applied to the backend "share=off", then guest writes won't be applied to the backend
file and thus will be invisible to other guests. file and thus will be invisible to other guests.
- "device nvdimm,id=nvdimm1,memdev=mem1" creates a virtual NVDIMM "readonly=on/off" controls whether the file $PATH is opened read-only or
device whose storage is provided by above memory backend device. read/write (default).
- "device nvdimm,id=nvdimm1,memdev=mem1,unarmed=off" creates a read/write
virtual NVDIMM device whose storage is provided by above memory backend
device.
"unarmed" controls the ACPI NFIT NVDIMM Region Mapping Structure "NVDIMM
State Flags" Bit 3 indicating that the device is "unarmed" and cannot accept
persistent writes. Linux guest drivers set the device to read-only when this
bit is present. Set unarmed to on when the memdev has readonly=on.
Multiple vNVDIMM devices can be created if multiple pairs of "-object" Multiple vNVDIMM devices can be created if multiple pairs of "-object"
and "-device" are provided. and "-device" are provided.

View File

@ -146,6 +146,15 @@ static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp)
return; return;
} }
if (!nvdimm->unarmed && memory_region_is_rom(mr)) {
HostMemoryBackend *hostmem = dimm->hostmem;
error_setg(errp, "'unarmed' property must be off since memdev %s "
"is read-only",
object_get_canonical_path_component(OBJECT(hostmem)));
return;
}
nvdimm->nvdimm_mr = g_new(MemoryRegion, 1); nvdimm->nvdimm_mr = g_new(MemoryRegion, 1);
memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm), memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm),
"nvdimm-memory", mr, 0, pmem_size); "nvdimm-memory", mr, 0, pmem_size);

View File

@ -966,6 +966,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr,
* - RAM_PMEM: the memory is persistent memory * - RAM_PMEM: the memory is persistent memory
* Other bits are ignored now. * Other bits are ignored now.
* @path: the path in which to allocate the RAM. * @path: the path in which to allocate the RAM.
* @readonly: true to open @path for reading, false for read/write.
* @errp: pointer to Error*, to store an error if it happens. * @errp: pointer to Error*, to store an error if it happens.
* *
* Note that this function does not do anything to cause the data in the * Note that this function does not do anything to cause the data in the
@ -978,6 +979,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
uint64_t align, uint64_t align,
uint32_t ram_flags, uint32_t ram_flags,
const char *path, const char *path,
bool readonly,
Error **errp); Error **errp);
/** /**

View File

@ -110,6 +110,7 @@ long qemu_maxrampagesize(void);
* - RAM_PMEM: the backend @mem_path or @fd is persistent memory * - RAM_PMEM: the backend @mem_path or @fd is persistent memory
* Other bits are ignored. * Other bits are ignored.
* @mem_path or @fd: specify the backing file or device * @mem_path or @fd: specify the backing file or device
* @readonly: true to open @path for reading, false for read/write.
* @errp: pointer to Error*, to store an error if it happens * @errp: pointer to Error*, to store an error if it happens
* *
* Return: * Return:
@ -118,9 +119,9 @@ long qemu_maxrampagesize(void);
*/ */
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, const char *mem_path, uint32_t ram_flags, const char *mem_path,
Error **errp); bool readonly, Error **errp);
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, int fd, uint32_t ram_flags, int fd, bool readonly,
Error **errp); Error **errp);
RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,

View File

@ -14,6 +14,7 @@ size_t qemu_mempath_getpagesize(const char *mem_path);
* @size: the number of bytes to be mmaped * @size: the number of bytes to be mmaped
* @align: if not zero, specify the alignment of the starting mapping address; * @align: if not zero, specify the alignment of the starting mapping address;
* otherwise, the alignment in use will be determined by QEMU. * otherwise, the alignment in use will be determined by QEMU.
* @readonly: true for a read-only mapping, false for read/write.
* @shared: map has RAM_SHARED flag. * @shared: map has RAM_SHARED flag.
* @is_pmem: map has RAM_PMEM flag. * @is_pmem: map has RAM_PMEM flag.
* *
@ -24,6 +25,7 @@ size_t qemu_mempath_getpagesize(const char *mem_path);
void *qemu_ram_mmap(int fd, void *qemu_ram_mmap(int fd,
size_t size, size_t size,
size_t align, size_t align,
bool readonly,
bool shared, bool shared,
bool is_pmem); bool is_pmem);

View File

@ -4426,7 +4426,7 @@ SRST
they are specified. Note that the 'id' property must be set. These they are specified. Note that the 'id' property must be set. These
objects are placed in the '/objects' path. 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`` ``-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,readonly=on|off``
Creates a memory file backend object, which can be used to back Creates a memory file backend object, which can be used to back
the guest RAM with huge pages. the guest RAM with huge pages.
@ -4509,6 +4509,9 @@ SRST
4.15) and the filesystem of ``mem-path`` mounted with DAX 4.15) and the filesystem of ``mem-path`` mounted with DAX
option. option.
The ``readonly`` option specifies whether the backing file is opened
read-only or read-write (default).
``-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`` ``-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 Creates a memory backend object, which can be used to back the
guest RAM. Memory backend objects offer more control than the guest RAM. Memory backend objects offer more control than the

View File

@ -1587,15 +1587,18 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
uint64_t align, uint64_t align,
uint32_t ram_flags, uint32_t ram_flags,
const char *path, const char *path,
bool readonly,
Error **errp) Error **errp)
{ {
Error *err = NULL; Error *err = NULL;
memory_region_init(mr, owner, name, size); memory_region_init(mr, owner, name, size);
mr->ram = true; mr->ram = true;
mr->readonly = readonly;
mr->terminates = true; mr->terminates = true;
mr->destructor = memory_region_destructor_ram; mr->destructor = memory_region_destructor_ram;
mr->align = align; mr->align = align;
mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err); mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path,
readonly, &err);
if (err) { if (err) {
mr->size = int128_zero(); mr->size = int128_zero();
object_unparent(OBJECT(mr)); object_unparent(OBJECT(mr));
@ -1618,7 +1621,7 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr,
mr->destructor = memory_region_destructor_ram; mr->destructor = memory_region_destructor_ram;
mr->ram_block = qemu_ram_alloc_from_fd(size, mr, mr->ram_block = qemu_ram_alloc_from_fd(size, mr,
share ? RAM_SHARED : 0, share ? RAM_SHARED : 0,
fd, &err); fd, false, &err);
if (err) { if (err) {
mr->size = int128_zero(); mr->size = int128_zero();
object_unparent(OBJECT(mr)); object_unparent(OBJECT(mr));

View File

@ -1398,6 +1398,7 @@ static int64_t get_file_align(int fd)
static int file_ram_open(const char *path, static int file_ram_open(const char *path,
const char *region_name, const char *region_name,
bool readonly,
bool *created, bool *created,
Error **errp) Error **errp)
{ {
@ -1408,7 +1409,7 @@ static int file_ram_open(const char *path,
*created = false; *created = false;
for (;;) { for (;;) {
fd = open(path, O_RDWR); fd = open(path, readonly ? O_RDONLY : O_RDWR);
if (fd >= 0) { if (fd >= 0) {
/* @path names an existing file, use it */ /* @path names an existing file, use it */
break; break;
@ -1460,6 +1461,7 @@ static int file_ram_open(const char *path,
static void *file_ram_alloc(RAMBlock *block, static void *file_ram_alloc(RAMBlock *block,
ram_addr_t memory, ram_addr_t memory,
int fd, int fd,
bool readonly,
bool truncate, bool truncate,
Error **errp) Error **errp)
{ {
@ -1510,7 +1512,7 @@ static void *file_ram_alloc(RAMBlock *block,
perror("ftruncate"); perror("ftruncate");
} }
area = qemu_ram_mmap(fd, memory, block->mr->align, area = qemu_ram_mmap(fd, memory, block->mr->align, readonly,
block->flags & RAM_SHARED, block->flags & RAM_PMEM); block->flags & RAM_SHARED, block->flags & RAM_PMEM);
if (area == MAP_FAILED) { if (area == MAP_FAILED) {
error_setg_errno(errp, errno, error_setg_errno(errp, errno,
@ -1942,7 +1944,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
#ifdef CONFIG_POSIX #ifdef CONFIG_POSIX
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, int fd, uint32_t ram_flags, int fd, bool readonly,
Error **errp) Error **errp)
{ {
RAMBlock *new_block; RAMBlock *new_block;
@ -1996,7 +1998,8 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
new_block->used_length = size; new_block->used_length = size;
new_block->max_length = size; new_block->max_length = size;
new_block->flags = ram_flags; new_block->flags = ram_flags;
new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp); new_block->host = file_ram_alloc(new_block, size, fd, readonly,
!file_size, errp);
if (!new_block->host) { if (!new_block->host) {
g_free(new_block); g_free(new_block);
return NULL; return NULL;
@ -2015,18 +2018,19 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, const char *mem_path, uint32_t ram_flags, const char *mem_path,
Error **errp) bool readonly, Error **errp)
{ {
int fd; int fd;
bool created; bool created;
RAMBlock *block; RAMBlock *block;
fd = file_ram_open(mem_path, memory_region_name(mr), &created, errp); fd = file_ram_open(mem_path, memory_region_name(mr), readonly, &created,
errp);
if (fd < 0) { if (fd < 0) {
return NULL; return NULL;
} }
block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, errp); block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, readonly, errp);
if (!block) { if (!block) {
if (created) { if (created) {
unlink(mem_path); unlink(mem_path);

View File

@ -85,9 +85,11 @@ size_t qemu_mempath_getpagesize(const char *mem_path)
void *qemu_ram_mmap(int fd, void *qemu_ram_mmap(int fd,
size_t size, size_t size,
size_t align, size_t align,
bool readonly,
bool shared, bool shared,
bool is_pmem) bool is_pmem)
{ {
int prot;
int flags; int flags;
int map_sync_flags = 0; int map_sync_flags = 0;
int guardfd; int guardfd;
@ -146,8 +148,9 @@ void *qemu_ram_mmap(int fd,
offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr; offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr;
ptr = mmap(guardptr + offset, size, PROT_READ | PROT_WRITE, prot = PROT_READ | (readonly ? 0 : PROT_WRITE);
flags | map_sync_flags, fd, 0);
ptr = mmap(guardptr + offset, size, prot, flags | map_sync_flags, fd, 0);
if (ptr == MAP_FAILED && map_sync_flags) { if (ptr == MAP_FAILED && map_sync_flags) {
if (errno == ENOTSUP) { if (errno == ENOTSUP) {
@ -171,8 +174,7 @@ void *qemu_ram_mmap(int fd,
* if map failed with MAP_SHARED_VALIDATE | MAP_SYNC, * if map failed with MAP_SHARED_VALIDATE | MAP_SYNC,
* we will remove these flags to handle compatibility. * we will remove these flags to handle compatibility.
*/ */
ptr = mmap(guardptr + offset, size, PROT_READ | PROT_WRITE, ptr = mmap(guardptr + offset, size, prot, flags, fd, 0);
flags, fd, 0);
} }
if (ptr == MAP_FAILED) { if (ptr == MAP_FAILED) {

View File

@ -230,7 +230,7 @@ void *qemu_memalign(size_t alignment, size_t size)
void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared) void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared)
{ {
size_t align = QEMU_VMALLOC_ALIGN; size_t align = QEMU_VMALLOC_ALIGN;
void *ptr = qemu_ram_mmap(-1, size, align, shared, false); void *ptr = qemu_ram_mmap(-1, size, align, false, shared, false);
if (ptr == MAP_FAILED) { if (ptr == MAP_FAILED) {
return NULL; return NULL;