#include "exec/ram_addr.h"
#include "exec/log.h"
+#include "qemu/pmem.h"
+
#include "migration/vmstate.h"
#include "qemu/range.h"
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)
*/
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.
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))
#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,
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.
#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)