summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/i915/intel_memory_region.c373
-rw-r--r--lib/i915/intel_memory_region.h5
2 files changed, 378 insertions, 0 deletions
diff --git a/lib/i915/intel_memory_region.c b/lib/i915/intel_memory_region.c
index dfbb8acf..a8759e06 100644
--- a/lib/i915/intel_memory_region.c
+++ b/lib/i915/intel_memory_region.c
@@ -28,11 +28,13 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <pthread.h>
#include "i915/gem_create.h"
#include "intel_reg.h"
#include "drmtest.h"
#include "ioctl_wrappers.h"
+#include "igt_aux.h"
#include "igt_dummyload.h"
#include "igt_gt.h"
#include "igt_params.h"
@@ -40,6 +42,7 @@
#include "intel_chipset.h"
#include "igt_collection.h"
#include "igt_device.h"
+#include "gem_mman.h"
#include "i915/intel_memory_region.h"
@@ -480,3 +483,373 @@ uint64_t gpu_meminfo_region_available(const struct drm_i915_query_memory_regions
return 0;
}
+
+#define PAGE_SIZE 4096
+
+enum cache_entry_type {
+ MIN_START_OFFSET,
+ MIN_ALIGNMENT,
+ SAFE_START_OFFSET,
+ SAFE_ALIGNMENT,
+};
+
+struct cache_entry {
+ uint16_t devid;
+ enum cache_entry_type type;
+
+ union {
+ /* for MIN_START_OFFSET */
+ struct {
+ uint64_t offset;
+ uint32_t region;
+ } start;
+
+ /* for MIN_ALIGNMENT */
+ struct {
+ uint64_t alignment;
+ uint64_t region1;
+ uint64_t region2;
+ } minalign;
+
+ /* for SAFE_START_OFFSET */
+ uint64_t safe_start_offset;
+
+ /* for SAFE_ALIGNMENT */
+ uint64_t safe_alignment;
+ };
+ struct igt_list_head link;
+};
+
+static IGT_LIST_HEAD(cache);
+static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static struct cache_entry *find_entry_unlocked(enum cache_entry_type type,
+ uint16_t devid,
+ uint32_t region1,
+ uint32_t region2)
+{
+ struct cache_entry *entry;
+
+ igt_list_for_each_entry(entry, &cache, link) {
+ if (entry->type != type || entry->devid != devid)
+ continue;
+
+ switch (entry->type) {
+ case MIN_START_OFFSET:
+ if (entry->start.region == region1)
+ return entry;
+ continue;
+
+ case MIN_ALIGNMENT:
+ if (entry->minalign.region1 == region1 &&
+ entry->minalign.region2 == region2)
+ return entry;
+ continue;
+
+ case SAFE_START_OFFSET:
+ case SAFE_ALIGNMENT:
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * gem_detect_min_start_offset_for_region:
+ * @i915: drm fd
+ * @region: memory region
+ *
+ * Returns: minimum start offset at which kernel allows placing objects
+ * for memory region.
+ */
+uint64_t gem_detect_min_start_offset_for_region(int i915, uint32_t region)
+{
+ struct drm_i915_gem_exec_object2 obj;
+ struct drm_i915_gem_execbuffer2 eb;
+ uint64_t start_offset = 0;
+ uint64_t bb_size = PAGE_SIZE;
+ uint32_t *batch;
+ uint16_t devid = intel_get_drm_devid(i915);
+ struct cache_entry *entry, *newentry;
+
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(MIN_START_OFFSET, devid, region, 0);
+ if (entry)
+ goto out;
+ pthread_mutex_unlock(&cache_mutex);
+
+ memset(&obj, 0, sizeof(obj));
+ memset(&eb, 0, sizeof(eb));
+
+ eb.buffers_ptr = to_user_pointer(&obj);
+ eb.buffer_count = 1;
+ eb.flags = I915_EXEC_DEFAULT;
+ igt_assert(__gem_create_in_memory_regions(i915, &obj.handle, &bb_size, region) == 0);
+ obj.flags = EXEC_OBJECT_PINNED;
+
+ batch = gem_mmap__device_coherent(i915, obj.handle, 0, bb_size, PROT_WRITE);
+ *batch = MI_BATCH_BUFFER_END;
+ munmap(batch, bb_size);
+
+ while (1) {
+ obj.offset = start_offset;
+
+ if (__gem_execbuf(i915, &eb) == 0)
+ break;
+
+ if (start_offset)
+ start_offset <<= 1;
+ else
+ start_offset = PAGE_SIZE;
+
+ if (start_offset >= 1ull << 32)
+ obj.flags |= EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+
+ igt_assert(start_offset <= 1ull << 48);
+ }
+ gem_close(i915, obj.handle);
+
+ newentry = malloc(sizeof(*newentry));
+ if (!newentry)
+ return start_offset;
+
+ /* Check does other thread did the job before */
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(MIN_START_OFFSET, devid, region, 0);
+ if (entry)
+ goto out;
+
+ entry = newentry;
+ entry->devid = devid;
+ entry->type = MIN_START_OFFSET;
+ entry->start.offset = start_offset;
+ entry->start.region = region;
+ igt_list_add(&entry->link, &cache);
+
+out:
+ pthread_mutex_unlock(&cache_mutex);
+
+ return entry->start.offset;
+}
+
+/**
+ * gem_detect_safe_start_offset:
+ * @i915: drm fd
+ *
+ * Returns: finds start offset which can be used as first one regardless
+ * memory region. Useful if for some reason some regions don't allow
+ * starting from 0x0 offset.
+ */
+uint64_t gem_detect_safe_start_offset(int i915)
+{
+ struct drm_i915_query_memory_regions *query_info;
+ struct igt_collection *regions, *set;
+ uint32_t region;
+ uint64_t offset = 0;
+ uint16_t devid = intel_get_drm_devid(i915);
+ struct cache_entry *entry, *newentry;
+
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(SAFE_START_OFFSET, devid, 0, 0);
+ if (entry)
+ goto out;
+ pthread_mutex_unlock(&cache_mutex);
+
+ query_info = gem_get_query_memory_regions(i915);
+ igt_assert(query_info);
+
+ set = get_memory_region_set(query_info,
+ I915_SYSTEM_MEMORY,
+ I915_DEVICE_MEMORY);
+
+ for_each_combination(regions, 1, set) {
+ region = igt_collection_get_value(regions, 0);
+ offset = max(offset,
+ gem_detect_min_start_offset_for_region(i915, region));
+ }
+ free(query_info);
+ igt_collection_destroy(set);
+
+ newentry = malloc(sizeof(*newentry));
+ if (!newentry)
+ return offset;
+
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(SAFE_START_OFFSET, devid, 0, 0);
+ if (entry)
+ goto out;
+
+ entry = newentry;
+ entry->devid = devid;
+ entry->type = SAFE_START_OFFSET;
+ entry->safe_start_offset = offset;
+ igt_list_add(&entry->link, &cache);
+
+out:
+ pthread_mutex_unlock(&cache_mutex);
+
+ return entry->safe_start_offset;
+}
+
+/**
+ * gem_detect_min_alignment_for_regions:
+ * @i915: drm fd
+ * @region1: first region
+ * @region2: second region
+ *
+ * Returns: minimum alignment which must be used when objects from @region1 and
+ * @region2 are going to interact.
+ */
+uint64_t gem_detect_min_alignment_for_regions(int i915,
+ uint32_t region1,
+ uint32_t region2)
+{
+ struct drm_i915_gem_exec_object2 obj[2];
+ struct drm_i915_gem_execbuffer2 eb;
+ uint64_t min_alignment = PAGE_SIZE;
+ uint64_t bb_size = PAGE_SIZE, obj_size = PAGE_SIZE;
+ uint32_t *batch;
+ uint16_t devid = intel_get_drm_devid(i915);
+ struct cache_entry *entry, *newentry;
+
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(MIN_ALIGNMENT, devid, region1, region2);
+ if (entry)
+ goto out;
+ pthread_mutex_unlock(&cache_mutex);
+
+ memset(obj, 0, sizeof(obj));
+ memset(&eb, 0, sizeof(eb));
+
+ /* Establish bb offset first */
+ eb.buffers_ptr = to_user_pointer(obj);
+ eb.buffer_count = ARRAY_SIZE(obj);
+ eb.flags = I915_EXEC_BATCH_FIRST | I915_EXEC_DEFAULT;
+ igt_assert(__gem_create_in_memory_regions(i915, &obj[0].handle,
+ &bb_size, region1) == 0);
+
+ batch = gem_mmap__device_coherent(i915, obj[0].handle, 0, bb_size,
+ PROT_WRITE);
+ *batch = MI_BATCH_BUFFER_END;
+ munmap(batch, bb_size);
+
+ obj[0].flags = EXEC_OBJECT_PINNED;
+ obj[0].offset = gem_detect_min_start_offset_for_region(i915, region1);
+
+ /* Find appropriate alignment of object */
+ igt_assert(__gem_create_in_memory_regions(i915, &obj[1].handle,
+ &obj_size, region2) == 0);
+ obj[1].handle = gem_create_in_memory_regions(i915, PAGE_SIZE, region2);
+ obj[1].flags = EXEC_OBJECT_PINNED;
+ while (1) {
+ obj[1].offset = ALIGN(obj[0].offset + bb_size, min_alignment);
+ igt_assert(obj[1].offset <= 1ull << 32);
+
+ if (__gem_execbuf(i915, &eb) == 0)
+ break;
+
+ min_alignment <<= 1;
+ }
+
+ gem_close(i915, obj[0].handle);
+ gem_close(i915, obj[1].handle);
+
+ newentry = malloc(sizeof(*newentry));
+ if (!newentry)
+ return min_alignment;
+
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(MIN_ALIGNMENT, devid, region1, region2);
+ if (entry)
+ goto out;
+
+ entry = newentry;
+ entry->devid = devid;
+ entry->type = MIN_ALIGNMENT;
+ entry->minalign.alignment = min_alignment;
+ entry->minalign.region1 = region1;
+ entry->minalign.region2 = region2;
+ igt_list_add(&entry->link, &cache);
+
+out:
+ pthread_mutex_unlock(&cache_mutex);
+
+ return entry->minalign.alignment;
+}
+
+/**
+ * gem_detect_safe_alignment:
+ * @i915: drm fd
+ *
+ * Returns: safe alignment for all memory regions on @i915 device.
+ * Safe in this case means max() from all minimum alignments for each
+ * region.
+ */
+uint64_t gem_detect_safe_alignment(int i915)
+{
+ struct drm_i915_query_memory_regions *query_info;
+ struct igt_collection *regions, *set;
+ uint64_t default_alignment = 0;
+ uint32_t region_bb, region_obj;
+ uint16_t devid = intel_get_drm_devid(i915);
+ struct cache_entry *entry, *newentry;
+
+ /* non-discrete uses 4K page size */
+ if (!gem_has_lmem(i915))
+ return PAGE_SIZE;
+
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(SAFE_ALIGNMENT, devid, 0, 0);
+ if (entry)
+ goto out;
+ pthread_mutex_unlock(&cache_mutex);
+
+ query_info = gem_get_query_memory_regions(i915);
+ igt_assert(query_info);
+
+ set = get_memory_region_set(query_info,
+ I915_SYSTEM_MEMORY,
+ I915_DEVICE_MEMORY);
+
+ for_each_variation_r(regions, 2, set) {
+ uint64_t alignment;
+
+ region_bb = igt_collection_get_value(regions, 0);
+ region_obj = igt_collection_get_value(regions, 1);
+
+ /* We're interested in triangular matrix */
+ if (region_bb > region_obj)
+ continue;
+
+ alignment = gem_detect_min_alignment_for_regions(i915,
+ region_bb,
+ region_obj);
+ if (default_alignment < alignment)
+ default_alignment = alignment;
+ }
+
+ free(query_info);
+ igt_collection_destroy(set);
+
+ newentry = malloc(sizeof(*newentry));
+ if (!newentry)
+ return default_alignment;
+
+ /* Try again, check does we have cache updated in the meantime. */
+ pthread_mutex_lock(&cache_mutex);
+ entry = find_entry_unlocked(SAFE_ALIGNMENT, devid, 0, 0);
+ if (entry)
+ goto out;
+
+ entry = newentry;
+ entry->devid = devid;
+ entry->type = SAFE_ALIGNMENT;
+ entry->safe_alignment = default_alignment;
+ igt_list_add(&entry->link, &cache);
+
+out:
+ pthread_mutex_unlock(&cache_mutex);
+
+ return entry->minalign.alignment;
+}
diff --git a/lib/i915/intel_memory_region.h b/lib/i915/intel_memory_region.h
index 8b427b7e..936e7d1c 100644
--- a/lib/i915/intel_memory_region.h
+++ b/lib/i915/intel_memory_region.h
@@ -129,4 +129,9 @@ uint64_t gpu_meminfo_region_available(const struct drm_i915_query_memory_regions
uint16_t memory_class,
uint16_t memory_instance);
+uint64_t gem_detect_min_start_offset_for_region(int i915, uint32_t region);
+uint64_t gem_detect_safe_start_offset(int i915);
+uint64_t gem_detect_min_alignment_for_regions(int i915, uint32_t region1, uint32_t region2);
+uint64_t gem_detect_safe_alignment(int i915);
+
#endif /* INTEL_MEMORY_REGION_H */