--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+/* Copyright 2020, Intel Corporation */
+
+/*
+ * vm_reservation.c -- implementation of virtual memory allocation API
+ */
+
+#include "alloc.h"
+#include "map.h"
+#include "pmem2_utils.h"
+#include "ravl_interval.h"
+#include "sys_util.h"
+#include "vm_reservation.h"
+
+#ifdef _WIN32
+#include <Windows.h>
+#endif
+
+struct pmem2_vm_reservation {
+ struct ravl_interval *itree;
+ void *addr;
+ size_t size;
+ os_rwlock_t lock;
+};
+
+int vm_reservation_reserve_memory(void *addr, size_t size, void **raddr,
+ size_t *rsize);
+int vm_reservation_release_memory(void *addr, size_t size);
+struct ravl_interval *vm_reservation_get_interval_tree(
+ struct pmem2_vm_reservation *rsv);
+
+/*
+ * pmem2_vm_reservation_get_address -- get reservation address
+ */
+void *
+pmem2_vm_reservation_get_address(struct pmem2_vm_reservation *rsv)
+{
+ LOG(3, "reservation %p", rsv);
+ /* we do not need to clear err because this function cannot fail */
+
+ return rsv->addr;
+}
+
+/*
+ * pmem2_vm_reservation_get_size -- get reservation size
+ */
+size_t
+pmem2_vm_reservation_get_size(struct pmem2_vm_reservation *rsv)
+{
+ LOG(3, "reservation %p", rsv);
+ /* we do not need to clear err because this function cannot fail */
+
+ return rsv->size;
+}
+
+/*
+ * mapping_min - return min boundary for mapping
+ */
+static size_t
+mapping_min(void *addr)
+{
+ struct pmem2_map *map = (struct pmem2_map *)addr;
+ return (size_t)map->addr;
+}
+
+/*
+ * mapping_max - return max boundary for mapping
+ */
+static size_t
+mapping_max(void *addr)
+{
+ struct pmem2_map *map = (struct pmem2_map *)addr;
+ return (size_t)map->addr + map->content_length;
+}
+
+/*
+ * pmem2_vm_reservation_init - initialize the reservation structure
+ */
+static int
+vm_reservation_init(struct pmem2_vm_reservation *rsv)
+{
+ util_rwlock_init(&rsv->lock);
+
+ rsv->itree = ravl_interval_new(mapping_min, mapping_max);
+ if (!rsv->itree)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * pmem2_vm_reservation_fini - finalize the reservation structure
+ */
+static void
+vm_reservation_fini(struct pmem2_vm_reservation *rsv)
+{
+ ravl_interval_delete(rsv->itree);
+ util_rwlock_destroy(&rsv->lock);
+}
+
+/*
+ * pmem2_vm_reservation_new -- creates new virtual memory reservation
+ */
+int
+pmem2_vm_reservation_new(struct pmem2_vm_reservation **rsv_ptr,
+ void *addr, size_t size)
+{
+ PMEM2_ERR_CLR();
+ *rsv_ptr = NULL;
+
+ /*
+ * base address has to be aligned to the allocation granularity
+ * on Windows, and to page size otherwise
+ */
+ if (addr && (unsigned long long)addr % Mmap_align) {
+ ERR("address %p is not a multiple of 0x%llx", addr,
+ Mmap_align);
+ return PMEM2_E_ADDRESS_UNALIGNED;
+ }
+
+ /* the size must always be a multiple of the page size */
+ if (size % Pagesize) {
+ ERR("reservation size %zu is not a multiple of %llu",
+ size, Pagesize);
+ return PMEM2_E_LENGTH_UNALIGNED;
+ }
+
+ int ret;
+ struct pmem2_vm_reservation *rsv = pmem2_malloc(
+ sizeof(struct pmem2_vm_reservation), &ret);
+ if (ret)
+ return ret;
+
+ /* initialize the ravl interval tree */
+ ret = vm_reservation_init(rsv);
+ if (ret)
+ goto err_rsv_init;
+
+ void *raddr = NULL;
+ size_t rsize = 0;
+ ret = vm_reservation_reserve_memory(addr, size, &raddr, &rsize);
+ if (ret)
+ goto err_reserve;
+
+ rsv->addr = raddr;
+ rsv->size = rsize;
+
+ *rsv_ptr = rsv;
+
+ return 0;
+
+err_reserve:
+ vm_reservation_fini(rsv);
+err_rsv_init:
+ Free(rsv);
+ return ret;
+}
+
+/*
+ * pmem2_vm_reservation_delete -- deletes reservation bound to
+ * the pmem2_vm_reservation structure
+ */
+int
+pmem2_vm_reservation_delete(struct pmem2_vm_reservation **rsv_ptr)
+{
+ PMEM2_ERR_CLR();
+
+ struct pmem2_vm_reservation *rsv = *rsv_ptr;
+
+ /* check if reservation contains any mapping */
+ if (vm_reservation_map_find(rsv, 0, rsv->size)) {
+ ERR("vm reservation %p isn't empty", rsv);
+ return PMEM2_E_VM_RESERVATION_NOT_EMPTY;
+ }
+
+ int ret = vm_reservation_release_memory(rsv->addr, rsv->size);
+ if (ret)
+ return ret;
+
+ vm_reservation_fini(rsv);
+ Free(rsv);
+
+ return 0;
+}
+
+/*
+ * vm_reservation_map_register_release -- register mapping in the mappings tree
+ * of reservation structure and release previously acquired lock regardless
+ * of the success or failure of the function.
+ */
+int
+vm_reservation_map_register_release(struct pmem2_vm_reservation *rsv,
+ struct pmem2_map *map)
+{
+ int ret = ravl_interval_insert(rsv->itree, map);
+ util_rwlock_unlock(&rsv->lock);
+
+ if (ret == -EEXIST) {
+ ERR(
+ "mapping at the given region of the reservation already exist");
+ return PMEM2_E_MAPPING_EXISTS;
+ }
+
+ return ret;
+}
+
+/*
+ * vm_reservation_map_unregister_release -- unregister mapping from the mapping
+ * tree of reservation structure and release previously acquired lock regardless
+ * of the success or failure of the function.
+ */
+int
+vm_reservation_map_unregister_release(struct pmem2_vm_reservation *rsv,
+ struct pmem2_map *map)
+{
+ int ret = 0;
+ struct ravl_interval_node *node;
+
+ node = ravl_interval_find_equal(rsv->itree, map);
+ if (node) {
+ ret = ravl_interval_remove(rsv->itree, node);
+ } else {
+ ERR("Cannot find mapping %p in the reservation %p",
+ map, rsv);
+ ret = PMEM2_E_MAPPING_NOT_FOUND;
+ }
+ util_rwlock_unlock(&rsv->lock);
+
+ return ret;
+}
+
+/*
+ * vm_reservation_map_find -- find the earliest mapping overlapping
+ * with (addr, addr+size) range
+ */
+struct pmem2_map *
+vm_reservation_map_find(struct pmem2_vm_reservation *rsv,
+ size_t reserv_offset, size_t len)
+{
+ struct pmem2_map map;
+ map.addr = (char *)rsv->addr + reserv_offset;
+ map.content_length = len;
+
+ struct ravl_interval_node *node;
+
+ node = ravl_interval_find(rsv->itree, &map);
+
+ if (!node)
+ return NULL;
+
+ return (struct pmem2_map *)ravl_interval_data(node);
+}
+
+/*
+ * vm_reservation_map_find_acquire -- find the earliest mapping overlapping
+ * with (addr, addr+size) range. This function acquires a lock and keeps it
+ * until next release operation.
+ */
+struct pmem2_map *
+vm_reservation_map_find_acquire(struct pmem2_vm_reservation *rsv,
+ size_t reserv_offset, size_t len)
+{
+ struct pmem2_map map;
+ map.addr = (char *)rsv->addr + reserv_offset;
+ map.content_length = len;
+
+ struct ravl_interval_node *node;
+
+ util_rwlock_wrlock(&rsv->lock);
+ node = ravl_interval_find(rsv->itree, &map);
+
+ if (!node)
+ return NULL;
+
+ return (struct pmem2_map *)ravl_interval_data(node);
+}
+
+/*
+ * vm_reservation_release -- releases previously acquired lock
+ */
+void
+vm_reservation_release(struct pmem2_vm_reservation *rsv)
+{
+ util_rwlock_unlock(&rsv->lock);
+}
+
+/*
+ * vm_reservation_get_interval_tree -- get interval tree
+ */
+struct ravl_interval *
+vm_reservation_get_interval_tree(struct pmem2_vm_reservation *rsv)
+{
+ return rsv->itree;
+}