提交 61c490e2 编写于 作者: B Beata Michalska 提交者: Peter Maydell

Memory: Enable writeback for given memory region

Add an option to trigger memory writeback to sync given memory region
with the corresponding backing store, case one is available.
This extends the support for persistent memory, allowing syncing on-demand.
Signed-off-by: NBeata Michalska <beata.michalska@linaro.org>
Reviewed-by: NRichard Henderson <richard.henderson@linaro.org>
Message-id: 20191121000843.24844-3-beata.michalska@linaro.org
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
上级 9e70492b
......@@ -65,6 +65,8 @@
#include "exec/ram_addr.h"
#include "exec/log.h"
#include "qemu/pmem.h"
#include "migration/vmstate.h"
#include "qemu/range.h"
......@@ -2156,6 +2158,40 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp)
return 0;
}
/*
* Trigger sync on the given ram block for range [start, start + length]
* with the backing store if one is available.
* Otherwise no-op.
* @Note: this is supposed to be a synchronous op.
*/
void qemu_ram_writeback(RAMBlock *block, ram_addr_t start, ram_addr_t length)
{
void *addr = ramblock_ptr(block, start);
/* The requested range should fit in within the block range */
g_assert((start + length) <= block->used_length);
#ifdef CONFIG_LIBPMEM
/* The lack of support for pmem should not block the sync */
if (ramblock_is_pmem(block)) {
pmem_persist(addr, length);
return;
}
#endif
if (block->fd >= 0) {
/**
* Case there is no support for PMEM or the memory has not been
* specified as persistent (or is not one) - use the msync.
* Less optimal but still achieves the same goal
*/
if (qemu_msync(addr, length, block->fd)) {
warn_report("%s: failed to sync memory range: start: "
RAM_ADDR_FMT " length: " RAM_ADDR_FMT,
__func__, start, length);
}
}
}
/* Called with ram_list.mutex held */
static void dirty_memory_extend(ram_addr_t old_ram_size,
ram_addr_t new_ram_size)
......
......@@ -1265,6 +1265,12 @@ void *memory_region_get_ram_ptr(MemoryRegion *mr);
*/
void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize,
Error **errp);
/**
* memory_region_do_writeback: Trigger writeback for selected address range
* [addr, addr + size]
*
*/
void memory_region_do_writeback(MemoryRegion *mr, hwaddr addr, hwaddr size);
/**
* memory_region_set_log: Turn dirty logging on or off for a region.
......
......@@ -174,6 +174,14 @@ void qemu_ram_free(RAMBlock *block);
int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp);
void qemu_ram_writeback(RAMBlock *block, ram_addr_t start, ram_addr_t length);
/* Clear whole block of mem */
static inline void qemu_ram_block_writeback(RAMBlock *block)
{
qemu_ram_writeback(block, 0, block->used_length);
}
#define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1)
#define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE))
......
......@@ -130,6 +130,7 @@ const char *qemu_strchrnul(const char *s, int c);
#endif
time_t mktimegm(struct tm *tm);
int qemu_fdatasync(int fd);
int qemu_msync(void *addr, size_t length, int fd);
int fcntl_setfl(int fd, int flag);
int qemu_parse_fd(const char *param);
int qemu_strtoi(const char *nptr, const char **endptr, int base,
......
......@@ -2207,6 +2207,18 @@ void memory_region_ram_resize(MemoryRegion *mr, ram_addr_t newsize, Error **errp
qemu_ram_resize(mr->ram_block, newsize, errp);
}
void memory_region_do_writeback(MemoryRegion *mr, hwaddr addr, hwaddr size)
{
/*
* Might be extended case needed to cover
* different types of memory regions
*/
if (mr->ram_block && mr->dirty_log_mask) {
qemu_ram_writeback(mr->ram_block, addr, size);
}
}
/*
* Call proper memory listeners about the change on the newly
* added/removed CoalescedMemoryRange.
......
......@@ -164,6 +164,44 @@ int qemu_fdatasync(int fd)
#endif
}
/**
* Sync changes made to the memory mapped file back to the backing
* storage. For POSIX compliant systems this will fallback
* to regular msync call. Otherwise it will trigger whole file sync
* (including the metadata case there is no support to skip that otherwise)
*
* @addr - start of the memory area to be synced
* @length - length of the are to be synced
* @fd - file descriptor for the file to be synced
* (mandatory only for POSIX non-compliant systems)
*/
int qemu_msync(void *addr, size_t length, int fd)
{
#ifdef CONFIG_POSIX
size_t align_mask = ~(qemu_real_host_page_size - 1);
/**
* There are no strict reqs as per the length of mapping
* to be synced. Still the length needs to follow the address
* alignment changes. Additionally - round the size to the multiple
* of PAGE_SIZE
*/
length += ((uintptr_t)addr & (qemu_real_host_page_size - 1));
length = (length + ~align_mask) & align_mask;
addr = (void *)((uintptr_t)addr & align_mask);
return msync(addr, length, MS_SYNC);
#else /* CONFIG_POSIX */
/**
* Perform the sync based on the file descriptor
* The sync range will most probably be wider than the one
* requested - but it will still get the job done
*/
return qemu_fdatasync(fd);
#endif /* CONFIG_POSIX */
}
#ifndef _WIN32
/* Sets a specific flag */
int fcntl_setfl(int fd, int flag)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册