Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
bbba969
Documentation
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fmc
fpga
gpio
gpu
drm
amd
arc
arm
armada
ast
atmel-hlcdc
bochs
bridge
cirrus
etnaviv
exynos
fsl-dcu
gma500
hisilicon
i2c
i810
i915
imx
lib
mediatek
meson
mga
mgag200
msm
mxsfb
nouveau
omapdrm
panel
qxl
r128
radeon
rcar-du
rockchip
savage
selftests
Makefile
drm_mm_selftests.h
drm_selftest.c
drm_selftest.h
test-drm_mm.c
shmobile
sis
sti
sun4i
tdfx
tegra
tilcdc
ttm
udl
vc4
vgem
via
virtio
vmwgfx
zte
Kconfig
Makefile
ati_pcigart.c
drm_agpsupport.c
drm_atomic.c
drm_atomic_helper.c
drm_auth.c
drm_blend.c
drm_bridge.c
drm_bufs.c
drm_cache.c
drm_color_mgmt.c
drm_connector.c
drm_context.c
drm_crtc.c
drm_crtc_helper.c
drm_crtc_helper_internal.h
drm_crtc_internal.h
drm_debugfs.c
drm_debugfs_crc.c
drm_dma.c
drm_dp_aux_dev.c
drm_dp_dual_mode_helper.c
drm_dp_helper.c
drm_dp_mst_topology.c
drm_drv.c
drm_dumb_buffers.c
drm_edid.c
drm_edid_load.c
drm_encoder.c
drm_encoder_slave.c
drm_fb_cma_helper.c
drm_fb_helper.c
drm_flip_work.c
drm_fops.c
drm_fourcc.c
drm_framebuffer.c
drm_gem.c
drm_gem_cma_helper.c
drm_global.c
drm_hashtab.c
drm_info.c
drm_internal.h
drm_ioc32.c
drm_ioctl.c
drm_irq.c
drm_kms_helper_common.c
drm_legacy.h
drm_lock.c
drm_memory.c
drm_mipi_dsi.c
drm_mm.c
drm_mode_config.c
drm_mode_object.c
drm_modes.c
drm_modeset_helper.c
drm_modeset_lock.c
drm_of.c
drm_panel.c
drm_pci.c
drm_plane.c
drm_plane_helper.c
drm_platform.c
drm_prime.c
drm_print.c
drm_probe_helper.c
drm_property.c
drm_rect.c
drm_scatter.c
drm_simple_kms_helper.c
drm_sysfs.c
drm_trace.h
drm_trace_points.c
drm_vm.c
drm_vma_manager.c
host1x
ipu-v3
vga
Makefile
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
ide
idle
iio
infiniband
input
iommu
ipack
irqchip
isdn
leds
lguest
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
mtd
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
sn
soc
spi
spmi
ssb
staging
target
tc
thermal
thunderbolt
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
gpu
/
drm
/
selftests
/
test-drm_mm.c
Copy path
Blame
Blame
Latest commit
History
History
2278 lines (1910 loc) · 51.2 KB
Breadcrumbs
linux
/
drivers
/
gpu
/
drm
/
selftests
/
test-drm_mm.c
Top
File metadata and controls
Code
Blame
2278 lines (1910 loc) · 51.2 KB
Raw
/* * Test cases for the drm_mm range manager */ #define pr_fmt(fmt) "drm_mm: " fmt #include <linux/module.h> #include <linux/prime_numbers.h> #include <linux/slab.h> #include <linux/random.h> #include <linux/vmalloc.h> #include <drm/drm_mm.h> #include "../lib/drm_random.h" #define TESTS "drm_mm_selftests.h" #include "drm_selftest.h" static unsigned int random_seed; static unsigned int max_iterations = 8192; static unsigned int max_prime = 128; enum { BEST, BOTTOMUP, TOPDOWN, EVICT, }; static const struct insert_mode { const char *name; enum drm_mm_insert_mode mode; } insert_modes[] = { [BEST] = { "best", DRM_MM_INSERT_BEST }, [BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW }, [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH }, [EVICT] = { "evict", DRM_MM_INSERT_EVICT }, {} }, evict_modes[] = { { "bottom-up", DRM_MM_INSERT_LOW }, { "top-down", DRM_MM_INSERT_HIGH }, {} }; static int igt_sanitycheck(void *ignored) { pr_info("%s - ok!\n", __func__); return 0; } static bool assert_no_holes(const struct drm_mm *mm) { struct drm_mm_node *hole; u64 hole_start, hole_end; unsigned long count; count = 0; drm_mm_for_each_hole(hole, mm, hole_start, hole_end) count++; if (count) { pr_err("Expected to find no holes (after reserve), found %lu instead\n", count); return false; } drm_mm_for_each_node(hole, mm) { if (drm_mm_hole_follows(hole)) { pr_err("Hole follows node, expected none!\n"); return false; } } return true; } static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end) { struct drm_mm_node *hole; u64 hole_start, hole_end; unsigned long count; bool ok = true; if (end <= start) return true; count = 0; drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { if (start != hole_start || end != hole_end) { if (ok) pr_err("empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n", hole_start, hole_end, start, end); ok = false; } count++; } if (count != 1) { pr_err("Expected to find one hole, found %lu instead\n", count); ok = false; } return ok; } static bool assert_continuous(const struct drm_mm *mm, u64 size) { struct drm_mm_node *node, *check, *found; unsigned long n; u64 addr; if (!assert_no_holes(mm)) return false; n = 0; addr = 0; drm_mm_for_each_node(node, mm) { if (node->start != addr) { pr_err("node[%ld] list out of order, expected %llx found %llx\n", n, addr, node->start); return false; } if (node->size != size) { pr_err("node[%ld].size incorrect, expected %llx, found %llx\n", n, size, node->size); return false; } if (drm_mm_hole_follows(node)) { pr_err("node[%ld] is followed by a hole!\n", n); return false; } found = NULL; drm_mm_for_each_node_in_range(check, mm, addr, addr + size) { if (node != check) { pr_err("lookup return wrong node, expected start %llx, found %llx\n", node->start, check->start); return false; } found = check; } if (!found) { pr_err("lookup failed for node %llx + %llx\n", addr, size); return false; } addr += size; n++; } return true; } static u64 misalignment(struct drm_mm_node *node, u64 alignment) { u64 rem; if (!alignment) return 0; div64_u64_rem(node->start, alignment, &rem); return rem; } static bool assert_node(struct drm_mm_node *node, struct drm_mm *mm, u64 size, u64 alignment, unsigned long color) { bool ok = true; if (!drm_mm_node_allocated(node) || node->mm != mm) { pr_err("node not allocated\n"); ok = false; } if (node->size != size) { pr_err("node has wrong size, found %llu, expected %llu\n", node->size, size); ok = false; } if (misalignment(node, alignment)) { pr_err("node is misalinged, start %llx rem %llu, expected alignment %llu\n", node->start, misalignment(node, alignment), alignment); ok = false; } if (node->color != color) { pr_err("node has wrong color, found %lu, expected %lu\n", node->color, color); ok = false; } return ok; } #define show_mm(mm) do { \ struct drm_printer __p = drm_debug_printer(__func__); \ drm_mm_print((mm), &__p); } while (0) static int igt_init(void *ignored) { const unsigned int size = 4096; struct drm_mm mm; struct drm_mm_node tmp; int ret = -EINVAL; /* Start with some simple checks on initialising the struct drm_mm */ memset(&mm, 0, sizeof(mm)); if (drm_mm_initialized(&mm)) { pr_err("zeroed mm claims to be initialized\n"); return ret; } memset(&mm, 0xff, sizeof(mm)); drm_mm_init(&mm, 0, size); if (!drm_mm_initialized(&mm)) { pr_err("mm claims not to be initialized\n"); goto out; } if (!drm_mm_clean(&mm)) { pr_err("mm not empty on creation\n"); goto out; } /* After creation, it should all be one massive hole */ if (!assert_one_hole(&mm, 0, size)) { ret = -EINVAL; goto out; } memset(&tmp, 0, sizeof(tmp)); tmp.start = 0; tmp.size = size; ret = drm_mm_reserve_node(&mm, &tmp); if (ret) { pr_err("failed to reserve whole drm_mm\n"); goto out; } /* After filling the range entirely, there should be no holes */ if (!assert_no_holes(&mm)) { ret = -EINVAL; goto out; } /* And then after emptying it again, the massive hole should be back */ drm_mm_remove_node(&tmp); if (!assert_one_hole(&mm, 0, size)) { ret = -EINVAL; goto out; } out: if (ret) show_mm(&mm); drm_mm_takedown(&mm); return ret; } static int igt_debug(void *ignored) { struct drm_mm mm; struct drm_mm_node nodes[2]; int ret; /* Create a small drm_mm with a couple of nodes and a few holes, and * check that the debug iterator doesn't explode over a trivial drm_mm. */ drm_mm_init(&mm, 0, 4096); memset(nodes, 0, sizeof(nodes)); nodes[0].start = 512; nodes[0].size = 1024; ret = drm_mm_reserve_node(&mm, &nodes[0]); if (ret) { pr_err("failed to reserve node[0] {start=%lld, size=%lld)\n", nodes[0].start, nodes[0].size); return ret; } nodes[1].size = 1024; nodes[1].start = 4096 - 512 - nodes[1].size; ret = drm_mm_reserve_node(&mm, &nodes[1]); if (ret) { pr_err("failed to reserve node[1] {start=%lld, size=%lld)\n", nodes[1].start, nodes[1].size); return ret; } show_mm(&mm); return 0; } static struct drm_mm_node *set_node(struct drm_mm_node *node, u64 start, u64 size) { node->start = start; node->size = size; return node; } static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node) { int err; err = drm_mm_reserve_node(mm, node); if (likely(err == -ENOSPC)) return true; if (!err) { pr_err("impossible reserve succeeded, node %llu + %llu\n", node->start, node->size); drm_mm_remove_node(node); } else { pr_err("impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n", err, -ENOSPC, node->start, node->size); } return false; } static bool check_reserve_boundaries(struct drm_mm *mm, unsigned int count, u64 size) { const struct boundary { u64 start, size; const char *name; } boundaries[] = { #define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" } B(0, 0), B(-size, 0), B(size, 0), B(size * count, 0), B(-size, size), B(-size, -size), B(-size, 2*size), B(0, -size), B(size, -size), B(count*size, size), B(count*size, -size), B(count*size, count*size), B(count*size, -count*size), B(count*size, -(count+1)*size), B((count+1)*size, size), B((count+1)*size, -size), B((count+1)*size, -2*size), #undef B }; struct drm_mm_node tmp = {}; int n; for (n = 0; n < ARRAY_SIZE(boundaries); n++) { if (!expect_reserve_fail(mm, set_node(&tmp, boundaries[n].start, boundaries[n].size))) { pr_err("boundary[%d:%s] failed, count=%u, size=%lld\n", n, boundaries[n].name, count, size); return false; } } return true; } static int __igt_reserve(unsigned int count, u64 size) { DRM_RND_STATE(prng, random_seed); struct drm_mm mm; struct drm_mm_node tmp, *nodes, *node, *next; unsigned int *order, n, m, o = 0; int ret, err; /* For exercising drm_mm_reserve_node(), we want to check that * reservations outside of the drm_mm range are rejected, and to * overlapping and otherwise already occupied ranges. Afterwards, * the tree and nodes should be intact. */ DRM_MM_BUG_ON(!count); DRM_MM_BUG_ON(!size); ret = -ENOMEM; order = drm_random_order(count, &prng); if (!order) goto err; nodes = vzalloc(sizeof(*nodes) * count); if (!nodes) goto err_order; ret = -EINVAL; drm_mm_init(&mm, 0, count * size); if (!check_reserve_boundaries(&mm, count, size)) goto out; for (n = 0; n < count; n++) { nodes[n].start = order[n] * size; nodes[n].size = size; err = drm_mm_reserve_node(&mm, &nodes[n]); if (err) { pr_err("reserve failed, step %d, start %llu\n", n, nodes[n].start); ret = err; goto out; } if (!drm_mm_node_allocated(&nodes[n])) { pr_err("reserved node not allocated! step %d, start %llu\n", n, nodes[n].start); goto out; } if (!expect_reserve_fail(&mm, &nodes[n])) goto out; } /* After random insertion the nodes should be in order */ if (!assert_continuous(&mm, size)) goto out; /* Repeated use should then fail */ drm_random_reorder(order, count, &prng); for (n = 0; n < count; n++) { if (!expect_reserve_fail(&mm, set_node(&tmp, order[n] * size, 1))) goto out; /* Remove and reinsert should work */ drm_mm_remove_node(&nodes[order[n]]); err = drm_mm_reserve_node(&mm, &nodes[order[n]]); if (err) { pr_err("reserve failed, step %d, start %llu\n", n, nodes[n].start); ret = err; goto out; } } if (!assert_continuous(&mm, size)) goto out; /* Overlapping use should then fail */ for (n = 0; n < count; n++) { if (!expect_reserve_fail(&mm, set_node(&tmp, 0, size*count))) goto out; } for (n = 0; n < count; n++) { if (!expect_reserve_fail(&mm, set_node(&tmp, size * n, size * (count - n)))) goto out; } /* Remove several, reinsert, check full */ for_each_prime_number(n, min(max_prime, count)) { for (m = 0; m < n; m++) { node = &nodes[order[(o + m) % count]]; drm_mm_remove_node(node); } for (m = 0; m < n; m++) { node = &nodes[order[(o + m) % count]]; err = drm_mm_reserve_node(&mm, node); if (err) { pr_err("reserve failed, step %d/%d, start %llu\n", m, n, node->start); ret = err; goto out; } } o += n; if (!assert_continuous(&mm, size)) goto out; } ret = 0; out: drm_mm_for_each_node_safe(node, next, &mm) drm_mm_remove_node(node); drm_mm_takedown(&mm); vfree(nodes); err_order: kfree(order); err: return ret; } static int igt_reserve(void *ignored) { const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); int n, ret; for_each_prime_number_from(n, 1, 54) { u64 size = BIT_ULL(n); ret = __igt_reserve(count, size - 1); if (ret) return ret; ret = __igt_reserve(count, size); if (ret) return ret; ret = __igt_reserve(count, size + 1); if (ret) return ret; } return 0; } static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color, const struct insert_mode *mode) { int err; err = drm_mm_insert_node_generic(mm, node, size, alignment, color, mode->mode); if (err) { pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n", size, alignment, color, mode->name, err); return false; } if (!assert_node(node, mm, size, alignment, color)) { drm_mm_remove_node(node); return false; } return true; } static bool expect_insert_fail(struct drm_mm *mm, u64 size) { struct drm_mm_node tmp = {}; int err; err = drm_mm_insert_node(mm, &tmp, size); if (likely(err == -ENOSPC)) return true; if (!err) { pr_err("impossible insert succeeded, node %llu + %llu\n", tmp.start, tmp.size); drm_mm_remove_node(&tmp); } else { pr_err("impossible insert failed with wrong error %d [expected %d], size %llu\n", err, -ENOSPC, size); } return false; } static int __igt_insert(unsigned int count, u64 size, bool replace) { DRM_RND_STATE(prng, random_seed); const struct insert_mode *mode; struct drm_mm mm; struct drm_mm_node *nodes, *node, *next; unsigned int *order, n, m, o = 0; int ret; /* Fill a range with lots of nodes, check it doesn't fail too early */ DRM_MM_BUG_ON(!count); DRM_MM_BUG_ON(!size); ret = -ENOMEM; nodes = vmalloc(count * sizeof(*nodes)); if (!nodes) goto err; order = drm_random_order(count, &prng); if (!order) goto err_nodes; ret = -EINVAL; drm_mm_init(&mm, 0, count * size); for (mode = insert_modes; mode->name; mode++) { for (n = 0; n < count; n++) { struct drm_mm_node tmp; node = replace ? &tmp : &nodes[n]; memset(node, 0, sizeof(*node)); if (!expect_insert(&mm, node, size, 0, n, mode)) { pr_err("%s insert failed, size %llu step %d\n", mode->name, size, n); goto out; } if (replace) { drm_mm_replace_node(&tmp, &nodes[n]); if (drm_mm_node_allocated(&tmp)) { pr_err("replaced old-node still allocated! step %d\n", n); goto out; } if (!assert_node(&nodes[n], &mm, size, 0, n)) { pr_err("replaced node did not inherit parameters, size %llu step %d\n", size, n); goto out; } if (tmp.start != nodes[n].start) { pr_err("replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n", tmp.start, size, nodes[n].start, nodes[n].size); goto out; } } } /* After random insertion the nodes should be in order */ if (!assert_continuous(&mm, size)) goto out; /* Repeated use should then fail */ if (!expect_insert_fail(&mm, size)) goto out; /* Remove one and reinsert, as the only hole it should refill itself */ for (n = 0; n < count; n++) { u64 addr = nodes[n].start; drm_mm_remove_node(&nodes[n]); if (!expect_insert(&mm, &nodes[n], size, 0, n, mode)) { pr_err("%s reinsert failed, size %llu step %d\n", mode->name, size, 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_continuous(&mm, size)) goto out; } /* Remove several, reinsert, check full */ for_each_prime_number(n, min(max_prime, count)) { for (m = 0; m < n; m++) { node = &nodes[order[(o + m) % count]]; drm_mm_remove_node(node); } for (m = 0; m < n; m++) { node = &nodes[order[(o + m) % count]]; if (!expect_insert(&mm, node, size, 0, n, mode)) { pr_err("%s multiple reinsert failed, size %llu step %d\n", mode->name, size, n); goto out; } } o += n; if (!assert_continuous(&mm, size)) goto out; if (!expect_insert_fail(&mm, 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); kfree(order); err_nodes: vfree(nodes); err: return ret; } static int igt_insert(void *ignored) { const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); unsigned int n; int ret; for_each_prime_number_from(n, 1, 54) { u64 size = BIT_ULL(n); ret = __igt_insert(count, size - 1, false); if (ret) return ret; ret = __igt_insert(count, size, false); if (ret) return ret; ret = __igt_insert(count, size + 1, false); } return 0; } static int igt_replace(void *ignored) { const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); unsigned int n; int ret; /* Reuse igt_insert to exercise replacement by inserting a dummy node, * then replacing it with the intended node. We want to check that * the tree is intact and all the information we need is carried * across to the target node. */ for_each_prime_number_from(n, 1, 54) { u64 size = BIT_ULL(n); ret = __igt_insert(count, size - 1, true); if (ret) return ret; ret = __igt_insert(count, size, true); if (ret) return ret; ret = __igt_insert(count, size + 1, true); } 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(mm, node, size, alignment, color, range_start, range_end, mode->mode); 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(mm, &tmp, size, 0, 0, range_start, range_end, 0); 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 (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) { pr_err("node %d is followed by a hole!\n", n); return false; } n++; } if (start > 0) { node = __drm_mm_interval_first(mm, 0, start - 1); if (node->allocated) { pr_err("node before start: node=%llx+%llu, start=%llx\n", node->start, node->size, start); return false; } } if (end < U64_MAX) { node = __drm_mm_interval_first(mm, end, U64_MAX); if (node->allocated) { 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; } static int igt_align(void *ignored) { const struct insert_mode *mode; const unsigned int max_count = min(8192u, max_prime); struct drm_mm mm; struct drm_mm_node *nodes, *node, *next; unsigned int prime; int ret = -EINVAL; /* For each of the possible insertion modes, we pick a few * arbitrary alignments and check that the inserted node * meets our requirements. */ nodes = vzalloc(max_count * sizeof(*nodes)); if (!nodes) goto err; drm_mm_init(&mm, 1, U64_MAX - 2); for (mode = insert_modes; mode->name; mode++) { unsigned int i = 0; for_each_prime_number_from(prime, 1, max_count) { u64 size = next_prime_number(prime); if (!expect_insert(&mm, &nodes[i], size, prime, i, mode)) { pr_err("%s insert failed with alignment=%d", mode->name, prime); goto out; } i++; } 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 igt_align_pot(int max) { struct drm_mm mm; struct drm_mm_node *node, *next; int bit; int ret = -EINVAL; /* Check that we can align to the full u64 address space */ drm_mm_init(&mm, 1, U64_MAX - 2); for (bit = max - 1; bit; bit--) { u64 align, size; node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) { ret = -ENOMEM; goto out; } align = BIT_ULL(bit); size = BIT_ULL(bit-1) + 1; if (!expect_insert(&mm, node, size, align, bit, &insert_modes[0])) { pr_err("insert failed with alignment=%llx [%d]", align, bit); goto out; } } ret = 0; out: drm_mm_for_each_node_safe(node, next, &mm) { drm_mm_remove_node(node); kfree(node); } drm_mm_takedown(&mm); return ret; } static int igt_align32(void *ignored) { return igt_align_pot(32); } static int igt_align64(void *ignored) { return igt_align_pot(64); } static void show_scan(const struct drm_mm_scan *scan) { pr_info("scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n", scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color); } static void show_holes(const struct drm_mm *mm, int count) { u64 hole_start, hole_end; struct drm_mm_node *hole; drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { struct drm_mm_node *next = list_next_entry(hole, node_list); const char *node1 = NULL, *node2 = NULL; if (hole->allocated) node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ", hole->start, hole->size, hole->color); if (next->allocated) node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]", next->start, next->size, next->color); pr_info("%sHole [%llx - %llx, size %lld]%s\n", node1, hole_start, hole_end, hole_end - hole_start, node2); kfree(node2); kfree(node1); if (!--count) break; } } struct evict_node { struct drm_mm_node node; struct list_head link; }; static bool evict_nodes(struct drm_mm_scan *scan, struct evict_node *nodes, unsigned int *order, unsigned int count, bool use_color, struct list_head *evict_list) { struct evict_node *e, *en; unsigned int i; for (i = 0; i < count; i++) { e = &nodes[order ? order[i] : i]; list_add(&e->link, evict_list); if (drm_mm_scan_add_block(scan, &e->node)) break; } list_for_each_entry_safe(e, en, evict_list, link) { if (!drm_mm_scan_remove_block(scan, &e->node)) list_del(&e->link); } if (list_empty(evict_list)) { pr_err("Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n", scan->size, count, scan->alignment, scan->color); return false; } list_for_each_entry(e, evict_list, link) drm_mm_remove_node(&e->node); if (use_color) { struct drm_mm_node *node; while ((node = drm_mm_scan_color_evict(scan))) { e = container_of(node, typeof(*e), node); drm_mm_remove_node(&e->node); list_add(&e->link, evict_list); } } else { if (drm_mm_scan_color_evict(scan)) { pr_err("drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); return false; } } return true; } static bool evict_nothing(struct drm_mm *mm, unsigned int total_size, struct evict_node *nodes) { struct drm_mm_scan scan; LIST_HEAD(evict_list); struct evict_node *e; struct drm_mm_node *node; unsigned int n; drm_mm_scan_init(&scan, mm, 1, 0, 0, 0); for (n = 0; n < total_size; n++) { e = &nodes[n]; list_add(&e->link, &evict_list); drm_mm_scan_add_block(&scan, &e->node); } list_for_each_entry(e, &evict_list, link) drm_mm_scan_remove_block(&scan, &e->node); for (n = 0; n < total_size; n++) { e = &nodes[n]; if (!drm_mm_node_allocated(&e->node)) { pr_err("node[%d] no longer allocated!\n", n); return false; } e->link.next = NULL; } drm_mm_for_each_node(node, mm) { e = container_of(node, typeof(*e), node); e->link.next = &e->link; } for (n = 0; n < total_size; n++) { e = &nodes[n]; if (!e->link.next) { pr_err("node[%d] no longer connected!\n", n); return false; } } return assert_continuous(mm, nodes[0].node.size); } static bool evict_everything(struct drm_mm *mm, unsigned int total_size, struct evict_node *nodes) { struct drm_mm_scan scan; LIST_HEAD(evict_list); struct evict_node *e; unsigned int n; int err; drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0); for (n = 0; n < total_size; n++) { e = &nodes[n]; list_add(&e->link, &evict_list); if (drm_mm_scan_add_block(&scan, &e->node)) break; } err = 0; list_for_each_entry(e, &evict_list, link) { if (!drm_mm_scan_remove_block(&scan, &e->node)) { if (!err) { pr_err("Node %lld not marked for eviction!\n", e->node.start); err = -EINVAL; } } } if (err) return false; list_for_each_entry(e, &evict_list, link) drm_mm_remove_node(&e->node); if (!assert_one_hole(mm, 0, total_size)) return false; list_for_each_entry(e, &evict_list, link) { err = drm_mm_reserve_node(mm, &e->node); if (err) { pr_err("Failed to reinsert node after eviction: start=%llx\n", e->node.start); return false; } } return assert_continuous(mm, nodes[0].node.size); } static int evict_something(struct drm_mm *mm, u64 range_start, u64 range_end, struct evict_node *nodes, unsigned int *order, unsigned int count, unsigned int size, unsigned int alignment, const struct insert_mode *mode) { struct drm_mm_scan scan; LIST_HEAD(evict_list); struct evict_node *e; struct drm_mm_node tmp; int err; drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start, range_end, mode->mode); if (!evict_nodes(&scan, nodes, order, count, false, &evict_list)) return -EINVAL; memset(&tmp, 0, sizeof(tmp)); err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, DRM_MM_INSERT_EVICT); if (err) { pr_err("Failed to insert into eviction hole: size=%d, align=%d\n", size, alignment); show_scan(&scan); show_holes(mm, 3); return err; } if (tmp.start < range_start || tmp.start + tmp.size > range_end) { pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", tmp.start, tmp.size, range_start, range_end); err = -EINVAL; } if (!assert_node(&tmp, mm, size, alignment, 0) || drm_mm_hole_follows(&tmp)) { pr_err("Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n", tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start, drm_mm_hole_follows(&tmp)); err = -EINVAL; } drm_mm_remove_node(&tmp); if (err) return err; list_for_each_entry(e, &evict_list, link) { err = drm_mm_reserve_node(mm, &e->node); if (err) { pr_err("Failed to reinsert node after eviction: start=%llx\n", e->node.start); return err; } } if (!assert_continuous(mm, nodes[0].node.size)) { pr_err("range is no longer continuous\n"); return -EINVAL; } return 0; } static int igt_evict(void *ignored) { DRM_RND_STATE(prng, random_seed); const unsigned int size = 8192; const struct insert_mode *mode; struct drm_mm mm; struct evict_node *nodes; struct drm_mm_node *node, *next; unsigned int *order, n; int ret, err; /* Here we populate a full drm_mm and then try and insert a new node * by evicting other nodes in a random order. The drm_mm_scan should * pick the first matching hole it finds from the random list. We * repeat that for different allocation strategies, alignments and * sizes to try and stress the hole finder. */ ret = -ENOMEM; nodes = vzalloc(size * sizeof(*nodes)); if (!nodes) goto err; order = drm_random_order(size, &prng); if (!order) goto err_nodes; ret = -EINVAL; drm_mm_init(&mm, 0, size); for (n = 0; n < size; n++) { err = drm_mm_insert_node(&mm, &nodes[n].node, 1); if (err) { pr_err("insert failed, step %d\n", n); ret = err; goto out; } } /* First check that using the scanner doesn't break the mm */ if (!evict_nothing(&mm, size, nodes)) { pr_err("evict_nothing() failed\n"); goto out; } if (!evict_everything(&mm, size, nodes)) { pr_err("evict_everything() failed\n"); goto out; } for (mode = evict_modes; mode->name; mode++) { for (n = 1; n <= size; n <<= 1) { drm_random_reorder(order, size, &prng); err = evict_something(&mm, 0, U64_MAX, nodes, order, size, n, 1, mode); if (err) { pr_err("%s evict_something(size=%u) failed\n", mode->name, n); ret = err; goto out; } } for (n = 1; n < size; n <<= 1) { drm_random_reorder(order, size, &prng); err = evict_something(&mm, 0, U64_MAX, nodes, order, size, size/2, n, mode); if (err) { pr_err("%s evict_something(size=%u, alignment=%u) failed\n", mode->name, size/2, n); ret = err; goto out; } } for_each_prime_number_from(n, 1, min(size, max_prime)) { unsigned int nsize = (size - n + 1) / 2; DRM_MM_BUG_ON(!nsize); drm_random_reorder(order, size, &prng); err = evict_something(&mm, 0, U64_MAX, nodes, order, size, nsize, n, mode); if (err) { pr_err("%s evict_something(size=%u, alignment=%u) failed\n", mode->name, nsize, n); ret = err; goto out; } } } ret = 0; out: drm_mm_for_each_node_safe(node, next, &mm) drm_mm_remove_node(node); drm_mm_takedown(&mm); kfree(order); err_nodes: vfree(nodes); err: return ret; } static int igt_evict_range(void *ignored) { DRM_RND_STATE(prng, random_seed); const unsigned int size = 8192; const unsigned int range_size = size / 2; const unsigned int range_start = size / 4; const unsigned int range_end = range_start + range_size; const struct insert_mode *mode; struct drm_mm mm; struct evict_node *nodes; struct drm_mm_node *node, *next; unsigned int *order, n; int ret, err; /* Like igt_evict() but now we are limiting the search to a * small portion of the full drm_mm. */ ret = -ENOMEM; nodes = vzalloc(size * sizeof(*nodes)); if (!nodes) goto err; order = drm_random_order(size, &prng); if (!order) goto err_nodes; ret = -EINVAL; drm_mm_init(&mm, 0, size); for (n = 0; n < size; n++) { err = drm_mm_insert_node(&mm, &nodes[n].node, 1); if (err) { pr_err("insert failed, step %d\n", n); ret = err; goto out; } } for (mode = evict_modes; mode->name; mode++) { for (n = 1; n <= range_size; n <<= 1) { drm_random_reorder(order, size, &prng); err = evict_something(&mm, range_start, range_end, nodes, order, size, n, 1, mode); if (err) { pr_err("%s evict_something(size=%u) failed with range [%u, %u]\n", mode->name, n, range_start, range_end); goto out; } } for (n = 1; n <= range_size; n <<= 1) { drm_random_reorder(order, size, &prng); err = evict_something(&mm, range_start, range_end, nodes, order, size, range_size/2, n, mode); if (err) { pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", mode->name, range_size/2, n, range_start, range_end); goto out; } } for_each_prime_number_from(n, 1, min(range_size, max_prime)) { unsigned int nsize = (range_size - n + 1) / 2; DRM_MM_BUG_ON(!nsize); drm_random_reorder(order, size, &prng); err = evict_something(&mm, range_start, range_end, nodes, order, size, nsize, n, mode); if (err) { pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", mode->name, nsize, n, range_start, range_end); goto out; } } } ret = 0; out: drm_mm_for_each_node_safe(node, next, &mm) drm_mm_remove_node(node); drm_mm_takedown(&mm); kfree(order); err_nodes: vfree(nodes); err: return ret; } static unsigned int node_index(const struct drm_mm_node *node) { return div64_u64(node->start, node->size); } static int igt_topdown(void *ignored) { const struct insert_mode *topdown = &insert_modes[TOPDOWN]; DRM_RND_STATE(prng, random_seed); const unsigned int count = 8192; unsigned int size; unsigned long *bitmap = NULL; struct drm_mm mm; struct drm_mm_node *nodes, *node, *next; unsigned int *order, n, m, o = 0; int ret; /* When allocating top-down, we expect to be returned a node * from a suitable hole at the top of the drm_mm. We check that * the returned node does match the highest available slot. */ ret = -ENOMEM; nodes = vzalloc(count * sizeof(*nodes)); if (!nodes) goto err; bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), GFP_TEMPORARY); if (!bitmap) goto err_nodes; order = drm_random_order(count, &prng); if (!order) goto err_bitmap; ret = -EINVAL; for (size = 1; size <= 64; size <<= 1) { drm_mm_init(&mm, 0, size*count); for (n = 0; n < count; n++) { if (!expect_insert(&mm, &nodes[n], size, 0, n, topdown)) { pr_err("insert failed, size %u step %d\n", size, n); goto out; } if (drm_mm_hole_follows(&nodes[n])) { pr_err("hole after topdown insert %d, start=%llx\n, size=%u", n, nodes[n].start, size); goto out; } if (!assert_one_hole(&mm, 0, size*(count - n - 1))) goto out; } if (!assert_continuous(&mm, size)) goto out; drm_random_reorder(order, count, &prng); for_each_prime_number_from(n, 1, min(count, max_prime)) { for (m = 0; m < n; m++) { node = &nodes[order[(o + m) % count]]; drm_mm_remove_node(node); __set_bit(node_index(node), bitmap); } for (m = 0; m < n; m++) { unsigned int last; node = &nodes[order[(o + m) % count]]; if (!expect_insert(&mm, node, size, 0, 0, topdown)) { pr_err("insert failed, step %d/%d\n", m, n); goto out; } if (drm_mm_hole_follows(node)) { pr_err("hole after topdown insert %d/%d, start=%llx\n", m, n, node->start); goto out; } last = find_last_bit(bitmap, count); if (node_index(node) != last) { pr_err("node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n", m, n, size, last, node_index(node)); goto out; } __clear_bit(last, bitmap); } DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); o += n; } 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); kfree(order); err_bitmap: kfree(bitmap); err_nodes: vfree(nodes); err: return ret; } static int igt_bottomup(void *ignored) { const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; DRM_RND_STATE(prng, random_seed); const unsigned int count = 8192; unsigned int size; unsigned long *bitmap; struct drm_mm mm; struct drm_mm_node *nodes, *node, *next; unsigned int *order, n, m, o = 0; int ret; /* Like igt_topdown, but instead of searching for the last hole, * we search for the first. */ ret = -ENOMEM; nodes = vzalloc(count * sizeof(*nodes)); if (!nodes) goto err; bitmap = kzalloc(count / BITS_PER_LONG * sizeof(unsigned long), GFP_TEMPORARY); if (!bitmap) goto err_nodes; order = drm_random_order(count, &prng); if (!order) goto err_bitmap; ret = -EINVAL; for (size = 1; size <= 64; size <<= 1) { drm_mm_init(&mm, 0, size*count); for (n = 0; n < count; n++) { if (!expect_insert(&mm, &nodes[n], size, 0, n, bottomup)) { pr_err("bottomup insert failed, size %u step %d\n", size, n); goto out; } if (!assert_one_hole(&mm, size*(n + 1), size*count)) goto out; } if (!assert_continuous(&mm, size)) goto out; drm_random_reorder(order, count, &prng); for_each_prime_number_from(n, 1, min(count, max_prime)) { for (m = 0; m < n; m++) { node = &nodes[order[(o + m) % count]]; drm_mm_remove_node(node); __set_bit(node_index(node), bitmap); } for (m = 0; m < n; m++) { unsigned int first; node = &nodes[order[(o + m) % count]]; if (!expect_insert(&mm, node, size, 0, 0, bottomup)) { pr_err("insert failed, step %d/%d\n", m, n); goto out; } first = find_first_bit(bitmap, count); if (node_index(node) != first) { pr_err("node %d/%d not inserted into bottom hole, expected %d, found %d\n", m, n, first, node_index(node)); goto out; } __clear_bit(first, bitmap); } DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); o += n; } 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); kfree(order); err_bitmap: kfree(bitmap); err_nodes: vfree(nodes); err: return ret; } static void separate_adjacent_colors(const struct drm_mm_node *node, unsigned long color, u64 *start, u64 *end) { if (node->allocated && node->color != color) ++*start; node = list_next_entry(node, node_list); if (node->allocated && node->color != color) --*end; } static bool colors_abutt(const struct drm_mm_node *node) { if (!drm_mm_hole_follows(node) && list_next_entry(node, node_list)->allocated) { pr_err("colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n", node->color, node->start, node->size, list_next_entry(node, node_list)->color, list_next_entry(node, node_list)->start, list_next_entry(node, node_list)->size); return true; } return false; } static int igt_color(void *ignored) { const unsigned int count = min(4096u, max_iterations); const struct insert_mode *mode; struct drm_mm mm; struct drm_mm_node *node, *nn; unsigned int n; int ret = -EINVAL, err; /* Color adjustment complicates everything. First we just check * that when we insert a node we apply any color_adjustment callback. * The callback we use should ensure that there is a gap between * any two nodes, and so after each insertion we check that those * holes are inserted and that they are preserved. */ drm_mm_init(&mm, 0, U64_MAX); for (n = 1; n <= count; n++) { node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) { ret = -ENOMEM; goto out; } if (!expect_insert(&mm, node, n, 0, n, &insert_modes[0])) { pr_err("insert failed, step %d\n", n); kfree(node); goto out; } } drm_mm_for_each_node_safe(node, nn, &mm) { if (node->color != node->size) { pr_err("invalid color stored: expected %lld, found %ld\n", node->size, node->color); goto out; } drm_mm_remove_node(node); kfree(node); } /* Now, let's start experimenting with applying a color callback */ mm.color_adjust = separate_adjacent_colors; for (mode = insert_modes; mode->name; mode++) { u64 last; node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) { ret = -ENOMEM; goto out; } node->size = 1 + 2*count; node->color = node->size; err = drm_mm_reserve_node(&mm, node); if (err) { pr_err("initial reserve failed!\n"); ret = err; goto out; } last = node->start + node->size; for (n = 1; n <= count; n++) { int rem; node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) { ret = -ENOMEM; goto out; } node->start = last; node->size = n + count; node->color = node->size; err = drm_mm_reserve_node(&mm, node); if (err != -ENOSPC) { pr_err("reserve %d did not report color overlap! err=%d\n", n, err); goto out; } node->start += n + 1; rem = misalignment(node, n + count); node->start += n + count - rem; err = drm_mm_reserve_node(&mm, node); if (err) { pr_err("reserve %d failed, err=%d\n", n, err); ret = err; goto out; } last = node->start + node->size; } for (n = 1; n <= count; n++) { node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) { ret = -ENOMEM; goto out; } if (!expect_insert(&mm, node, n, n, n, mode)) { pr_err("%s insert failed, step %d\n", mode->name, n); kfree(node); goto out; } } drm_mm_for_each_node_safe(node, nn, &mm) { u64 rem; if (node->color != node->size) { pr_err("%s invalid color stored: expected %lld, found %ld\n", mode->name, node->size, node->color); goto out; } if (colors_abutt(node)) goto out; div64_u64_rem(node->start, node->size, &rem); if (rem) { pr_err("%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n", mode->name, node->start, node->size, rem); goto out; } drm_mm_remove_node(node); kfree(node); } } ret = 0; out: drm_mm_for_each_node_safe(node, nn, &mm) { drm_mm_remove_node(node); kfree(node); } drm_mm_takedown(&mm); return ret; } static int evict_color(struct drm_mm *mm, u64 range_start, u64 range_end, struct evict_node *nodes, unsigned int *order, unsigned int count, unsigned int size, unsigned int alignment, unsigned long color, const struct insert_mode *mode) { struct drm_mm_scan scan; LIST_HEAD(evict_list); struct evict_node *e; struct drm_mm_node tmp; int err; drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start, range_end, mode->mode); if (!evict_nodes(&scan, nodes, order, count, true, &evict_list)) return -EINVAL; memset(&tmp, 0, sizeof(tmp)); err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, DRM_MM_INSERT_EVICT); if (err) { pr_err("Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", size, alignment, color, err); show_scan(&scan); show_holes(mm, 3); return err; } if (tmp.start < range_start || tmp.start + tmp.size > range_end) { pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", tmp.start, tmp.size, range_start, range_end); err = -EINVAL; } if (colors_abutt(&tmp)) err = -EINVAL; if (!assert_node(&tmp, mm, size, alignment, color)) { pr_err("Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n", tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start); err = -EINVAL; } drm_mm_remove_node(&tmp); if (err) return err; list_for_each_entry(e, &evict_list, link) { err = drm_mm_reserve_node(mm, &e->node); if (err) { pr_err("Failed to reinsert node after eviction: start=%llx\n", e->node.start); return err; } } return 0; } static int igt_color_evict(void *ignored) { DRM_RND_STATE(prng, random_seed); const unsigned int total_size = min(8192u, max_iterations); const struct insert_mode *mode; unsigned long color = 0; struct drm_mm mm; struct evict_node *nodes; struct drm_mm_node *node, *next; unsigned int *order, n; int ret, err; /* Check that the drm_mm_scan also honours color adjustment when * choosing its victims to create a hole. Our color_adjust does not * allow two nodes to be placed together without an intervening hole * enlarging the set of victims that must be evicted. */ ret = -ENOMEM; nodes = vzalloc(total_size * sizeof(*nodes)); if (!nodes) goto err; order = drm_random_order(total_size, &prng); if (!order) goto err_nodes; ret = -EINVAL; drm_mm_init(&mm, 0, 2*total_size - 1); mm.color_adjust = separate_adjacent_colors; for (n = 0; n < total_size; n++) { if (!expect_insert(&mm, &nodes[n].node, 1, 0, color++, &insert_modes[0])) { pr_err("insert failed, step %d\n", n); goto out; } } for (mode = evict_modes; mode->name; mode++) { for (n = 1; n <= total_size; n <<= 1) { drm_random_reorder(order, total_size, &prng); err = evict_color(&mm, 0, U64_MAX, nodes, order, total_size, n, 1, color++, mode); if (err) { pr_err("%s evict_color(size=%u) failed\n", mode->name, n); goto out; } } for (n = 1; n < total_size; n <<= 1) { drm_random_reorder(order, total_size, &prng); err = evict_color(&mm, 0, U64_MAX, nodes, order, total_size, total_size/2, n, color++, mode); if (err) { pr_err("%s evict_color(size=%u, alignment=%u) failed\n", mode->name, total_size/2, n); goto out; } } for_each_prime_number_from(n, 1, min(total_size, max_prime)) { unsigned int nsize = (total_size - n + 1) / 2; DRM_MM_BUG_ON(!nsize); drm_random_reorder(order, total_size, &prng); err = evict_color(&mm, 0, U64_MAX, nodes, order, total_size, nsize, n, color++, mode); if (err) { pr_err("%s evict_color(size=%u, alignment=%u) failed\n", mode->name, nsize, n); goto out; } } } ret = 0; out: if (ret) show_mm(&mm); drm_mm_for_each_node_safe(node, next, &mm) drm_mm_remove_node(node); drm_mm_takedown(&mm); kfree(order); err_nodes: vfree(nodes); err: return ret; } static int igt_color_evict_range(void *ignored) { DRM_RND_STATE(prng, random_seed); const unsigned int total_size = 8192; const unsigned int range_size = total_size / 2; const unsigned int range_start = total_size / 4; const unsigned int range_end = range_start + range_size; const struct insert_mode *mode; unsigned long color = 0; struct drm_mm mm; struct evict_node *nodes; struct drm_mm_node *node, *next; unsigned int *order, n; int ret, err; /* Like igt_color_evict(), but limited to small portion of the full * drm_mm range. */ ret = -ENOMEM; nodes = vzalloc(total_size * sizeof(*nodes)); if (!nodes) goto err; order = drm_random_order(total_size, &prng); if (!order) goto err_nodes; ret = -EINVAL; drm_mm_init(&mm, 0, 2*total_size - 1); mm.color_adjust = separate_adjacent_colors; for (n = 0; n < total_size; n++) { if (!expect_insert(&mm, &nodes[n].node, 1, 0, color++, &insert_modes[0])) { pr_err("insert failed, step %d\n", n); goto out; } } for (mode = evict_modes; mode->name; mode++) { for (n = 1; n <= range_size; n <<= 1) { drm_random_reorder(order, range_size, &prng); err = evict_color(&mm, range_start, range_end, nodes, order, total_size, n, 1, color++, mode); if (err) { pr_err("%s evict_color(size=%u) failed for range [%x, %x]\n", mode->name, n, range_start, range_end); goto out; } } for (n = 1; n < range_size; n <<= 1) { drm_random_reorder(order, total_size, &prng); err = evict_color(&mm, range_start, range_end, nodes, order, total_size, range_size/2, n, color++, mode); if (err) { pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", mode->name, total_size/2, n, range_start, range_end); goto out; } } for_each_prime_number_from(n, 1, min(range_size, max_prime)) { unsigned int nsize = (range_size - n + 1) / 2; DRM_MM_BUG_ON(!nsize); drm_random_reorder(order, total_size, &prng); err = evict_color(&mm, range_start, range_end, nodes, order, total_size, nsize, n, color++, mode); if (err) { pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", mode->name, nsize, n, range_start, range_end); goto out; } } } ret = 0; out: if (ret) show_mm(&mm); drm_mm_for_each_node_safe(node, next, &mm) drm_mm_remove_node(node); drm_mm_takedown(&mm); kfree(order); err_nodes: vfree(nodes); err: return ret; } #include "drm_selftest.c" static int __init test_drm_mm_init(void) { int err; while (!random_seed) random_seed = get_random_int(); pr_info("Testing DRM range manger (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u\n", random_seed, max_iterations, max_prime); err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); return err > 0 ? 0 : err; } static void __exit test_drm_mm_exit(void) { } module_init(test_drm_mm_init); module_exit(test_drm_mm_exit); module_param(random_seed, uint, 0400); module_param(max_iterations, uint, 0400); module_param(max_prime, uint, 0400); MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
You can’t perform that action at this time.