#include <sys/types.h>
#include <stdbool.h>
#include <time.h>
+#include <unistd.h>
#include "errno.h"
+#include "common/gen_aux_map.h"
#include "common/gen_clflush.h"
#include "dev/gen_debug.h"
#include "common/gen_gem.h"
#include "dev/gen_device_info.h"
#include "main/macros.h"
+#include "os/os_mman.h"
#include "util/debug.h"
#include "util/macros.h"
#include "util/hash_table.h"
};
struct iris_bufmgr {
+ /**
+ * List into the list of bufmgr.
+ */
+ struct list_head link;
+
+ uint32_t refcount;
+
int fd;
mtx_t lock;
struct util_vma_heap vma_allocator[IRIS_MEMZONE_COUNT];
bool has_llc:1;
+ bool has_mmap_offset:1;
bool bo_reuse:1;
+
+ struct gen_aux_map_context *aux_map_ctx;
+};
+
+static mtx_t global_bufmgr_list_mutex = _MTX_INITIALIZER_NP;
+static struct list_head global_bufmgr_list = {
+ .next = &global_bufmgr_list,
+ .prev = &global_bufmgr_list,
};
static int bo_set_tiling_internal(struct iris_bo *bo, uint32_t tiling_mode,
static void bo_free(struct iris_bo *bo);
-static uint64_t vma_alloc(struct iris_bufmgr *bufmgr,
- enum iris_memory_zone memzone,
- uint64_t size, uint64_t alignment);
-
-static uint32_t
-key_hash_uint(const void *key)
-{
- return _mesa_hash_data(key, 4);
-}
-
-static bool
-key_uint_equal(const void *a, const void *b)
-{
- return *((unsigned *) a) == *((unsigned *) b);
-}
-
static struct iris_bo *
find_and_ref_external_bo(struct hash_table *ht, unsigned int key)
{
if (!bo)
return NULL;
+ if (bo->aux_map_address) {
+ /* This buffer was associated with an aux-buffer range. We make sure
+ * that buffers are not reused from the cache while the buffer is (busy)
+ * being used by an executing batch. Since we are here, the buffer is no
+ * longer being used by a batch and the buffer was deleted (in order to
+ * end up in the cache). Therefore its old aux-buffer range can be
+ * removed from the aux-map.
+ */
+ if (bo->bufmgr->aux_map_ctx)
+ gen_aux_map_unmap_range(bo->bufmgr->aux_map_ctx, bo->gtt_offset,
+ bo->size);
+ bo->aux_map_address = 0;
+ }
+
/* If the cached BO isn't in the right memory zone, or the alignment
* isn't sufficient, free the old memory and assign it a new address.
*/
p_atomic_set(&bo->refcount, 1);
bo->size = open_arg.size;
- bo->gtt_offset = 0;
bo->bufmgr = bufmgr;
bo->gem_handle = open_arg.handle;
bo->name = name;
bo->gem_handle, bo->name, strerror(errno));
}
+ if (bo->aux_map_address && bo->bufmgr->aux_map_ctx) {
+ gen_aux_map_unmap_range(bo->bufmgr->aux_map_ctx, bo->gtt_offset,
+ bo->size);
+ }
+
/* Return the VMA for reuse */
vma_free(bo->bufmgr, bo->gtt_offset, bo->size);
if (bo->map_cpu && !bo->userptr) {
VG_NOACCESS(bo->map_cpu, bo->size);
- munmap(bo->map_cpu, bo->size);
+ os_munmap(bo->map_cpu, bo->size);
}
if (bo->map_wc) {
VG_NOACCESS(bo->map_wc, bo->size);
- munmap(bo->map_wc, bo->size);
+ os_munmap(bo->map_wc, bo->size);
}
if (bo->map_gtt) {
VG_NOACCESS(bo->map_gtt, bo->size);
- munmap(bo->map_gtt, bo->size);
+ os_munmap(bo->map_gtt, bo->size);
}
if (bo->idle) {
}
static void *
-iris_bo_map_cpu(struct pipe_debug_callback *dbg,
- struct iris_bo *bo, unsigned flags)
+iris_bo_gem_mmap_legacy(struct pipe_debug_callback *dbg,
+ struct iris_bo *bo, bool wc)
{
struct iris_bufmgr *bufmgr = bo->bufmgr;
+ struct drm_i915_gem_mmap mmap_arg = {
+ .handle = bo->gem_handle,
+ .size = bo->size,
+ .flags = wc ? I915_MMAP_WC : 0,
+ };
+
+ int ret = gen_ioctl(bufmgr->fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);
+ if (ret != 0) {
+ DBG("%s:%d: Error mapping buffer %d (%s): %s .\n",
+ __FILE__, __LINE__, bo->gem_handle, bo->name, strerror(errno));
+ return NULL;
+ }
+ void *map = (void *) (uintptr_t) mmap_arg.addr_ptr;
+
+ return map;
+}
+
+static void *
+iris_bo_gem_mmap_offset(struct pipe_debug_callback *dbg, struct iris_bo *bo,
+ bool wc)
+{
+ struct iris_bufmgr *bufmgr = bo->bufmgr;
+
+ struct drm_i915_gem_mmap_offset mmap_arg = {
+ .handle = bo->gem_handle,
+ .flags = wc ? I915_MMAP_OFFSET_WC : I915_MMAP_OFFSET_WB,
+ };
+
+ /* Get the fake offset back */
+ int ret = gen_ioctl(bufmgr->fd, DRM_IOCTL_I915_GEM_MMAP_OFFSET, &mmap_arg);
+ if (ret != 0) {
+ DBG("%s:%d: Error preparing buffer %d (%s): %s .\n",
+ __FILE__, __LINE__, bo->gem_handle, bo->name, strerror(errno));
+ return NULL;
+ }
+
+ /* And map it */
+ void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ bufmgr->fd, mmap_arg.offset);
+ if (map == MAP_FAILED) {
+ DBG("%s:%d: Error mapping buffer %d (%s): %s .\n",
+ __FILE__, __LINE__, bo->gem_handle, bo->name, strerror(errno));
+ return NULL;
+ }
+
+ return map;
+}
+
+static void *
+iris_bo_gem_mmap(struct pipe_debug_callback *dbg, struct iris_bo *bo, bool wc)
+{
+ struct iris_bufmgr *bufmgr = bo->bufmgr;
+
+ if (bufmgr->has_mmap_offset)
+ return iris_bo_gem_mmap_offset(dbg, bo, wc);
+ else
+ return iris_bo_gem_mmap_legacy(dbg, bo, wc);
+}
+
+static void *
+iris_bo_map_cpu(struct pipe_debug_callback *dbg,
+ struct iris_bo *bo, unsigned flags)
+{
/* We disallow CPU maps for writing to non-coherent buffers, as the
* CPU map can become invalidated when a batch is flushed out, which
* can happen at unpredictable times. You should use WC maps instead.
if (!bo->map_cpu) {
DBG("iris_bo_map_cpu: %d (%s)\n", bo->gem_handle, bo->name);
-
- struct drm_i915_gem_mmap mmap_arg = {
- .handle = bo->gem_handle,
- .size = bo->size,
- };
- int ret = gen_ioctl(bufmgr->fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);
- if (ret != 0) {
- DBG("%s:%d: Error mapping buffer %d (%s): %s .\n",
- __FILE__, __LINE__, bo->gem_handle, bo->name, strerror(errno));
+ void *map = iris_bo_gem_mmap(dbg, bo, false);
+ if (!map) {
return NULL;
}
- void *map = (void *) (uintptr_t) mmap_arg.addr_ptr;
+
VG_DEFINED(map, bo->size);
if (p_atomic_cmpxchg(&bo->map_cpu, NULL, map)) {
VG_NOACCESS(map, bo->size);
- munmap(map, bo->size);
+ os_munmap(map, bo->size);
}
}
assert(bo->map_cpu);
iris_bo_map_wc(struct pipe_debug_callback *dbg,
struct iris_bo *bo, unsigned flags)
{
- struct iris_bufmgr *bufmgr = bo->bufmgr;
-
if (!bo->map_wc) {
DBG("iris_bo_map_wc: %d (%s)\n", bo->gem_handle, bo->name);
-
- struct drm_i915_gem_mmap mmap_arg = {
- .handle = bo->gem_handle,
- .size = bo->size,
- .flags = I915_MMAP_WC,
- };
- int ret = gen_ioctl(bufmgr->fd, DRM_IOCTL_I915_GEM_MMAP, &mmap_arg);
- if (ret != 0) {
- DBG("%s:%d: Error mapping buffer %d (%s): %s .\n",
- __FILE__, __LINE__, bo->gem_handle, bo->name, strerror(errno));
+ void *map = iris_bo_gem_mmap(dbg, bo, true);
+ if (!map) {
return NULL;
}
- void *map = (void *) (uintptr_t) mmap_arg.addr_ptr;
VG_DEFINED(map, bo->size);
if (p_atomic_cmpxchg(&bo->map_wc, NULL, map)) {
VG_NOACCESS(map, bo->size);
- munmap(map, bo->size);
+ os_munmap(map, bo->size);
}
}
assert(bo->map_wc);
}
/* and mmap it. */
- void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE,
- MAP_SHARED, bufmgr->fd, mmap_arg.offset);
+ void *map = os_mmap(0, bo->size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, bufmgr->fd, mmap_arg.offset);
if (map == MAP_FAILED) {
DBG("%s:%d: Error mapping buffer %d (%s): %s .\n",
__FILE__, __LINE__, bo->gem_handle, bo->name, strerror(errno));
if (p_atomic_cmpxchg(&bo->map_gtt, NULL, map)) {
VG_NOACCESS(map, bo->size);
- munmap(map, bo->size);
+ os_munmap(map, bo->size);
}
}
assert(bo->map_gtt);
return ret;
}
-void
+static void
iris_bufmgr_destroy(struct iris_bufmgr *bufmgr)
{
+ /* Free aux-map buffers */
+ gen_aux_map_finish(bufmgr->aux_map_ctx);
+
+ /* bufmgr will no longer try to free VMA entries in the aux-map */
+ bufmgr->aux_map_ctx = NULL;
+
mtx_destroy(&bufmgr->lock);
/* Free any cached buffer objects we were going to reuse */
util_vma_heap_finish(&bufmgr->vma_allocator[z]);
}
+ close(bufmgr->fd);
+
free(bufmgr);
}
}
struct iris_bo *
-iris_bo_import_dmabuf(struct iris_bufmgr *bufmgr, int prime_fd)
+iris_bo_import_dmabuf(struct iris_bufmgr *bufmgr, int prime_fd,
+ uint32_t tiling, uint32_t stride)
{
uint32_t handle;
struct iris_bo *bo;
if (gen_ioctl(bufmgr->fd, DRM_IOCTL_I915_GEM_GET_TILING, &get_tiling))
goto err;
- bo->tiling_mode = get_tiling.tiling_mode;
- bo->swizzle_mode = get_tiling.swizzle_mode;
- /* XXX stride is unknown */
+ if (get_tiling.tiling_mode == tiling || tiling > I915_TILING_LAST) {
+ bo->tiling_mode = get_tiling.tiling_mode;
+ bo->swizzle_mode = get_tiling.swizzle_mode;
+ /* XXX stride is unknown */
+ } else {
+ if (bo_set_tiling_internal(bo, tiling, stride)) {
+ goto err;
+ }
+ }
out:
mtx_unlock(&bufmgr->lock);
{
if (!bo->external) {
_mesa_hash_table_insert(bo->bufmgr->handle_table, &bo->gem_handle, bo);
+ /* If a BO is going to be used externally, it could be sent to the
+ * display HW. So make sure our CPU mappings don't assume cache
+ * coherency since display is outside that cache.
+ */
+ bo->cache_coherent = false;
bo->external = true;
bo->reusable = false;
}
}
-static void
+void
iris_bo_make_external(struct iris_bo *bo)
{
struct iris_bufmgr *bufmgr = bo->bufmgr;
return 0;
}
+static struct gen_buffer *
+gen_aux_map_buffer_alloc(void *driver_ctx, uint32_t size)
+{
+ struct gen_buffer *buf = malloc(sizeof(struct gen_buffer));
+ if (!buf)
+ return NULL;
+
+ struct iris_bufmgr *bufmgr = (struct iris_bufmgr *)driver_ctx;
+
+ struct iris_bo *bo =
+ iris_bo_alloc_tiled(bufmgr, "aux-map", size, 64 * 1024,
+ IRIS_MEMZONE_OTHER, I915_TILING_NONE, 0, 0);
+
+ buf->driver_bo = bo;
+ buf->gpu = bo->gtt_offset;
+ buf->gpu_end = buf->gpu + bo->size;
+ buf->map = iris_bo_map(NULL, bo, MAP_WRITE | MAP_RAW);
+ return buf;
+}
+
+static void
+gen_aux_map_buffer_free(void *driver_ctx, struct gen_buffer *buffer)
+{
+ iris_bo_unreference((struct iris_bo*)buffer->driver_bo);
+ free(buffer);
+}
+
+static struct gen_mapped_pinned_buffer_alloc aux_map_allocator = {
+ .alloc = gen_aux_map_buffer_alloc,
+ .free = gen_aux_map_buffer_free,
+};
+
+static int
+gem_param(int fd, int name)
+{
+ int v = -1; /* No param uses (yet) the sign bit, reserve it for errors */
+
+ struct drm_i915_getparam gp = { .param = name, .value = &v };
+ if (gen_ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+ return -1;
+
+ return v;
+}
+
/**
* Initializes the GEM buffer manager, which uses the kernel to allocate, map,
* and manage map buffer objections.
*
* \param fd File descriptor of the opened DRM device.
*/
-struct iris_bufmgr *
-iris_bufmgr_init(struct gen_device_info *devinfo, int fd, bool bo_reuse)
+static struct iris_bufmgr *
+iris_bufmgr_create(struct gen_device_info *devinfo, int fd, bool bo_reuse)
{
uint64_t gtt_size = iris_gtt_size(fd);
if (gtt_size <= IRIS_MEMZONE_OTHER_START)
* Don't do this! Ensure that each library/bufmgr has its own device
* fd so that its namespace does not clash with another.
*/
- bufmgr->fd = fd;
+ bufmgr->fd = dup(fd);
+
+ p_atomic_set(&bufmgr->refcount, 1);
if (mtx_init(&bufmgr->lock, mtx_plain) != 0) {
+ close(bufmgr->fd);
free(bufmgr);
return NULL;
}
bufmgr->has_llc = devinfo->has_llc;
bufmgr->bo_reuse = bo_reuse;
+ bufmgr->has_mmap_offset = gem_param(fd, I915_PARAM_MMAP_GTT_VERSION) >= 4;
STATIC_ASSERT(IRIS_MEMZONE_SHADER_START == 0ull);
const uint64_t _4GB = 1ull << 32;
+ const uint64_t _2GB = 1ul << 31;
/* The STATE_BASE_ADDRESS size field can only hold 1 page shy of 4GB */
const uint64_t _4GB_minus_1 = _4GB - PAGE_SIZE;
util_vma_heap_init(&bufmgr->vma_allocator[IRIS_MEMZONE_SURFACE],
IRIS_MEMZONE_SURFACE_START,
_4GB_minus_1 - IRIS_MAX_BINDERS * IRIS_BINDER_SIZE);
+ /* TODO: Why does limiting to 2GB help some state items on gen12?
+ * - CC Viewport Pointer
+ * - Blend State Pointer
+ * - Color Calc State Pointer
+ */
+ const uint64_t dynamic_pool_size =
+ (devinfo->gen >= 12 ? _2GB : _4GB_minus_1) - IRIS_BORDER_COLOR_POOL_SIZE;
util_vma_heap_init(&bufmgr->vma_allocator[IRIS_MEMZONE_DYNAMIC],
IRIS_MEMZONE_DYNAMIC_START + IRIS_BORDER_COLOR_POOL_SIZE,
- _4GB_minus_1 - IRIS_BORDER_COLOR_POOL_SIZE);
+ dynamic_pool_size);
/* Leave the last 4GB out of the high vma range, so that no state
* base address + size can overflow 48 bits.
init_cache_buckets(bufmgr);
bufmgr->name_table =
- _mesa_hash_table_create(NULL, key_hash_uint, key_uint_equal);
+ _mesa_hash_table_create(NULL, _mesa_hash_uint, _mesa_key_uint_equal);
bufmgr->handle_table =
- _mesa_hash_table_create(NULL, key_hash_uint, key_uint_equal);
+ _mesa_hash_table_create(NULL, _mesa_hash_uint, _mesa_key_uint_equal);
+ if (devinfo->gen >= 12) {
+ bufmgr->aux_map_ctx = gen_aux_map_init(bufmgr, &aux_map_allocator,
+ devinfo);
+ assert(bufmgr->aux_map_ctx);
+ }
+
+ return bufmgr;
+}
+
+static struct iris_bufmgr *
+iris_bufmgr_ref(struct iris_bufmgr *bufmgr)
+{
+ p_atomic_inc(&bufmgr->refcount);
return bufmgr;
}
+
+void
+iris_bufmgr_unref(struct iris_bufmgr *bufmgr)
+{
+ mtx_lock(&global_bufmgr_list_mutex);
+ if (p_atomic_dec_zero(&bufmgr->refcount)) {
+ list_del(&bufmgr->link);
+ iris_bufmgr_destroy(bufmgr);
+ }
+ mtx_unlock(&global_bufmgr_list_mutex);
+}
+
+/**
+ * Gets an already existing GEM buffer manager or create a new one.
+ *
+ * \param fd File descriptor of the opened DRM device.
+ */
+struct iris_bufmgr *
+iris_bufmgr_get_for_fd(struct gen_device_info *devinfo, int fd, bool bo_reuse)
+{
+ struct stat st;
+
+ if (fstat(fd, &st))
+ return NULL;
+
+ struct iris_bufmgr *bufmgr = NULL;
+
+ mtx_lock(&global_bufmgr_list_mutex);
+ list_for_each_entry(struct iris_bufmgr, iter_bufmgr, &global_bufmgr_list, link) {
+ struct stat iter_st;
+ if (fstat(iter_bufmgr->fd, &iter_st))
+ continue;
+
+ if (st.st_rdev == iter_st.st_rdev) {
+ assert(iter_bufmgr->bo_reuse == bo_reuse);
+ bufmgr = iris_bufmgr_ref(iter_bufmgr);
+ goto unlock;
+ }
+ }
+
+ bufmgr = iris_bufmgr_create(devinfo, fd, bo_reuse);
+ list_addtail(&bufmgr->link, &global_bufmgr_list);
+
+ unlock:
+ mtx_unlock(&global_bufmgr_list_mutex);
+
+ return bufmgr;
+}
+
+int
+iris_bufmgr_get_fd(struct iris_bufmgr *bufmgr)
+{
+ return bufmgr->fd;
+}
+
+void*
+iris_bufmgr_get_aux_map_context(struct iris_bufmgr *bufmgr)
+{
+ return bufmgr->aux_map_ctx;
+}