summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrzej Turko <andrzej.turko@linux.intel.com>2021-05-26 14:00:19 +0100
committerMatthew Auld <matthew.auld@intel.com>2021-05-27 11:36:21 +0100
commitf32993a2eda6557aa1029b37430f6971ba44f3b6 (patch)
treef2796b6ec7f885288e71331a90777d14be0132c5
parentb0789992cefc3b08fb5fd891522a116b367c3963 (diff)
lib/i915/intel_memory_region: Add new memory region lib
With an introduction of Local Memory concept we should be able to allocate object in specific memory region. This patch implements helper functions that allow this, both for querying what the device supports and hooking into gem_create_ext to select the placements. Co-authored-by: Lukasz Kalamarz <lukasz.kalamarz@intel.com> Co-authored-by: Zbigniew Kempczynski <zbigniew.kempczynski@intel.com> Signed-off-by: Andrzej Turko <andrzej.turko@linux.intel.com> Signed-off-by: Lukasz Kalamarz <lukasz.kalamarz@intel.com> Signed-off-by: Zbigniew Kempczynski <zbigniew.kempczynski@intel.com> Cc: Zbigniew Kempczynski <zbigniew.kempczynski@intel.com> Cc: Dominik Grzegorzek <dominik.grzegorzek@intel.com> Cc: Petri Latvala <petri.latvala@intel.com> Signed-off-by: Matthew Auld <matthew.auld@intel.com> Acked-by: Petri Latvala <petri.latvala@intel.com>
-rw-r--r--lib/i915/intel_memory_region.c441
-rw-r--r--lib/i915/intel_memory_region.h130
-rw-r--r--lib/ioctl_wrappers.h1
-rw-r--r--lib/meson.build1
4 files changed, 573 insertions, 0 deletions
diff --git a/lib/i915/intel_memory_region.c b/lib/i915/intel_memory_region.c
new file mode 100644
index 00000000..983ac4df
--- /dev/null
+++ b/lib/i915/intel_memory_region.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright © 2020 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <linux/limits.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "i915/gem_create.h"
+#include "intel_reg.h"
+#include "drmtest.h"
+#include "ioctl_wrappers.h"
+#include "igt_dummyload.h"
+#include "igt_gt.h"
+#include "igt_params.h"
+#include "igt_sysfs.h"
+#include "intel_chipset.h"
+#include "igt_collection.h"
+#include "igt_device.h"
+
+#include "i915/intel_memory_region.h"
+
+#define i915_query_items(fd, items, n_items) do { \
+ igt_assert_eq(__i915_query_items(fd, items, n_items), 0); \
+ errno = 0; \
+ } while (0)
+#define i915_query_items_err(fd, items, n_items, err) do { \
+ igt_assert_eq(__i915_query_items(fd, items, n_items), -err); \
+ } while (0)
+
+static int
+__i915_query(int fd, struct drm_i915_query *q)
+{
+ if (igt_ioctl(fd, DRM_IOCTL_I915_QUERY, q))
+ return -errno;
+ return 0;
+}
+
+static int
+__i915_query_items(int fd, struct drm_i915_query_item *items, uint32_t n_items)
+{
+ struct drm_i915_query q = {
+ .num_items = n_items,
+ .items_ptr = to_user_pointer(items),
+ };
+ return __i915_query(fd, &q);
+}
+
+bool gem_has_query_support(int fd)
+{
+ struct drm_i915_query query = {};
+
+ return __i915_query(fd, &query) == 0;
+}
+
+const char *get_memory_region_name(uint32_t region)
+{
+ uint16_t class = MEMORY_TYPE_FROM_REGION(region);
+
+ switch (class) {
+ case I915_MEMORY_CLASS_SYSTEM:
+ return "smem";
+ case I915_MEMORY_CLASS_DEVICE:
+ return "lmem";
+ }
+ igt_assert_f(false, "Unknown memory region");
+}
+
+/**
+ * gem_get_batch_size:
+ * @fd: open i915 drm file descriptor
+ * @mem_region_type: used memory_region type
+ *
+ * With introduction of LMEM we observe different page sizes for those two
+ * memory regions. Without this helper funtion we may be prone to forget
+ * about setting proper page size.
+ */
+uint32_t gem_get_batch_size(int fd, uint8_t mem_region_type)
+{
+ return (mem_region_type == I915_MEMORY_CLASS_DEVICE) ? 65536 : 4096;
+}
+
+/**
+ * gem_get_query_memory_regions:
+ * @fd: open i915 drm file descriptor
+ *
+ * This function wraps query mechanism for memory regions.
+ *
+ * Returns: Filled struct with available memory regions.
+ */
+struct drm_i915_query_memory_regions *gem_get_query_memory_regions(int fd)
+{
+ struct drm_i915_query_item item;
+ struct drm_i915_query_memory_regions *query_info;
+
+ memset(&item, 0, sizeof(item));
+ item.query_id = DRM_I915_QUERY_MEMORY_REGIONS;
+ i915_query_items(fd, &item, 1);
+
+ query_info = calloc(1, item.length);
+
+ item.data_ptr = to_user_pointer(query_info);
+ i915_query_items(fd, &item, 1);
+
+ return query_info;
+}
+
+/**
+ * gem_get_lmem_region_count:
+ * @fd: open i915 drm file descriptor
+ *
+ * Helper function to check how many lmem regions are available on device.
+ *
+ * Returns: Number of found lmem regions.
+ */
+uint8_t gem_get_lmem_region_count(int fd)
+{
+ struct drm_i915_query_memory_regions *query_info;
+ uint8_t num_regions;
+ uint8_t lmem_regions = 0;
+
+ query_info = gem_get_query_memory_regions(fd);
+ num_regions = query_info->num_regions;
+
+ for (int i = 0; i < num_regions; i++) {
+ if (query_info->regions[i].region.memory_class == I915_MEMORY_CLASS_DEVICE)
+ lmem_regions += 1;
+ }
+
+ return lmem_regions;
+}
+
+/**
+ * gem_has_lmem:
+ * @fd: open i915 drm file descriptor
+ *
+ * Helper function to check if lmem is available on device.
+ *
+ * Returns: True if at least one lmem region was found.
+ */
+bool gem_has_lmem(int fd)
+{
+ return gem_get_lmem_region_count(fd) > 0;
+}
+
+/* A version of gem_create_in_memory_region_list which can be allowed to
+ fail so that the object creation can be retried */
+int __gem_create_in_memory_region_list(int fd, uint32_t *handle, uint64_t size,
+ struct drm_i915_gem_memory_class_instance *mem_regions,
+ int num_regions)
+{
+ struct drm_i915_gem_create_ext_memory_regions ext_regions = {
+ .base = { .name = I915_GEM_CREATE_EXT_MEMORY_REGIONS },
+ .num_regions = num_regions,
+ .regions = to_user_pointer(mem_regions),
+ };
+
+ return __gem_create_ext(fd, &size, handle, &ext_regions.base);
+}
+
+/* gem_create_in_memory_region_list:
+ * @fd: opened i915 drm file descriptor
+ * @size: requested size of the buffer
+ * @mem_regions: memory regions array (priority list)
+ * @num_regions: @mem_regions length
+ */
+uint32_t gem_create_in_memory_region_list(int fd, uint64_t size,
+ struct drm_i915_gem_memory_class_instance *mem_regions,
+ int num_regions)
+{
+ uint32_t handle;
+ int ret = __gem_create_in_memory_region_list(fd, &handle, size,
+ mem_regions, num_regions);
+ igt_assert_eq(ret, 0);
+ return handle;
+}
+
+static bool __region_belongs_to_regions_type(struct drm_i915_gem_memory_class_instance region,
+ uint32_t *mem_regions_type,
+ int num_regions)
+{
+ for (int i = 0; i < num_regions; i++)
+ if (mem_regions_type[i] == region.memory_class)
+ return true;
+ return false;
+}
+
+struct igt_collection *
+__get_memory_region_set(struct drm_i915_query_memory_regions *regions,
+ uint32_t *mem_regions_type,
+ int num_regions)
+{
+ struct drm_i915_gem_memory_class_instance region;
+ struct igt_collection *set;
+ int count = 0, pos = 0;
+
+ for (int i = 0; i < regions->num_regions; i++) {
+ region = regions->regions[i].region;
+ if (__region_belongs_to_regions_type(region,
+ mem_regions_type,
+ num_regions))
+ count++;
+ }
+
+ set = igt_collection_create(count);
+
+ for (int i = 0; i < regions->num_regions; i++) {
+ region = regions->regions[i].region;
+ if (__region_belongs_to_regions_type(region,
+ mem_regions_type,
+ num_regions))
+ igt_collection_set_value(set, pos++,
+ INTEL_MEMORY_REGION_ID(region.memory_class,
+ region.memory_instance));
+ }
+
+ igt_assert(count == pos);
+
+ return set;
+}
+
+/**
+ * memregion_dynamic_subtest_name:
+ * @igt_collection: memory region collection
+ *
+ * Function iterates over all memory regions inside the collection (keeped
+ * in the value field) and generates the name which can be used during dynamic
+ * subtest creation.
+ *
+ * Returns: newly allocated string, has to be freed by caller. Asserts if
+ * caller tries to create a name using empty collection.
+ */
+char *memregion_dynamic_subtest_name(struct igt_collection *set)
+{
+ struct igt_collection_data *data;
+ char *name, *p;
+ uint32_t region, len;
+
+ igt_assert(set && set->size);
+ /* enough for "name%d-" * n */
+ len = set->size * 8;
+ p = name = malloc(len);
+ igt_assert(name);
+
+ for_each_collection_data(data, set) {
+ int r;
+
+ region = data->value;
+ if (IS_DEVICE_MEMORY_REGION(region))
+ r = snprintf(p, len, "%s%d-",
+ get_memory_region_name(region),
+ MEMORY_INSTANCE_FROM_REGION(region));
+ else
+ r = snprintf(p, len, "%s-",
+ get_memory_region_name(region));
+
+ igt_assert(r > 0);
+ p += r;
+ len -= r;
+ }
+
+ /* remove last '-' */
+ *(p - 1) = 0;
+
+ return name;
+}
+
+/**
+ * intel_dump_gpu_meminfo:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ *
+ * Outputs memory regions and their sizes.
+ */
+void intel_dump_gpu_meminfo(struct drm_i915_query_memory_regions *info)
+{
+ int i;
+
+ igt_assert(info);
+
+ igt_info("GPU meminfo:\n");
+
+ for (i = 0; i < info->num_regions; i++) {
+ uint32_t region = INTEL_MEMORY_REGION_ID(info->regions[i].region.memory_class,
+ info->regions[i].region.memory_instance);
+ const char *name = get_memory_region_name(region);
+
+ igt_info("- %s [%d] memory [size: 0x%llx, available: 0x%llx]\n",
+ name, info->regions[i].region.memory_instance,
+ info->regions[i].probed_size,
+ info->regions[i].unallocated_size);
+ }
+}
+
+/**
+ * gpu_meminfo_region_count:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ *
+ * Returns: number of regions for type @memory_class
+ */
+uint32_t gpu_meminfo_region_count(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class)
+{
+ uint32_t num = 0;
+ int i;
+
+ igt_assert(info);
+
+ for (i = 0; i < info->num_regions; i++)
+ if (info->regions[i].region.memory_class == memory_class)
+ num++;
+
+ return num;
+}
+
+/**
+ * gpu_meminfo_region_total_size:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ *
+ * Returns: total size of all regions which are type @memory_class, -1 when the
+ * size of at least one region is unknown
+ */
+uint64_t gpu_meminfo_region_total_size(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class)
+{
+ uint64_t total = 0;
+ int i;
+
+ igt_assert(info);
+
+ for (i = 0; i < info->num_regions; i++)
+ if (info->regions[i].region.memory_class == memory_class) {
+ if (info->regions[i].probed_size == -1)
+ return -1;
+
+ total += info->regions[i].probed_size;
+ }
+
+ return total;
+}
+
+/**
+ * gpu_meminfo_region_total_available:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ *
+ * Returns: available size of all regions which are type @memory_class, -1 when
+ * the size of at least one region cannot be estimated
+ */
+uint64_t gpu_meminfo_region_total_available(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class)
+{
+ uint64_t avail = 0;
+ int i;
+
+ igt_assert(info);
+
+ for (i = 0; i < info->num_regions; i++)
+ if (info->regions[i].region.memory_class == memory_class) {
+ if (info->regions[i].unallocated_size == -1)
+ return -1;
+
+ avail += info->regions[i].unallocated_size;
+ }
+
+ return avail;
+}
+
+/**
+ * gpu_meminfo_region_size:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ * @memory_instance: memory region instance
+ *
+ * Returns: available size of @memory_instance which type is @memory_class, -1
+ * when the size is unknown
+ */
+uint64_t gpu_meminfo_region_size(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class,
+ uint16_t memory_instance)
+{
+ int i;
+
+ igt_assert(info);
+
+ for (i = 0; i < info->num_regions; i++)
+ if (info->regions[i].region.memory_class == memory_class &&
+ info->regions[i].region.memory_instance == memory_instance)
+ return info->regions[i].probed_size;
+
+ return 0;
+}
+
+/**
+ * gpu_meminfo_region_available:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ * @memory_instance: memory region instance
+ *
+ * Returns: available size of @memory_instance region which type is
+ * @memory_class, -1 when the size cannot be estimated
+ */
+uint64_t gpu_meminfo_region_available(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class,
+ uint16_t memory_instance)
+{
+ int i;
+
+ igt_assert(info);
+
+ for (i = 0; i < info->num_regions; i++)
+ if (info->regions[i].region.memory_class == memory_class &&
+ info->regions[i].region.memory_instance == memory_instance)
+ return info->regions[i].unallocated_size;
+
+ return 0;
+}
diff --git a/lib/i915/intel_memory_region.h b/lib/i915/intel_memory_region.h
new file mode 100644
index 00000000..e9d1d66d
--- /dev/null
+++ b/lib/i915/intel_memory_region.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2020 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "igt_collection.h"
+
+#ifndef INTEL_MEMORY_REGION_H
+#define INTEL_MEMORY_REGION_H
+
+#define I915_SYSTEM_MEMORY I915_MEMORY_CLASS_SYSTEM
+#define I915_DEVICE_MEMORY I915_MEMORY_CLASS_DEVICE
+#define I915_STOLEN_SYSTEM_MEMORY I915_MEMORY_CLASS_STOLEN_SYSTEM
+#define I915_STOLEN_DEVICE_MEMORY I915_MEMORY_CLASS_STOLEN_DEVICE
+
+#define INTEL_MEMORY_REGION_ID(type, instance) ((type) << 16u | (instance))
+#define MEMORY_TYPE_FROM_REGION(r) ((r) >> 16u)
+#define MEMORY_INSTANCE_FROM_REGION(r) ((r) & 0xffff)
+
+#define IS_MEMORY_REGION_TYPE(region, type) \
+ (MEMORY_TYPE_FROM_REGION(region) == type)
+
+#define IS_DEVICE_MEMORY_REGION(region) \
+ IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_DEVICE)
+#define IS_SYSTEM_MEMORY_REGION(region) \
+ IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_SYSTEM)
+
+#define IS_STOLEN_MEMORY_REGION(region) \
+ (IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_STOLEN_SYSTEM) || \
+ IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_STOLEN_DEVICE))
+
+#define REGION_SMEM INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_SYSTEM, 0)
+#define REGION_LMEM(n) INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_DEVICE, (n))
+#define REGION_STLN_SMEM(n) INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_STOLEN_SYSTEM, (n))
+#define REGION_STLN_LMEM(n) INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_STOLEN_DEVICE, (n))
+
+bool gem_has_query_support(int fd);
+
+const char *get_memory_region_name(uint32_t region);
+uint32_t gem_get_batch_size(int fd, uint8_t mem_region_type);
+
+struct drm_i915_query_memory_regions *gem_get_query_memory_regions(int fd);
+
+uint8_t gem_get_lmem_region_count(int fd);
+
+bool gem_has_lmem(int fd);
+
+int __gem_create_in_memory_region_list(int fd, uint32_t *handle, uint64_t size,
+ struct drm_i915_gem_memory_class_instance *mem_regions,
+ int num_regions);
+
+uint32_t gem_create_in_memory_region_list(int fd, uint64_t size,
+ struct drm_i915_gem_memory_class_instance *mem_regions,
+ int num_regions);
+
+/*
+ * XXX: the whole converting to class_instance thing is meant as a temporary
+ * stop gap which should keep everything working, such that we don't have to
+ * rewrite the world in one go to fit the new uAPI.
+ */
+#define __gem_create_in_memory_regions(fd, handle, size, regions...) ({ \
+ unsigned int arr__[] = { regions }; \
+ struct drm_i915_gem_memory_class_instance arr_query__[ARRAY_SIZE(arr__)]; \
+ for (int i__ = 0; i__ < ARRAY_SIZE(arr_query__); ++i__) { \
+ arr_query__[i__].memory_class = MEMORY_TYPE_FROM_REGION(arr__[i__]); \
+ arr_query__[i__].memory_instance = MEMORY_INSTANCE_FROM_REGION(arr__[i__]); \
+ } \
+ __gem_create_in_memory_region_list(fd, handle, size, arr_query__, ARRAY_SIZE(arr_query__)); \
+})
+#define gem_create_in_memory_regions(fd, size, regions...) ({ \
+ unsigned int arr__[] = { regions }; \
+ struct drm_i915_gem_memory_class_instance arr_query__[ARRAY_SIZE(arr__)]; \
+ for (int i__ = 0; i__ < ARRAY_SIZE(arr_query__); ++i__) { \
+ arr_query__[i__].memory_class = MEMORY_TYPE_FROM_REGION(arr__[i__]); \
+ arr_query__[i__].memory_instance = MEMORY_INSTANCE_FROM_REGION(arr__[i__]); \
+ } \
+ gem_create_in_memory_region_list(fd, size, arr_query__, ARRAY_SIZE(arr_query__)); \
+})
+
+struct igt_collection *
+__get_memory_region_set(struct drm_i915_query_memory_regions *regions,
+ uint32_t *mem_regions_type,
+ int num_regions);
+
+/*
+ * Helper macro to create igt_collection which contains all memory regions
+ * which matches mem_region_types array. Useful to filter out stolen memory
+ * from accessible memory regions.
+ */
+#define get_memory_region_set(regions, mem_region_types...) ({ \
+ unsigned int arr__[] = { mem_region_types }; \
+ __get_memory_region_set(regions, arr__, ARRAY_SIZE(arr__)); \
+})
+
+char *memregion_dynamic_subtest_name(struct igt_collection *set);
+
+void intel_dump_gpu_meminfo(struct drm_i915_query_memory_regions *info);
+
+uint32_t gpu_meminfo_region_count(struct drm_i915_query_memory_regions *info,
+ uint16_t region_class);
+uint64_t gpu_meminfo_region_total_size(struct drm_i915_query_memory_regions *info,
+ uint16_t region_class);
+uint64_t gpu_meminfo_region_total_available(struct drm_i915_query_memory_regions *info,
+ uint16_t region_type);
+
+uint64_t gpu_meminfo_region_size(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class,
+ uint16_t memory_instance);
+uint64_t gpu_meminfo_region_available(struct drm_i915_query_memory_regions *info,
+ uint16_t memory_class,
+ uint16_t memory_instance);
+
+#endif /* INTEL_MEMORY_REGION_H */
diff --git a/lib/ioctl_wrappers.h b/lib/ioctl_wrappers.h
index 9ea67365..36640e30 100644
--- a/lib/ioctl_wrappers.h
+++ b/lib/ioctl_wrappers.h
@@ -38,6 +38,7 @@
#include "i915/gem_context.h"
#include "i915/gem_scheduler.h"
+#include "i915/intel_memory_region.h"
/**
* igt_ioctl:
diff --git a/lib/meson.build b/lib/meson.build
index d5a1c970..078a357b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -10,6 +10,7 @@ lib_sources = [
'i915/gem_ring.c',
'i915/gem_mman.c',
'i915/gem_vm.c',
+ 'i915/intel_memory_region.c',
'igt_collection.c',
'igt_color_encoding.c',
'igt_debugfs.c',