drm: kselftest for drm_mm_insert_node_in_range()
authorChris Wilson <chris@chris-wilson.co.uk>
Thu, 22 Dec 2016 08:36:15 +0000 (08:36 +0000)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 27 Dec 2016 11:37:19 +0000 (12:37 +0100)
Exercise drm_mm_insert_node_in_range(), check that we only allocate from
the specified range.

v2: Use all allocation flags
v3: Don't pass in invalid ranges - these will be asserted later.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20161222083641.2691-13-chris@chris-wilson.co.uk
drivers/gpu/drm/selftests/drm_mm_selftests.h
drivers/gpu/drm/selftests/test-drm_mm.c

index dca726baa65dd5a3d4996f613e6e6b19afc6c5e0..92b2c1cb10faf9e19dab74b06f15bc206a562836 100644 (file)
@@ -11,3 +11,4 @@ selftest(debug, igt_debug)
 selftest(reserve, igt_reserve)
 selftest(insert, igt_insert)
 selftest(replace, igt_replace)
+selftest(insert_range, igt_insert_range)
index 7914428c9862b01d38d3036a1aef70e6c4dd7b72..afc9274d80867275dbff0e571594495621ad9f98 100644 (file)
@@ -738,6 +738,275 @@ static int igt_replace(void *ignored)
        return 0;
 }
 
+static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node,
+                                  u64 size, u64 alignment, unsigned long color,
+                                  u64 range_start, u64 range_end,
+                                  const struct insert_mode *mode)
+{
+       int err;
+
+       err = drm_mm_insert_node_in_range_generic(mm, node,
+                                                 size, alignment, color,
+                                                 range_start, range_end,
+                                                 mode->search_flags,
+                                                 mode->create_flags);
+       if (err) {
+               pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n",
+                      size, alignment, color, mode->name,
+                      range_start, range_end, err);
+               return false;
+       }
+
+       if (!assert_node(node, mm, size, alignment, color)) {
+               drm_mm_remove_node(node);
+               return false;
+       }
+
+       return true;
+}
+
+static bool expect_insert_in_range_fail(struct drm_mm *mm,
+                                       u64 size,
+                                       u64 range_start,
+                                       u64 range_end)
+{
+       struct drm_mm_node tmp = {};
+       int err;
+
+       err = drm_mm_insert_node_in_range_generic(mm, &tmp,
+                                                 size, 0, 0,
+                                                 range_start, range_end,
+                                                 DRM_MM_SEARCH_DEFAULT,
+                                                 DRM_MM_CREATE_DEFAULT);
+       if (likely(err == -ENOSPC))
+               return true;
+
+       if (!err) {
+               pr_err("impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n",
+                      tmp.start, tmp.size, range_start, range_end);
+               drm_mm_remove_node(&tmp);
+       } else {
+               pr_err("impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n",
+                      err, -ENOSPC, size, range_start, range_end);
+       }
+
+       return false;
+}
+
+static bool assert_contiguous_in_range(struct drm_mm *mm,
+                                      u64 size,
+                                      u64 start,
+                                      u64 end)
+{
+       struct drm_mm_node *node;
+       unsigned int n;
+
+       if (!expect_insert_in_range_fail(mm, size, start, end))
+               return false;
+
+       n = div64_u64(start + size - 1, size);
+       drm_mm_for_each_node(node, mm) {
+               if (node->start < start || node->start + node->size > end) {
+                       pr_err("node %d out of range, address [%llx + %llu], range [%llx, %llx]\n",
+                              n, node->start, node->start + node->size, start, end);
+                       return false;
+               }
+
+               if (node->start != n * size) {
+                       pr_err("node %d out of order, expected start %llx, found %llx\n",
+                              n, n * size, node->start);
+                       return false;
+               }
+
+               if (node->size != size) {
+                       pr_err("node %d has wrong size, expected size %llx, found %llx\n",
+                              n, size, node->size);
+                       return false;
+               }
+
+               if (node->hole_follows && drm_mm_hole_node_end(node) < end) {
+                       pr_err("node %d is followed by a hole!\n", n);
+                       return false;
+               }
+
+               n++;
+       }
+
+       drm_mm_for_each_node_in_range(node, mm, 0, start) {
+               if (node) {
+                       pr_err("node before start: node=%llx+%llu, start=%llx\n",
+                              node->start, node->size, start);
+                       return false;
+               }
+       }
+
+       drm_mm_for_each_node_in_range(node, mm, end, U64_MAX) {
+               if (node) {
+                       pr_err("node after end: node=%llx+%llu, end=%llx\n",
+                              node->start, node->size, end);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
+{
+       const struct insert_mode *mode;
+       struct drm_mm mm;
+       struct drm_mm_node *nodes, *node, *next;
+       unsigned int n, start_n, end_n;
+       int ret;
+
+       DRM_MM_BUG_ON(!count);
+       DRM_MM_BUG_ON(!size);
+       DRM_MM_BUG_ON(end <= start);
+
+       /* Very similar to __igt_insert(), but now instead of populating the
+        * full range of the drm_mm, we try to fill a small portion of it.
+        */
+
+       ret = -ENOMEM;
+       nodes = vzalloc(count * sizeof(*nodes));
+       if (!nodes)
+               goto err;
+
+       ret = -EINVAL;
+       drm_mm_init(&mm, 0, count * size);
+
+       start_n = div64_u64(start + size - 1, size);
+       end_n = div64_u64(end - size, size);
+
+       for (mode = insert_modes; mode->name; mode++) {
+               for (n = start_n; n <= end_n; n++) {
+                       if (!expect_insert_in_range(&mm, &nodes[n],
+                                                   size, size, n,
+                                                   start, end, mode)) {
+                               pr_err("%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n",
+                                      mode->name, size, n,
+                                      start_n, end_n,
+                                      start, end);
+                               goto out;
+                       }
+               }
+
+               if (!assert_contiguous_in_range(&mm, size, start, end)) {
+                       pr_err("%s: range [%llx, %llx] not full after initialisation, size=%llu\n",
+                              mode->name, start, end, size);
+                       goto out;
+               }
+
+               /* Remove one and reinsert, it should refill itself */
+               for (n = start_n; n <= end_n; n++) {
+                       u64 addr = nodes[n].start;
+
+                       drm_mm_remove_node(&nodes[n]);
+                       if (!expect_insert_in_range(&mm, &nodes[n],
+                                                   size, size, n,
+                                                   start, end, mode)) {
+                               pr_err("%s reinsert failed, step %d\n", mode->name, n);
+                               goto out;
+                       }
+
+                       if (nodes[n].start != addr) {
+                               pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n",
+                                      mode->name, n, addr, nodes[n].start);
+                               goto out;
+                       }
+               }
+
+               if (!assert_contiguous_in_range(&mm, size, start, end)) {
+                       pr_err("%s: range [%llx, %llx] not full after reinsertion, size=%llu\n",
+                              mode->name, start, end, size);
+                       goto out;
+               }
+
+               drm_mm_for_each_node_safe(node, next, &mm)
+                       drm_mm_remove_node(node);
+               DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+       }
+
+       ret = 0;
+out:
+       drm_mm_for_each_node_safe(node, next, &mm)
+               drm_mm_remove_node(node);
+       drm_mm_takedown(&mm);
+       vfree(nodes);
+err:
+       return ret;
+}
+
+static int insert_outside_range(void)
+{
+       struct drm_mm mm;
+       const unsigned int start = 1024;
+       const unsigned int end = 2048;
+       const unsigned int size = end - start;
+
+       drm_mm_init(&mm, start, size);
+
+       if (!expect_insert_in_range_fail(&mm, 1, 0, start))
+               return -EINVAL;
+
+       if (!expect_insert_in_range_fail(&mm, size,
+                                        start - size/2, start + (size+1)/2))
+               return -EINVAL;
+
+       if (!expect_insert_in_range_fail(&mm, size,
+                                        end - (size+1)/2, end + size/2))
+               return -EINVAL;
+
+       if (!expect_insert_in_range_fail(&mm, 1, end, end + size))
+               return -EINVAL;
+
+       drm_mm_takedown(&mm);
+       return 0;
+}
+
+static int igt_insert_range(void *ignored)
+{
+       const unsigned int count = min_t(unsigned int, BIT(13), max_iterations);
+       unsigned int n;
+       int ret;
+
+       /* Check that requests outside the bounds of drm_mm are rejected. */
+       ret = insert_outside_range();
+       if (ret)
+               return ret;
+
+       for_each_prime_number_from(n, 1, 50) {
+               const u64 size = BIT_ULL(n);
+               const u64 max = count * size;
+
+               ret = __igt_insert_range(count, size, 0, max);
+               if (ret)
+                       return ret;
+
+               ret = __igt_insert_range(count, size, 1, max);
+               if (ret)
+                       return ret;
+
+               ret = __igt_insert_range(count, size, 0, max - 1);
+               if (ret)
+                       return ret;
+
+               ret = __igt_insert_range(count, size, 0, max/2);
+               if (ret)
+                       return ret;
+
+               ret = __igt_insert_range(count, size, max/2, max);
+               if (ret)
+                       return ret;
+
+               ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 #include "drm_selftest.c"
 
 static int __init test_drm_mm_init(void)