Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,131 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#include "i915_scatterlist.h"
#include "huge_gem_object.h"
static void huge_free_pages(struct drm_i915_gem_object *obj,
struct sg_table *pages)
{
unsigned long nreal = obj->scratch / PAGE_SIZE;
struct sgt_iter sgt_iter;
struct page *page;
for_each_sgt_page(page, sgt_iter, pages) {
__free_page(page);
if (!--nreal)
break;
}
sg_free_table(pages);
kfree(pages);
}
static int huge_get_pages(struct drm_i915_gem_object *obj)
{
#define GFP (GFP_KERNEL | __GFP_NOWARN | __GFP_RETRY_MAYFAIL)
const unsigned long nreal = obj->scratch / PAGE_SIZE;
unsigned int npages; /* restricted by sg_alloc_table */
struct scatterlist *sg, *src, *end;
struct sg_table *pages;
unsigned long n;
if (overflows_type(obj->base.size / PAGE_SIZE, npages))
return -E2BIG;
npages = obj->base.size / PAGE_SIZE;
pages = kmalloc_obj(*pages, GFP);
if (!pages)
return -ENOMEM;
if (sg_alloc_table(pages, npages, GFP)) {
kfree(pages);
return -ENOMEM;
}
sg = pages->sgl;
for (n = 0; n < nreal; n++) {
struct page *page;
page = alloc_page(GFP | __GFP_HIGHMEM);
if (!page) {
sg_mark_end(sg);
goto err;
}
sg_set_page(sg, page, PAGE_SIZE, 0);
sg = __sg_next(sg);
}
if (nreal < npages) {
for (end = sg, src = pages->sgl; sg; sg = __sg_next(sg)) {
sg_set_page(sg, sg_page(src), PAGE_SIZE, 0);
src = __sg_next(src);
if (src == end)
src = pages->sgl;
}
}
if (i915_gem_gtt_prepare_pages(obj, pages))
goto err;
__i915_gem_object_set_pages(obj, pages);
return 0;
err:
huge_free_pages(obj, pages);
return -ENOMEM;
#undef GFP
}
static void huge_put_pages(struct drm_i915_gem_object *obj,
struct sg_table *pages)
{
i915_gem_gtt_finish_pages(obj, pages);
huge_free_pages(obj, pages);
obj->mm.dirty = false;
}
static const struct drm_i915_gem_object_ops huge_ops = {
.name = "huge-gem",
.get_pages = huge_get_pages,
.put_pages = huge_put_pages,
};
struct drm_i915_gem_object *
huge_gem_object(struct drm_i915_private *i915,
phys_addr_t phys_size,
dma_addr_t dma_size)
{
static struct lock_class_key lock_class;
struct drm_i915_gem_object *obj;
unsigned int cache_level;
GEM_BUG_ON(!phys_size || phys_size > dma_size);
GEM_BUG_ON(!IS_ALIGNED(phys_size, PAGE_SIZE));
GEM_BUG_ON(!IS_ALIGNED(dma_size, I915_GTT_PAGE_SIZE));
if (overflows_type(dma_size, obj->base.size))
return ERR_PTR(-E2BIG);
obj = i915_gem_object_alloc();
if (!obj)
return ERR_PTR(-ENOMEM);
drm_gem_private_object_init(&i915->drm, &obj->base, dma_size);
i915_gem_object_init(obj, &huge_ops, &lock_class, 0);
obj->mem_flags |= I915_BO_FLAG_STRUCT_PAGE;
obj->read_domains = I915_GEM_DOMAIN_CPU;
obj->write_domain = I915_GEM_DOMAIN_CPU;
cache_level = HAS_LLC(i915) ? I915_CACHE_LLC : I915_CACHE_NONE;
i915_gem_object_set_cache_coherency(obj, cache_level);
obj->scratch = phys_size;
return obj;
}
@@ -0,0 +1,33 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#ifndef __HUGE_GEM_OBJECT_H
#define __HUGE_GEM_OBJECT_H
#include <linux/types.h>
#include "gem/i915_gem_object_types.h"
struct drm_i915_private;
struct drm_i915_gem_object *
huge_gem_object(struct drm_i915_private *i915,
phys_addr_t phys_size,
dma_addr_t dma_size);
static inline phys_addr_t
huge_gem_object_phys_size(struct drm_i915_gem_object *obj)
{
return obj->scratch;
}
static inline dma_addr_t
huge_gem_object_dma_size(struct drm_i915_gem_object *obj)
{
return obj->base.size;
}
#endif /* !__HUGE_GEM_OBJECT_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,746 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2019 Intel Corporation
*/
#include <drm/drm_print.h>
#include "i915_selftest.h"
#include "display/intel_display_device.h"
#include "gt/intel_context.h"
#include "gt/intel_engine_regs.h"
#include "gt/intel_engine_user.h"
#include "gt/intel_gpu_commands.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_regs.h"
#include "gem/i915_gem_lmem.h"
#include "gem/selftests/igt_gem_utils.h"
#include "selftests/igt_flush_test.h"
#include "selftests/mock_drm.h"
#include "selftests/i915_random.h"
#include "huge_gem_object.h"
#include "mock_context.h"
#define OW_SIZE 16 /* in bytes */
#define F_SUBTILE_SIZE 64 /* in bytes */
#define F_TILE_WIDTH 128 /* in bytes */
#define F_TILE_HEIGHT 32 /* in pixels */
#define F_SUBTILE_WIDTH OW_SIZE /* in bytes */
#define F_SUBTILE_HEIGHT 4 /* in pixels */
static int linear_x_y_to_ftiled_pos(int x, int y, u32 stride, int bpp)
{
int tile_base;
int tile_x, tile_y;
int swizzle, subtile;
int pixel_size = bpp / 8;
int pos;
/*
* Subtile remapping for F tile. Note that map[a]==b implies map[b]==a
* so we can use the same table to tile and until.
*/
static const u8 f_subtile_map[] = {
0, 1, 2, 3, 8, 9, 10, 11,
4, 5, 6, 7, 12, 13, 14, 15,
16, 17, 18, 19, 24, 25, 26, 27,
20, 21, 22, 23, 28, 29, 30, 31,
32, 33, 34, 35, 40, 41, 42, 43,
36, 37, 38, 39, 44, 45, 46, 47,
48, 49, 50, 51, 56, 57, 58, 59,
52, 53, 54, 55, 60, 61, 62, 63
};
x *= pixel_size;
/*
* Where does the 4k tile start (in bytes)? This is the same for Y and
* F so we can use the Y-tile algorithm to get to that point.
*/
tile_base =
y / F_TILE_HEIGHT * stride * F_TILE_HEIGHT +
x / F_TILE_WIDTH * 4096;
/* Find pixel within tile */
tile_x = x % F_TILE_WIDTH;
tile_y = y % F_TILE_HEIGHT;
/* And figure out the subtile within the 4k tile */
subtile = tile_y / F_SUBTILE_HEIGHT * 8 + tile_x / F_SUBTILE_WIDTH;
/* Swizzle the subtile number according to the bspec diagram */
swizzle = f_subtile_map[subtile];
/* Calculate new position */
pos = tile_base +
swizzle * F_SUBTILE_SIZE +
tile_y % F_SUBTILE_HEIGHT * OW_SIZE +
tile_x % F_SUBTILE_WIDTH;
GEM_BUG_ON(!IS_ALIGNED(pos, pixel_size));
return pos / pixel_size * 4;
}
enum client_tiling {
CLIENT_TILING_LINEAR,
CLIENT_TILING_X,
CLIENT_TILING_Y, /* Y-major, either Tile4 (Xe_HP and beyond) or legacy TileY */
CLIENT_NUM_TILING_TYPES
};
#define WIDTH 512
#define HEIGHT 32
struct blit_buffer {
struct i915_vma *vma;
u32 start_val;
enum client_tiling tiling;
};
struct tiled_blits {
struct intel_context *ce;
struct blit_buffer buffers[3];
struct blit_buffer scratch;
struct i915_vma *batch;
u64 hole;
u64 align;
u32 width;
u32 height;
};
static bool fastblit_supports_x_tiling(const struct drm_i915_private *i915)
{
struct intel_display *display = i915->display;
int gen = GRAPHICS_VER(i915);
/* XY_FAST_COPY_BLT does not exist on pre-gen9 platforms */
drm_WARN_ON(&i915->drm, gen < 9);
if (gen < 12)
return true;
if (GRAPHICS_VER_FULL(i915) < IP_VER(12, 55))
return false;
return intel_display_device_present(display);
}
static bool fast_blit_ok(const struct blit_buffer *buf)
{
/* XY_FAST_COPY_BLT does not exist on pre-gen9 platforms */
if (GRAPHICS_VER(buf->vma->vm->i915) < 9)
return false;
/* filter out platforms with unsupported X-tile support in fastblit */
if (buf->tiling == CLIENT_TILING_X && !fastblit_supports_x_tiling(buf->vma->vm->i915))
return false;
return true;
}
static int prepare_blit(const struct tiled_blits *t,
struct blit_buffer *dst,
struct blit_buffer *src,
struct drm_i915_gem_object *batch)
{
const int ver = GRAPHICS_VER(to_i915(batch->base.dev));
bool use_64b_reloc = ver >= 8;
u32 src_pitch, dst_pitch;
u32 cmd, *cs;
cs = i915_gem_object_pin_map_unlocked(batch, I915_MAP_WC);
if (IS_ERR(cs))
return PTR_ERR(cs);
if (fast_blit_ok(dst) && fast_blit_ok(src)) {
struct intel_gt *gt = t->ce->engine->gt;
u32 src_tiles = 0, dst_tiles = 0;
u32 src_4t = 0, dst_4t = 0;
/* Need to program BLIT_CCTL if it is not done previously
* before using XY_FAST_COPY_BLT
*/
*cs++ = MI_LOAD_REGISTER_IMM(1);
*cs++ = i915_mmio_reg_offset(BLIT_CCTL(t->ce->engine->mmio_base));
*cs++ = (BLIT_CCTL_SRC_MOCS(gt->mocs.uc_index) |
BLIT_CCTL_DST_MOCS(gt->mocs.uc_index));
src_pitch = t->width; /* in dwords */
if (src->tiling == CLIENT_TILING_Y) {
src_tiles = XY_FAST_COPY_BLT_D0_SRC_TILE_MODE(YMAJOR);
if (GRAPHICS_VER_FULL(to_i915(batch->base.dev)) >= IP_VER(12, 55))
src_4t = XY_FAST_COPY_BLT_D1_SRC_TILE4;
} else if (src->tiling == CLIENT_TILING_X) {
src_tiles = XY_FAST_COPY_BLT_D0_SRC_TILE_MODE(TILE_X);
} else {
src_pitch *= 4; /* in bytes */
}
dst_pitch = t->width; /* in dwords */
if (dst->tiling == CLIENT_TILING_Y) {
dst_tiles = XY_FAST_COPY_BLT_D0_DST_TILE_MODE(YMAJOR);
if (GRAPHICS_VER_FULL(to_i915(batch->base.dev)) >= IP_VER(12, 55))
dst_4t = XY_FAST_COPY_BLT_D1_DST_TILE4;
} else if (dst->tiling == CLIENT_TILING_X) {
dst_tiles = XY_FAST_COPY_BLT_D0_DST_TILE_MODE(TILE_X);
} else {
dst_pitch *= 4; /* in bytes */
}
*cs++ = GEN9_XY_FAST_COPY_BLT_CMD | (10 - 2) |
src_tiles | dst_tiles;
*cs++ = src_4t | dst_4t | BLT_DEPTH_32 | dst_pitch;
*cs++ = 0;
*cs++ = t->height << 16 | t->width;
*cs++ = lower_32_bits(i915_vma_offset(dst->vma));
*cs++ = upper_32_bits(i915_vma_offset(dst->vma));
*cs++ = 0;
*cs++ = src_pitch;
*cs++ = lower_32_bits(i915_vma_offset(src->vma));
*cs++ = upper_32_bits(i915_vma_offset(src->vma));
} else {
if (ver >= 6) {
*cs++ = MI_LOAD_REGISTER_IMM(1);
*cs++ = i915_mmio_reg_offset(BCS_SWCTRL);
cmd = (BCS_SRC_Y | BCS_DST_Y) << 16;
if (src->tiling == CLIENT_TILING_Y)
cmd |= BCS_SRC_Y;
if (dst->tiling == CLIENT_TILING_Y)
cmd |= BCS_DST_Y;
*cs++ = cmd;
cmd = MI_FLUSH_DW;
if (ver >= 8)
cmd++;
*cs++ = cmd;
*cs++ = 0;
*cs++ = 0;
*cs++ = 0;
}
cmd = XY_SRC_COPY_BLT_CMD | BLT_WRITE_RGBA | (8 - 2);
if (ver >= 8)
cmd += 2;
src_pitch = t->width * 4;
if (src->tiling) {
cmd |= XY_SRC_COPY_BLT_SRC_TILED;
src_pitch /= 4;
}
dst_pitch = t->width * 4;
if (dst->tiling) {
cmd |= XY_SRC_COPY_BLT_DST_TILED;
dst_pitch /= 4;
}
*cs++ = cmd;
*cs++ = BLT_DEPTH_32 | BLT_ROP_SRC_COPY | dst_pitch;
*cs++ = 0;
*cs++ = t->height << 16 | t->width;
*cs++ = lower_32_bits(i915_vma_offset(dst->vma));
if (use_64b_reloc)
*cs++ = upper_32_bits(i915_vma_offset(dst->vma));
*cs++ = 0;
*cs++ = src_pitch;
*cs++ = lower_32_bits(i915_vma_offset(src->vma));
if (use_64b_reloc)
*cs++ = upper_32_bits(i915_vma_offset(src->vma));
}
*cs++ = MI_BATCH_BUFFER_END;
i915_gem_object_flush_map(batch);
i915_gem_object_unpin_map(batch);
return 0;
}
static void tiled_blits_destroy_buffers(struct tiled_blits *t)
{
int i;
for (i = 0; i < ARRAY_SIZE(t->buffers); i++)
i915_vma_put(t->buffers[i].vma);
i915_vma_put(t->scratch.vma);
i915_vma_put(t->batch);
}
static struct i915_vma *
__create_vma(struct tiled_blits *t, size_t size, bool lmem)
{
struct drm_i915_private *i915 = t->ce->vm->i915;
struct drm_i915_gem_object *obj;
struct i915_vma *vma;
if (lmem)
obj = i915_gem_object_create_lmem(i915, size, 0);
else
obj = i915_gem_object_create_shmem(i915, size);
if (IS_ERR(obj))
return ERR_CAST(obj);
vma = i915_vma_instance(obj, t->ce->vm, NULL);
if (IS_ERR(vma))
i915_gem_object_put(obj);
return vma;
}
static struct i915_vma *create_vma(struct tiled_blits *t, bool lmem)
{
return __create_vma(t, PAGE_ALIGN(t->width * t->height * 4), lmem);
}
static int tiled_blits_create_buffers(struct tiled_blits *t,
int width, int height,
struct rnd_state *prng)
{
struct drm_i915_private *i915 = t->ce->engine->i915;
int i;
t->width = width;
t->height = height;
t->batch = __create_vma(t, PAGE_SIZE, false);
if (IS_ERR(t->batch))
return PTR_ERR(t->batch);
t->scratch.vma = create_vma(t, false);
if (IS_ERR(t->scratch.vma)) {
i915_vma_put(t->batch);
return PTR_ERR(t->scratch.vma);
}
for (i = 0; i < ARRAY_SIZE(t->buffers); i++) {
struct i915_vma *vma;
vma = create_vma(t, HAS_LMEM(i915) && i % 2);
if (IS_ERR(vma)) {
tiled_blits_destroy_buffers(t);
return PTR_ERR(vma);
}
t->buffers[i].vma = vma;
t->buffers[i].tiling =
i915_prandom_u32_max_state(CLIENT_NUM_TILING_TYPES, prng);
}
return 0;
}
static void fill_scratch(struct tiled_blits *t, u32 *vaddr, u32 val)
{
int i;
t->scratch.start_val = val;
for (i = 0; i < t->width * t->height; i++)
vaddr[i] = val++;
i915_gem_object_flush_map(t->scratch.vma->obj);
}
static u64 swizzle_bit(unsigned int bit, u64 offset)
{
return (offset & BIT_ULL(bit)) >> (bit - 6);
}
static u64 tiled_offset(const struct intel_gt *gt,
u64 v,
unsigned int stride,
enum client_tiling tiling,
int x_pos, int y_pos)
{
unsigned int swizzle;
u64 x, y;
if (tiling == CLIENT_TILING_LINEAR)
return v;
y = div64_u64_rem(v, stride, &x);
if (tiling == CLIENT_TILING_X) {
v = div64_u64_rem(y, 8, &y) * stride * 8;
v += y * 512;
v += div64_u64_rem(x, 512, &x) << 12;
v += x;
swizzle = gt->ggtt->bit_6_swizzle_x;
} else if (GRAPHICS_VER_FULL(gt->i915) >= IP_VER(12, 55)) {
/* Y-major tiling layout is Tile4 for Xe_HP and beyond */
v = linear_x_y_to_ftiled_pos(x_pos, y_pos, stride, 32);
/* no swizzling for f-tiling */
swizzle = I915_BIT_6_SWIZZLE_NONE;
} else {
const unsigned int ytile_span = 16;
const unsigned int ytile_height = 512;
v = div64_u64_rem(y, 32, &y) * stride * 32;
v += y * ytile_span;
v += div64_u64_rem(x, ytile_span, &x) * ytile_height;
v += x;
swizzle = gt->ggtt->bit_6_swizzle_y;
}
switch (swizzle) {
case I915_BIT_6_SWIZZLE_9:
v ^= swizzle_bit(9, v);
break;
case I915_BIT_6_SWIZZLE_9_10:
v ^= swizzle_bit(9, v) ^ swizzle_bit(10, v);
break;
case I915_BIT_6_SWIZZLE_9_11:
v ^= swizzle_bit(9, v) ^ swizzle_bit(11, v);
break;
case I915_BIT_6_SWIZZLE_9_10_11:
v ^= swizzle_bit(9, v) ^ swizzle_bit(10, v) ^ swizzle_bit(11, v);
break;
}
return v;
}
static const char *repr_tiling(enum client_tiling tiling)
{
switch (tiling) {
case CLIENT_TILING_LINEAR: return "linear";
case CLIENT_TILING_X: return "X";
case CLIENT_TILING_Y: return "Y / 4";
default: return "unknown";
}
}
static int verify_buffer(const struct tiled_blits *t,
struct blit_buffer *buf,
struct rnd_state *prng)
{
const u32 *vaddr;
int ret = 0;
int x, y, p;
x = i915_prandom_u32_max_state(t->width, prng);
y = i915_prandom_u32_max_state(t->height, prng);
p = y * t->width + x;
vaddr = i915_gem_object_pin_map_unlocked(buf->vma->obj, I915_MAP_WC);
if (IS_ERR(vaddr))
return PTR_ERR(vaddr);
if (vaddr[0] != buf->start_val) {
ret = -EINVAL;
} else {
u64 v = tiled_offset(buf->vma->vm->gt,
p * 4, t->width * 4,
buf->tiling, x, y);
if (vaddr[v / sizeof(*vaddr)] != buf->start_val + p)
ret = -EINVAL;
}
if (ret) {
pr_err("Invalid %s tiling detected at (%d, %d), start_val %x\n",
repr_tiling(buf->tiling),
x, y, buf->start_val);
igt_hexdump(vaddr, 4096);
}
i915_gem_object_unpin_map(buf->vma->obj);
return ret;
}
static int pin_buffer(struct i915_vma *vma, u64 addr)
{
int err;
if (drm_mm_node_allocated(&vma->node) && i915_vma_offset(vma) != addr) {
err = i915_vma_unbind_unlocked(vma);
if (err)
return err;
}
err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_OFFSET_FIXED | addr);
if (err)
return err;
GEM_BUG_ON(i915_vma_offset(vma) != addr);
return 0;
}
static int
tiled_blit(struct tiled_blits *t,
struct blit_buffer *dst, u64 dst_addr,
struct blit_buffer *src, u64 src_addr)
{
struct i915_request *rq;
int err;
err = pin_buffer(src->vma, src_addr);
if (err) {
pr_err("Cannot pin src @ %llx\n", src_addr);
return err;
}
err = pin_buffer(dst->vma, dst_addr);
if (err) {
pr_err("Cannot pin dst @ %llx\n", dst_addr);
goto err_src;
}
err = i915_vma_pin(t->batch, 0, 0, PIN_USER | PIN_HIGH);
if (err) {
pr_err("cannot pin batch\n");
goto err_dst;
}
err = prepare_blit(t, dst, src, t->batch->obj);
if (err)
goto err_bb;
rq = intel_context_create_request(t->ce);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto err_bb;
}
err = igt_vma_move_to_active_unlocked(t->batch, rq, 0);
if (!err)
err = igt_vma_move_to_active_unlocked(src->vma, rq, 0);
if (!err)
err = igt_vma_move_to_active_unlocked(dst->vma, rq, 0);
if (!err)
err = rq->engine->emit_bb_start(rq,
i915_vma_offset(t->batch),
i915_vma_size(t->batch),
0);
i915_request_get(rq);
i915_request_add(rq);
if (i915_request_wait(rq, 0, HZ / 2) < 0)
err = -ETIME;
i915_request_put(rq);
dst->start_val = src->start_val;
err_bb:
i915_vma_unpin(t->batch);
err_dst:
i915_vma_unpin(dst->vma);
err_src:
i915_vma_unpin(src->vma);
return err;
}
static struct tiled_blits *
tiled_blits_create(struct intel_engine_cs *engine, struct rnd_state *prng)
{
struct drm_mm_node hole;
struct tiled_blits *t;
u64 hole_size;
int err;
t = kzalloc_obj(*t);
if (!t)
return ERR_PTR(-ENOMEM);
t->ce = intel_context_create(engine);
if (IS_ERR(t->ce)) {
err = PTR_ERR(t->ce);
goto err_free;
}
t->align = i915_vm_min_alignment(t->ce->vm, INTEL_MEMORY_LOCAL);
t->align = max(t->align,
i915_vm_min_alignment(t->ce->vm, INTEL_MEMORY_SYSTEM));
hole_size = 2 * round_up(WIDTH * HEIGHT * 4, t->align);
hole_size *= 2; /* room to maneuver */
hole_size += 2 * t->align; /* padding on either side */
mutex_lock(&t->ce->vm->mutex);
memset(&hole, 0, sizeof(hole));
err = drm_mm_insert_node_in_range(&t->ce->vm->mm, &hole,
hole_size, t->align,
I915_COLOR_UNEVICTABLE,
0, U64_MAX,
DRM_MM_INSERT_BEST);
if (!err)
drm_mm_remove_node(&hole);
mutex_unlock(&t->ce->vm->mutex);
if (err) {
err = -ENODEV;
goto err_put;
}
t->hole = hole.start + t->align;
pr_info("Using hole at %llx\n", t->hole);
err = tiled_blits_create_buffers(t, WIDTH, HEIGHT, prng);
if (err)
goto err_put;
return t;
err_put:
intel_context_put(t->ce);
err_free:
kfree(t);
return ERR_PTR(err);
}
static void tiled_blits_destroy(struct tiled_blits *t)
{
tiled_blits_destroy_buffers(t);
intel_context_put(t->ce);
kfree(t);
}
static int tiled_blits_prepare(struct tiled_blits *t,
struct rnd_state *prng)
{
u64 offset = round_up(t->width * t->height * 4, t->align);
u32 *map;
int err;
int i;
map = i915_gem_object_pin_map_unlocked(t->scratch.vma->obj, I915_MAP_WC);
if (IS_ERR(map))
return PTR_ERR(map);
/* Use scratch to fill objects */
for (i = 0; i < ARRAY_SIZE(t->buffers); i++) {
fill_scratch(t, map, prandom_u32_state(prng));
GEM_BUG_ON(verify_buffer(t, &t->scratch, prng));
err = tiled_blit(t,
&t->buffers[i], t->hole + offset,
&t->scratch, t->hole);
if (err == 0)
err = verify_buffer(t, &t->buffers[i], prng);
if (err) {
pr_err("Failed to create buffer %d\n", i);
break;
}
}
i915_gem_object_unpin_map(t->scratch.vma->obj);
return err;
}
static int tiled_blits_bounce(struct tiled_blits *t, struct rnd_state *prng)
{
u64 offset = round_up(t->width * t->height * 4, 2 * t->align);
int err;
/* We want to check position invariant tiling across GTT eviction */
err = tiled_blit(t,
&t->buffers[1], t->hole + offset / 2,
&t->buffers[0], t->hole + 2 * offset);
if (err)
return err;
/* Simulating GTT eviction of the same buffer / layout */
t->buffers[2].tiling = t->buffers[0].tiling;
/* Reposition so that we overlap the old addresses, and slightly off */
err = tiled_blit(t,
&t->buffers[2], t->hole + t->align,
&t->buffers[1], t->hole + 3 * offset / 2);
if (err)
return err;
err = verify_buffer(t, &t->buffers[2], prng);
if (err)
return err;
return 0;
}
static int __igt_client_tiled_blits(struct intel_engine_cs *engine,
struct rnd_state *prng)
{
struct tiled_blits *t;
int err;
t = tiled_blits_create(engine, prng);
if (IS_ERR(t))
return PTR_ERR(t);
err = tiled_blits_prepare(t, prng);
if (err)
goto out;
err = tiled_blits_bounce(t, prng);
if (err)
goto out;
out:
tiled_blits_destroy(t);
return err;
}
static bool has_bit17_swizzle(int sw)
{
return (sw == I915_BIT_6_SWIZZLE_9_10_17 ||
sw == I915_BIT_6_SWIZZLE_9_17);
}
static bool bad_swizzling(struct drm_i915_private *i915)
{
struct i915_ggtt *ggtt = to_gt(i915)->ggtt;
if (i915->gem_quirks & GEM_QUIRK_PIN_SWIZZLED_PAGES)
return true;
if (has_bit17_swizzle(ggtt->bit_6_swizzle_x) ||
has_bit17_swizzle(ggtt->bit_6_swizzle_y))
return true;
return false;
}
static int igt_client_tiled_blits(void *arg)
{
struct drm_i915_private *i915 = arg;
I915_RND_STATE(prng);
int inst = 0;
/* Test requires explicit BLT tiling controls */
if (GRAPHICS_VER(i915) < 4)
return 0;
if (bad_swizzling(i915)) /* Requires sane (sub-page) swizzling */
return 0;
do {
struct intel_engine_cs *engine;
int err;
engine = intel_engine_lookup_user(i915,
I915_ENGINE_CLASS_COPY,
inst++);
if (!engine)
return 0;
err = __igt_client_tiled_blits(engine, &prng);
if (err == -ENODEV)
err = 0;
if (err)
return err;
} while (1);
}
int i915_gem_client_blt_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_client_tiled_blits),
};
if (intel_gt_is_wedged(to_gt(i915)))
return 0;
return i915_live_subtests(tests, i915);
}
@@ -0,0 +1,434 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2017 Intel Corporation
*/
#include <linux/prime_numbers.h>
#include "gt/intel_engine_pm.h"
#include "gt/intel_gpu_commands.h"
#include "gt/intel_gt.h"
#include "gt/intel_gt_pm.h"
#include "gt/intel_ring.h"
#include "i915_selftest.h"
#include "selftests/i915_random.h"
struct context {
struct drm_i915_gem_object *obj;
struct intel_engine_cs *engine;
};
static int cpu_set(struct context *ctx, unsigned long offset, u32 v)
{
unsigned int needs_clflush;
struct page *page;
u32 *cpu;
int err;
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_prepare_write(ctx->obj, &needs_clflush);
if (err)
goto out;
page = i915_gem_object_get_page(ctx->obj, offset >> PAGE_SHIFT);
cpu = kmap_local_page(page) + offset_in_page(offset);
if (needs_clflush & CLFLUSH_BEFORE)
drm_clflush_virt_range(cpu, sizeof(*cpu));
*cpu = v;
if (needs_clflush & CLFLUSH_AFTER)
drm_clflush_virt_range(cpu, sizeof(*cpu));
kunmap_local(cpu);
i915_gem_object_finish_access(ctx->obj);
out:
i915_gem_object_unlock(ctx->obj);
return err;
}
static int cpu_get(struct context *ctx, unsigned long offset, u32 *v)
{
unsigned int needs_clflush;
struct page *page;
u32 *cpu;
int err;
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_prepare_read(ctx->obj, &needs_clflush);
if (err)
goto out;
page = i915_gem_object_get_page(ctx->obj, offset >> PAGE_SHIFT);
cpu = kmap_local_page(page) + offset_in_page(offset);
if (needs_clflush & CLFLUSH_BEFORE)
drm_clflush_virt_range(cpu, sizeof(*cpu));
*v = *cpu;
kunmap_local(cpu);
i915_gem_object_finish_access(ctx->obj);
out:
i915_gem_object_unlock(ctx->obj);
return err;
}
static int gtt_set(struct context *ctx, unsigned long offset, u32 v)
{
intel_wakeref_t wakeref;
struct i915_vma *vma;
u32 __iomem *map;
int err = 0;
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_set_to_gtt_domain(ctx->obj, true);
i915_gem_object_unlock(ctx->obj);
if (err)
return err;
vma = i915_gem_object_ggtt_pin(ctx->obj, NULL, 0, 0, PIN_MAPPABLE);
if (IS_ERR(vma))
return PTR_ERR(vma);
wakeref = intel_gt_pm_get(vma->vm->gt);
map = i915_vma_pin_iomap(vma);
i915_vma_unpin(vma);
if (IS_ERR(map)) {
err = PTR_ERR(map);
goto out_rpm;
}
iowrite32(v, &map[offset / sizeof(*map)]);
i915_vma_unpin_iomap(vma);
out_rpm:
intel_gt_pm_put(vma->vm->gt, wakeref);
return err;
}
static int gtt_get(struct context *ctx, unsigned long offset, u32 *v)
{
intel_wakeref_t wakeref;
struct i915_vma *vma;
u32 __iomem *map;
int err = 0;
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_set_to_gtt_domain(ctx->obj, false);
i915_gem_object_unlock(ctx->obj);
if (err)
return err;
vma = i915_gem_object_ggtt_pin(ctx->obj, NULL, 0, 0, PIN_MAPPABLE);
if (IS_ERR(vma))
return PTR_ERR(vma);
wakeref = intel_gt_pm_get(vma->vm->gt);
map = i915_vma_pin_iomap(vma);
i915_vma_unpin(vma);
if (IS_ERR(map)) {
err = PTR_ERR(map);
goto out_rpm;
}
*v = ioread32(&map[offset / sizeof(*map)]);
i915_vma_unpin_iomap(vma);
out_rpm:
intel_gt_pm_put(vma->vm->gt, wakeref);
return err;
}
static int wc_set(struct context *ctx, unsigned long offset, u32 v)
{
u32 *map;
int err;
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_set_to_wc_domain(ctx->obj, true);
i915_gem_object_unlock(ctx->obj);
if (err)
return err;
map = i915_gem_object_pin_map_unlocked(ctx->obj, I915_MAP_WC);
if (IS_ERR(map))
return PTR_ERR(map);
map[offset / sizeof(*map)] = v;
__i915_gem_object_flush_map(ctx->obj, offset, sizeof(*map));
i915_gem_object_unpin_map(ctx->obj);
return 0;
}
static int wc_get(struct context *ctx, unsigned long offset, u32 *v)
{
u32 *map;
int err;
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_set_to_wc_domain(ctx->obj, false);
i915_gem_object_unlock(ctx->obj);
if (err)
return err;
map = i915_gem_object_pin_map_unlocked(ctx->obj, I915_MAP_WC);
if (IS_ERR(map))
return PTR_ERR(map);
*v = map[offset / sizeof(*map)];
i915_gem_object_unpin_map(ctx->obj);
return 0;
}
static int gpu_set(struct context *ctx, unsigned long offset, u32 v)
{
struct i915_request *rq;
struct i915_vma *vma;
u32 *cs;
int err;
vma = i915_gem_object_ggtt_pin(ctx->obj, NULL, 0, 0, 0);
if (IS_ERR(vma))
return PTR_ERR(vma);
i915_gem_object_lock(ctx->obj, NULL);
err = i915_gem_object_set_to_gtt_domain(ctx->obj, true);
if (err)
goto out_unlock;
rq = intel_engine_create_kernel_request(ctx->engine);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto out_unpin;
}
cs = intel_ring_begin(rq, 4);
if (IS_ERR(cs)) {
err = PTR_ERR(cs);
goto out_rq;
}
if (GRAPHICS_VER(ctx->engine->i915) >= 8) {
*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
*cs++ = lower_32_bits(i915_ggtt_offset(vma) + offset);
*cs++ = upper_32_bits(i915_ggtt_offset(vma) + offset);
*cs++ = v;
} else if (GRAPHICS_VER(ctx->engine->i915) >= 4) {
*cs++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
*cs++ = 0;
*cs++ = i915_ggtt_offset(vma) + offset;
*cs++ = v;
} else {
*cs++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
*cs++ = i915_ggtt_offset(vma) + offset;
*cs++ = v;
*cs++ = MI_NOOP;
}
intel_ring_advance(rq, cs);
err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
out_rq:
i915_request_add(rq);
out_unpin:
i915_vma_unpin(vma);
out_unlock:
i915_gem_object_unlock(ctx->obj);
return err;
}
static bool always_valid(struct context *ctx)
{
return true;
}
static bool needs_fence_registers(struct context *ctx)
{
struct intel_gt *gt = ctx->engine->gt;
if (intel_gt_is_wedged(gt))
return false;
return gt->ggtt->num_fences;
}
static bool needs_mi_store_dword(struct context *ctx)
{
if (intel_gt_is_wedged(ctx->engine->gt))
return false;
return intel_engine_can_store_dword(ctx->engine);
}
static const struct igt_coherency_mode {
const char *name;
int (*set)(struct context *ctx, unsigned long offset, u32 v);
int (*get)(struct context *ctx, unsigned long offset, u32 *v);
bool (*valid)(struct context *ctx);
} igt_coherency_mode[] = {
{ "cpu", cpu_set, cpu_get, always_valid },
{ "gtt", gtt_set, gtt_get, needs_fence_registers },
{ "wc", wc_set, wc_get, always_valid },
{ "gpu", gpu_set, NULL, needs_mi_store_dword },
{ },
};
static struct intel_engine_cs *
random_engine(struct drm_i915_private *i915, struct rnd_state *prng)
{
struct intel_engine_cs *engine;
unsigned int count;
count = 0;
for_each_uabi_engine(engine, i915)
count++;
count = i915_prandom_u32_max_state(count, prng);
for_each_uabi_engine(engine, i915)
if (count-- == 0)
return engine;
return NULL;
}
static int igt_gem_coherency(void *arg)
{
const unsigned int ncachelines = PAGE_SIZE/64;
struct drm_i915_private *i915 = arg;
const struct igt_coherency_mode *read, *write, *over;
unsigned long count, n;
u32 *offsets, *values;
I915_RND_STATE(prng);
struct context ctx;
int err = 0;
/*
* We repeatedly write, overwrite and read from a sequence of
* cachelines in order to try and detect incoherency (unflushed writes
* from either the CPU or GPU). Each setter/getter uses our cache
* domain API which should prevent incoherency.
*/
offsets = kmalloc_array(ncachelines, 2*sizeof(u32), GFP_KERNEL);
if (!offsets)
return -ENOMEM;
for (count = 0; count < ncachelines; count++)
offsets[count] = count * 64 + 4 * (count % 16);
values = offsets + ncachelines;
ctx.engine = random_engine(i915, &prng);
if (!ctx.engine) {
err = -ENODEV;
goto out_free;
}
pr_info("%s: using %s\n", __func__, ctx.engine->name);
intel_engine_pm_get(ctx.engine);
for (over = igt_coherency_mode; over->name; over++) {
if (!over->set)
continue;
if (!over->valid(&ctx))
continue;
for (write = igt_coherency_mode; write->name; write++) {
if (!write->set)
continue;
if (!write->valid(&ctx))
continue;
for (read = igt_coherency_mode; read->name; read++) {
if (!read->get)
continue;
if (!read->valid(&ctx))
continue;
for_each_prime_number_from(count, 1, ncachelines) {
ctx.obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
if (IS_ERR(ctx.obj)) {
err = PTR_ERR(ctx.obj);
goto out_pm;
}
i915_random_reorder(offsets, ncachelines, &prng);
for (n = 0; n < count; n++)
values[n] = prandom_u32_state(&prng);
for (n = 0; n < count; n++) {
err = over->set(&ctx, offsets[n], ~values[n]);
if (err) {
pr_err("Failed to set stale value[%ld/%ld] in object using %s, err=%d\n",
n, count, over->name, err);
goto put_object;
}
}
for (n = 0; n < count; n++) {
err = write->set(&ctx, offsets[n], values[n]);
if (err) {
pr_err("Failed to set value[%ld/%ld] in object using %s, err=%d\n",
n, count, write->name, err);
goto put_object;
}
}
for (n = 0; n < count; n++) {
u32 found;
err = read->get(&ctx, offsets[n], &found);
if (err) {
pr_err("Failed to get value[%ld/%ld] in object using %s, err=%d\n",
n, count, read->name, err);
goto put_object;
}
if (found != values[n]) {
pr_err("Value[%ld/%ld] mismatch, (overwrite with %s) wrote [%s] %x read [%s] %x (inverse %x), at offset %x\n",
n, count, over->name,
write->name, values[n],
read->name, found,
~values[n], offsets[n]);
err = -EINVAL;
goto put_object;
}
}
i915_gem_object_put(ctx.obj);
}
}
}
}
out_pm:
intel_engine_pm_put(ctx.engine);
out_free:
kfree(offsets);
return err;
put_object:
i915_gem_object_put(ctx.obj);
goto out_pm;
}
int i915_gem_coherency_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_gem_coherency),
};
return i915_live_subtests(tests, i915);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,560 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#include "i915_drv.h"
#include "i915_selftest.h"
#include "gem/i915_gem_context.h"
#include "gt/intel_gt.h"
#include "mock_context.h"
#include "mock_dmabuf.h"
#include "igt_gem_utils.h"
#include "selftests/mock_drm.h"
#include "selftests/mock_gem_device.h"
static int igt_dmabuf_export(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct dma_buf *dmabuf;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
dmabuf = i915_gem_prime_export(&obj->base, 0);
i915_gem_object_put(obj);
if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%d\n",
(int)PTR_ERR(dmabuf));
return PTR_ERR(dmabuf);
}
dma_buf_put(dmabuf);
return 0;
}
static int igt_dmabuf_import_self(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj, *import_obj;
struct drm_gem_object *import;
struct dma_buf *dmabuf;
int err;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
dmabuf = i915_gem_prime_export(&obj->base, 0);
if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%d\n",
(int)PTR_ERR(dmabuf));
err = PTR_ERR(dmabuf);
goto out;
}
import = i915_gem_prime_import(&i915->drm, dmabuf);
if (IS_ERR(import)) {
pr_err("i915_gem_prime_import failed with err=%d\n",
(int)PTR_ERR(import));
err = PTR_ERR(import);
goto out_dmabuf;
}
import_obj = to_intel_bo(import);
if (import != &obj->base) {
pr_err("i915_gem_prime_import created a new object!\n");
err = -EINVAL;
goto out_import;
}
i915_gem_object_lock(import_obj, NULL);
err = __i915_gem_object_get_pages(import_obj);
i915_gem_object_unlock(import_obj);
if (err) {
pr_err("Same object dma-buf get_pages failed!\n");
goto out_import;
}
err = 0;
out_import:
i915_gem_object_put(import_obj);
out_dmabuf:
dma_buf_put(dmabuf);
out:
i915_gem_object_put(obj);
return err;
}
static int igt_dmabuf_import_same_driver_lmem(void *arg)
{
struct drm_i915_private *i915 = arg;
struct intel_memory_region *lmem = i915->mm.regions[INTEL_REGION_LMEM_0];
struct drm_i915_gem_object *obj;
struct drm_gem_object *import;
struct dma_buf *dmabuf;
int err;
if (!lmem)
return 0;
force_different_devices = true;
obj = __i915_gem_object_create_user(i915, PAGE_SIZE, &lmem, 1);
if (IS_ERR(obj)) {
pr_err("__i915_gem_object_create_user failed with err=%ld\n",
PTR_ERR(obj));
err = PTR_ERR(obj);
goto out_ret;
}
dmabuf = i915_gem_prime_export(&obj->base, 0);
if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%ld\n",
PTR_ERR(dmabuf));
err = PTR_ERR(dmabuf);
goto out;
}
/*
* We expect an import of an LMEM-only object to fail with
* -EOPNOTSUPP because it can't be migrated to SMEM.
*/
import = i915_gem_prime_import(&i915->drm, dmabuf);
if (!IS_ERR(import)) {
drm_gem_object_put(import);
pr_err("i915_gem_prime_import succeeded when it shouldn't have\n");
err = -EINVAL;
} else if (PTR_ERR(import) != -EOPNOTSUPP) {
pr_err("i915_gem_prime_import failed with the wrong err=%ld\n",
PTR_ERR(import));
err = PTR_ERR(import);
} else {
err = 0;
}
dma_buf_put(dmabuf);
out:
i915_gem_object_put(obj);
out_ret:
force_different_devices = false;
return err;
}
static int verify_access(struct drm_i915_private *i915,
struct drm_i915_gem_object *native_obj,
struct drm_i915_gem_object *import_obj)
{
struct i915_gem_engines_iter it;
struct i915_gem_context *ctx;
struct intel_context *ce;
struct i915_vma *vma;
struct file *file;
u32 *vaddr;
int err = 0, i;
unsigned int mode;
file = mock_file(i915);
if (IS_ERR(file))
return PTR_ERR(file);
ctx = live_context(i915, file);
if (IS_ERR(ctx)) {
err = PTR_ERR(ctx);
goto out_file;
}
for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
if (intel_engine_can_store_dword(ce->engine))
break;
}
i915_gem_context_unlock_engines(ctx);
if (!ce)
goto out_file;
vma = i915_vma_instance(import_obj, ce->vm, NULL);
if (IS_ERR(vma)) {
err = PTR_ERR(vma);
goto out_file;
}
err = i915_vma_pin(vma, 0, 0, PIN_USER);
if (err)
goto out_file;
err = igt_gpu_fill_dw(ce, vma, 0,
vma->size >> PAGE_SHIFT, 0xdeadbeaf);
i915_vma_unpin(vma);
if (err)
goto out_file;
err = i915_gem_object_wait(import_obj, 0, MAX_SCHEDULE_TIMEOUT);
if (err)
goto out_file;
mode = intel_gt_coherent_map_type(to_gt(i915), native_obj, false);
vaddr = i915_gem_object_pin_map_unlocked(native_obj, mode);
if (IS_ERR(vaddr)) {
err = PTR_ERR(vaddr);
goto out_file;
}
for (i = 0; i < native_obj->base.size / sizeof(u32); i += PAGE_SIZE / sizeof(u32)) {
if (vaddr[i] != 0xdeadbeaf) {
pr_err("Data mismatch [%d]=%u\n", i, vaddr[i]);
err = -EINVAL;
goto out_file;
}
}
out_file:
fput(file);
return err;
}
static int igt_dmabuf_import_same_driver(struct drm_i915_private *i915,
struct intel_memory_region **regions,
unsigned int num_regions)
{
struct drm_i915_gem_object *obj, *import_obj;
struct drm_gem_object *import;
struct dma_buf *dmabuf;
struct dma_buf_attachment *import_attach;
struct sg_table *st;
long timeout;
int err;
force_different_devices = true;
obj = __i915_gem_object_create_user(i915, SZ_8M,
regions, num_regions);
if (IS_ERR(obj)) {
pr_err("__i915_gem_object_create_user failed with err=%ld\n",
PTR_ERR(obj));
err = PTR_ERR(obj);
goto out_ret;
}
dmabuf = i915_gem_prime_export(&obj->base, 0);
if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%ld\n",
PTR_ERR(dmabuf));
err = PTR_ERR(dmabuf);
goto out;
}
import = i915_gem_prime_import(&i915->drm, dmabuf);
if (IS_ERR(import)) {
pr_err("i915_gem_prime_import failed with err=%ld\n",
PTR_ERR(import));
err = PTR_ERR(import);
goto out_dmabuf;
}
import_obj = to_intel_bo(import);
if (import == &obj->base) {
pr_err("i915_gem_prime_import reused gem object!\n");
err = -EINVAL;
goto out_import;
}
i915_gem_object_lock(import_obj, NULL);
err = __i915_gem_object_get_pages(import_obj);
if (err) {
pr_err("Different objects dma-buf get_pages failed!\n");
i915_gem_object_unlock(import_obj);
goto out_import;
}
/*
* If the exported object is not in system memory, something
* weird is going on. TODO: When p2p is supported, this is no
* longer considered weird.
*/
if (obj->mm.region != i915->mm.regions[INTEL_REGION_SMEM]) {
pr_err("Exported dma-buf is not in system memory\n");
err = -EINVAL;
}
i915_gem_object_unlock(import_obj);
err = verify_access(i915, obj, import_obj);
if (err)
goto out_import;
/* Now try a fake an importer */
import_attach = dma_buf_attach(dmabuf, obj->base.dev->dev);
if (IS_ERR(import_attach)) {
err = PTR_ERR(import_attach);
goto out_import;
}
st = dma_buf_map_attachment_unlocked(import_attach, DMA_BIDIRECTIONAL);
if (IS_ERR(st)) {
err = PTR_ERR(st);
goto out_detach;
}
timeout = dma_resv_wait_timeout(dmabuf->resv, DMA_RESV_USAGE_WRITE,
true, 5 * HZ);
if (!timeout) {
pr_err("dmabuf wait for exclusive fence timed out.\n");
timeout = -ETIME;
}
err = timeout > 0 ? 0 : timeout;
dma_buf_unmap_attachment_unlocked(import_attach, st, DMA_BIDIRECTIONAL);
out_detach:
dma_buf_detach(dmabuf, import_attach);
out_import:
i915_gem_object_put(import_obj);
out_dmabuf:
dma_buf_put(dmabuf);
out:
i915_gem_object_put(obj);
out_ret:
force_different_devices = false;
return err;
}
static int igt_dmabuf_import_same_driver_smem(void *arg)
{
struct drm_i915_private *i915 = arg;
struct intel_memory_region *smem = i915->mm.regions[INTEL_REGION_SMEM];
return igt_dmabuf_import_same_driver(i915, &smem, 1);
}
static int igt_dmabuf_import_same_driver_lmem_smem(void *arg)
{
struct drm_i915_private *i915 = arg;
struct intel_memory_region *regions[2];
if (!i915->mm.regions[INTEL_REGION_LMEM_0])
return 0;
regions[0] = i915->mm.regions[INTEL_REGION_LMEM_0];
regions[1] = i915->mm.regions[INTEL_REGION_SMEM];
return igt_dmabuf_import_same_driver(i915, regions, 2);
}
static int igt_dmabuf_import(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct dma_buf *dmabuf;
void *obj_map, *dma_map;
struct iosys_map map;
u32 pattern[] = { 0, 0xaa, 0xcc, 0x55, 0xff };
int err, i;
dmabuf = mock_dmabuf(1);
if (IS_ERR(dmabuf))
return PTR_ERR(dmabuf);
obj = to_intel_bo(i915_gem_prime_import(&i915->drm, dmabuf));
if (IS_ERR(obj)) {
pr_err("i915_gem_prime_import failed with err=%d\n",
(int)PTR_ERR(obj));
err = PTR_ERR(obj);
goto out_dmabuf;
}
if (obj->base.dev != &i915->drm) {
pr_err("i915_gem_prime_import created a non-i915 object!\n");
err = -EINVAL;
goto out_obj;
}
if (obj->base.size != PAGE_SIZE) {
pr_err("i915_gem_prime_import is wrong size found %lld, expected %ld\n",
(long long)obj->base.size, PAGE_SIZE);
err = -EINVAL;
goto out_obj;
}
err = dma_buf_vmap_unlocked(dmabuf, &map);
dma_map = err ? NULL : map.vaddr;
if (!dma_map) {
pr_err("dma_buf_vmap failed\n");
err = -ENOMEM;
goto out_obj;
}
if (0) { /* Can not yet map dmabuf */
obj_map = i915_gem_object_pin_map(obj, I915_MAP_WB);
if (IS_ERR(obj_map)) {
err = PTR_ERR(obj_map);
pr_err("i915_gem_object_pin_map failed with err=%d\n", err);
goto out_dma_map;
}
for (i = 0; i < ARRAY_SIZE(pattern); i++) {
memset(dma_map, pattern[i], PAGE_SIZE);
if (memchr_inv(obj_map, pattern[i], PAGE_SIZE)) {
err = -EINVAL;
pr_err("imported vmap not all set to %x!\n", pattern[i]);
i915_gem_object_unpin_map(obj);
goto out_dma_map;
}
}
for (i = 0; i < ARRAY_SIZE(pattern); i++) {
memset(obj_map, pattern[i], PAGE_SIZE);
if (memchr_inv(dma_map, pattern[i], PAGE_SIZE)) {
err = -EINVAL;
pr_err("exported vmap not all set to %x!\n", pattern[i]);
i915_gem_object_unpin_map(obj);
goto out_dma_map;
}
}
i915_gem_object_unpin_map(obj);
}
err = 0;
out_dma_map:
dma_buf_vunmap_unlocked(dmabuf, &map);
out_obj:
i915_gem_object_put(obj);
out_dmabuf:
dma_buf_put(dmabuf);
return err;
}
static int igt_dmabuf_import_ownership(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct dma_buf *dmabuf;
struct iosys_map map;
void *ptr;
int err;
dmabuf = mock_dmabuf(1);
if (IS_ERR(dmabuf))
return PTR_ERR(dmabuf);
err = dma_buf_vmap_unlocked(dmabuf, &map);
ptr = err ? NULL : map.vaddr;
if (!ptr) {
pr_err("dma_buf_vmap failed\n");
err = -ENOMEM;
goto err_dmabuf;
}
memset(ptr, 0xc5, PAGE_SIZE);
dma_buf_vunmap_unlocked(dmabuf, &map);
obj = to_intel_bo(i915_gem_prime_import(&i915->drm, dmabuf));
if (IS_ERR(obj)) {
pr_err("i915_gem_prime_import failed with err=%d\n",
(int)PTR_ERR(obj));
err = PTR_ERR(obj);
goto err_dmabuf;
}
dma_buf_put(dmabuf);
err = i915_gem_object_pin_pages_unlocked(obj);
if (err) {
pr_err("i915_gem_object_pin_pages failed with err=%d\n", err);
goto out_obj;
}
err = 0;
i915_gem_object_unpin_pages(obj);
out_obj:
i915_gem_object_put(obj);
return err;
err_dmabuf:
dma_buf_put(dmabuf);
return err;
}
static int igt_dmabuf_export_vmap(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
struct dma_buf *dmabuf;
struct iosys_map map;
void *ptr;
int err;
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
dmabuf = i915_gem_prime_export(&obj->base, 0);
if (IS_ERR(dmabuf)) {
pr_err("i915_gem_prime_export failed with err=%d\n",
(int)PTR_ERR(dmabuf));
err = PTR_ERR(dmabuf);
goto err_obj;
}
i915_gem_object_put(obj);
err = dma_buf_vmap_unlocked(dmabuf, &map);
ptr = err ? NULL : map.vaddr;
if (!ptr) {
pr_err("dma_buf_vmap failed\n");
err = -ENOMEM;
goto out;
}
if (!mem_is_zero(ptr, dmabuf->size)) {
pr_err("Exported object not initialised to zero!\n");
err = -EINVAL;
goto out;
}
memset(ptr, 0xc5, dmabuf->size);
err = 0;
dma_buf_vunmap_unlocked(dmabuf, &map);
out:
dma_buf_put(dmabuf);
return err;
err_obj:
i915_gem_object_put(obj);
return err;
}
int i915_gem_dmabuf_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_dmabuf_export),
SUBTEST(igt_dmabuf_import_self),
SUBTEST(igt_dmabuf_import),
SUBTEST(igt_dmabuf_import_ownership),
SUBTEST(igt_dmabuf_export_vmap),
};
struct drm_i915_private *i915;
int err;
i915 = mock_gem_device();
if (!i915)
return -ENOMEM;
err = i915_subtests(tests, i915);
mock_destroy_device(i915);
return err;
}
int i915_gem_dmabuf_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_dmabuf_export),
SUBTEST(igt_dmabuf_import_same_driver_lmem),
SUBTEST(igt_dmabuf_import_same_driver_smem),
SUBTEST(igt_dmabuf_import_same_driver_lmem_smem),
};
return i915_live_subtests(tests, i915);
}
@@ -0,0 +1,528 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2020-2021 Intel Corporation
*/
#include "gt/intel_migrate.h"
#include "gt/intel_gpu_commands.h"
#include "gem/i915_gem_ttm_move.h"
#include "i915_deps.h"
#include "selftests/igt_reset.h"
#include "selftests/igt_spinner.h"
static int igt_fill_check_buffer(struct drm_i915_gem_object *obj,
struct intel_gt *gt,
bool fill)
{
unsigned int i, count = obj->base.size / sizeof(u32);
enum i915_map_type map_type =
intel_gt_coherent_map_type(gt, obj, false);
u32 *cur;
int err = 0;
assert_object_held(obj);
cur = i915_gem_object_pin_map(obj, map_type);
if (IS_ERR(cur))
return PTR_ERR(cur);
if (fill)
for (i = 0; i < count; ++i)
*cur++ = i;
else
for (i = 0; i < count; ++i)
if (*cur++ != i) {
pr_err("Object content mismatch at location %d of %d\n", i, count);
err = -EINVAL;
break;
}
i915_gem_object_unpin_map(obj);
return err;
}
static int igt_create_migrate(struct intel_gt *gt, enum intel_region_id src,
enum intel_region_id dst)
{
struct drm_i915_private *i915 = gt->i915;
struct intel_memory_region *src_mr = i915->mm.regions[src];
struct intel_memory_region *dst_mr = i915->mm.regions[dst];
struct drm_i915_gem_object *obj;
struct i915_gem_ww_ctx ww;
int err = 0;
GEM_BUG_ON(!src_mr);
GEM_BUG_ON(!dst_mr);
/* Switch object backing-store on create */
obj = i915_gem_object_create_region(src_mr, dst_mr->min_page_size, 0, 0);
if (IS_ERR(obj))
return PTR_ERR(obj);
for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
err = igt_fill_check_buffer(obj, gt, true);
if (err)
continue;
err = i915_gem_object_migrate(obj, &ww, dst);
if (err)
continue;
err = i915_gem_object_pin_pages(obj);
if (err)
continue;
if (i915_gem_object_can_migrate(obj, src))
err = -EINVAL;
i915_gem_object_unpin_pages(obj);
err = i915_gem_object_wait_migration(obj, true);
if (err)
continue;
err = igt_fill_check_buffer(obj, gt, false);
}
i915_gem_object_put(obj);
return err;
}
static int igt_smem_create_migrate(void *arg)
{
return igt_create_migrate(arg, INTEL_REGION_LMEM_0, INTEL_REGION_SMEM);
}
static int igt_lmem_create_migrate(void *arg)
{
return igt_create_migrate(arg, INTEL_REGION_SMEM, INTEL_REGION_LMEM_0);
}
static int igt_same_create_migrate(void *arg)
{
return igt_create_migrate(arg, INTEL_REGION_LMEM_0, INTEL_REGION_LMEM_0);
}
static int lmem_pages_migrate_one(struct i915_gem_ww_ctx *ww,
struct drm_i915_gem_object *obj,
struct i915_vma *vma,
bool silent_migrate)
{
int err;
err = i915_gem_object_lock(obj, ww);
if (err)
return err;
if (vma) {
err = i915_vma_pin_ww(vma, ww, obj->base.size, 0,
0UL | PIN_OFFSET_FIXED |
PIN_USER);
if (err) {
if (err != -EINTR && err != ERESTARTSYS &&
err != -EDEADLK)
pr_err("Failed to pin vma.\n");
return err;
}
i915_vma_unpin(vma);
}
/*
* Migration will implicitly unbind (asynchronously) any bound
* vmas.
*/
if (i915_gem_object_is_lmem(obj)) {
err = i915_gem_object_migrate(obj, ww, INTEL_REGION_SMEM);
if (err) {
if (!silent_migrate)
pr_err("Object failed migration to smem\n");
if (err)
return err;
}
if (i915_gem_object_is_lmem(obj)) {
pr_err("object still backed by lmem\n");
err = -EINVAL;
}
if (!i915_gem_object_has_struct_page(obj)) {
pr_err("object not backed by struct page\n");
err = -EINVAL;
}
} else {
err = i915_gem_object_migrate(obj, ww, INTEL_REGION_LMEM_0);
if (err) {
if (!silent_migrate)
pr_err("Object failed migration to lmem\n");
if (err)
return err;
}
if (i915_gem_object_has_struct_page(obj)) {
pr_err("object still backed by struct page\n");
err = -EINVAL;
}
if (!i915_gem_object_is_lmem(obj)) {
pr_err("object not backed by lmem\n");
err = -EINVAL;
}
}
return err;
}
static int __igt_lmem_pages_migrate(struct intel_gt *gt,
struct i915_address_space *vm,
struct i915_deps *deps,
struct igt_spinner *spin,
struct dma_fence *spin_fence,
bool borked_migrate)
{
struct drm_i915_private *i915 = gt->i915;
struct drm_i915_gem_object *obj;
struct i915_vma *vma = NULL;
struct i915_gem_ww_ctx ww;
struct i915_request *rq;
int err;
int i;
/* From LMEM to shmem and back again */
obj = i915_gem_object_create_lmem(i915, SZ_2M, 0);
if (IS_ERR(obj))
return PTR_ERR(obj);
if (vm) {
vma = i915_vma_instance(obj, vm, NULL);
if (IS_ERR(vma)) {
err = PTR_ERR(vma);
goto out_put;
}
}
/* Initial GPU fill, sync, CPU initialization. */
for_i915_gem_ww(&ww, err, true) {
err = i915_gem_object_lock(obj, &ww);
if (err)
continue;
err = ____i915_gem_object_get_pages(obj);
if (err)
continue;
err = intel_migrate_clear(&gt->migrate, &ww, deps,
obj->mm.pages->sgl, obj->pat_index,
i915_gem_object_is_lmem(obj),
0xdeadbeaf, &rq);
if (rq) {
err = dma_resv_reserve_fences(obj->base.resv, 1);
if (!err)
dma_resv_add_fence(obj->base.resv, &rq->fence,
DMA_RESV_USAGE_KERNEL);
i915_request_put(rq);
}
if (err)
continue;
if (!vma) {
err = igt_fill_check_buffer(obj, gt, true);
if (err)
continue;
}
}
if (err)
goto out_put;
/*
* Migrate to and from smem without explicitly syncing.
* Finalize with data in smem for fast readout.
*/
for (i = 1; i <= 5; ++i) {
for_i915_gem_ww(&ww, err, true)
err = lmem_pages_migrate_one(&ww, obj, vma,
borked_migrate);
if (err)
goto out_put;
}
err = i915_gem_object_lock_interruptible(obj, NULL);
if (err)
goto out_put;
if (spin) {
if (dma_fence_is_signaled(spin_fence)) {
pr_err("Spinner was terminated by hangcheck.\n");
err = -EBUSY;
goto out_unlock;
}
igt_spinner_end(spin);
}
/* Finally sync migration and check content. */
err = i915_gem_object_wait_migration(obj, true);
if (err)
goto out_unlock;
if (vma) {
err = i915_vma_wait_for_bind(vma);
if (err)
goto out_unlock;
} else {
err = igt_fill_check_buffer(obj, gt, false);
}
out_unlock:
i915_gem_object_unlock(obj);
out_put:
i915_gem_object_put(obj);
return err;
}
static int igt_lmem_pages_failsafe_migrate(void *arg)
{
int fail_gpu, fail_alloc, ban_memcpy, ret;
struct intel_gt *gt = arg;
for (fail_gpu = 0; fail_gpu < 2; ++fail_gpu) {
for (fail_alloc = 0; fail_alloc < 2; ++fail_alloc) {
for (ban_memcpy = 0; ban_memcpy < 2; ++ban_memcpy) {
pr_info("Simulated failure modes: gpu: %d, alloc:%d, ban_memcpy: %d\n",
fail_gpu, fail_alloc, ban_memcpy);
i915_ttm_migrate_set_ban_memcpy(ban_memcpy);
i915_ttm_migrate_set_failure_modes(fail_gpu,
fail_alloc);
ret = __igt_lmem_pages_migrate(gt, NULL, NULL,
NULL, NULL,
ban_memcpy &&
fail_gpu);
if (ban_memcpy && fail_gpu) {
struct intel_gt *__gt;
unsigned int id;
if (ret != -EIO) {
pr_err("expected -EIO, got (%d)\n", ret);
ret = -EINVAL;
} else {
ret = 0;
}
for_each_gt(__gt, gt->i915, id) {
intel_wakeref_t wakeref;
bool wedged;
mutex_lock(&__gt->reset.mutex);
wedged = test_bit(I915_WEDGED, &__gt->reset.flags);
mutex_unlock(&__gt->reset.mutex);
if (fail_gpu && !fail_alloc) {
if (!wedged) {
pr_err("gt(%u) not wedged\n", id);
ret = -EINVAL;
continue;
}
} else if (wedged) {
pr_err("gt(%u) incorrectly wedged\n", id);
ret = -EINVAL;
} else {
continue;
}
wakeref = intel_runtime_pm_get(__gt->uncore->rpm);
igt_global_reset_lock(__gt);
intel_gt_reset(__gt, ALL_ENGINES, NULL);
igt_global_reset_unlock(__gt);
intel_runtime_pm_put(__gt->uncore->rpm, wakeref);
}
if (ret)
goto out_err;
}
}
}
}
out_err:
i915_ttm_migrate_set_failure_modes(false, false);
i915_ttm_migrate_set_ban_memcpy(false);
return ret;
}
/*
* This subtest tests that unbinding at migration is indeed performed
* async. We launch a spinner and a number of migrations depending on
* that spinner to have terminated. Before each migration we bind a
* vma, which should then be async unbound by the migration operation.
* If we are able to schedule migrations without blocking while the
* spinner is still running, those unbinds are indeed async and non-
* blocking.
*
* Note that each async bind operation is awaiting the previous migration
* due to the moving fence resulting from the migration.
*/
static int igt_async_migrate(struct intel_gt *gt)
{
struct intel_engine_cs *engine;
enum intel_engine_id id;
struct i915_ppgtt *ppgtt;
struct igt_spinner spin;
int err;
ppgtt = i915_ppgtt_create(gt, 0);
if (IS_ERR(ppgtt))
return PTR_ERR(ppgtt);
if (igt_spinner_init(&spin, gt)) {
err = -ENOMEM;
goto out_spin;
}
for_each_engine(engine, gt, id) {
struct ttm_operation_ctx ctx = {
.interruptible = true
};
struct dma_fence *spin_fence;
struct intel_context *ce;
struct i915_request *rq;
struct i915_deps deps;
ce = intel_context_create(engine);
if (IS_ERR(ce)) {
err = PTR_ERR(ce);
goto out_ce;
}
/*
* Use MI_NOOP, making the spinner non-preemptible. If there
* is a code path where we fail async operation due to the
* running spinner, we will block and fail to end the
* spinner resulting in a deadlock. But with a non-
* preemptible spinner, hangcheck will terminate the spinner
* for us, and we will later detect that and fail the test.
*/
rq = igt_spinner_create_request(&spin, ce, MI_NOOP);
intel_context_put(ce);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto out_ce;
}
i915_deps_init(&deps, GFP_KERNEL);
err = i915_deps_add_dependency(&deps, &rq->fence, &ctx);
spin_fence = dma_fence_get(&rq->fence);
i915_request_add(rq);
if (err)
goto out_ce;
err = __igt_lmem_pages_migrate(gt, &ppgtt->vm, &deps, &spin,
spin_fence, false);
i915_deps_fini(&deps);
dma_fence_put(spin_fence);
if (err)
goto out_ce;
}
out_ce:
igt_spinner_fini(&spin);
out_spin:
i915_vm_put(&ppgtt->vm);
return err;
}
/*
* Setting ASYNC_FAIL_ALLOC to 2 will simulate memory allocation failure while
* arming the migration error check and block async migration. This
* will cause us to deadlock and hangcheck will terminate the spinner
* causing the test to fail.
*/
#define ASYNC_FAIL_ALLOC 1
static int igt_lmem_async_migrate(void *arg)
{
int fail_gpu, fail_alloc, ban_memcpy, ret;
struct intel_gt *gt = arg;
for (fail_gpu = 0; fail_gpu < 2; ++fail_gpu) {
for (fail_alloc = 0; fail_alloc < ASYNC_FAIL_ALLOC; ++fail_alloc) {
for (ban_memcpy = 0; ban_memcpy < 2; ++ban_memcpy) {
pr_info("Simulated failure modes: gpu: %d, alloc: %d, ban_memcpy: %d\n",
fail_gpu, fail_alloc, ban_memcpy);
i915_ttm_migrate_set_ban_memcpy(ban_memcpy);
i915_ttm_migrate_set_failure_modes(fail_gpu,
fail_alloc);
ret = igt_async_migrate(gt);
if (fail_gpu && ban_memcpy) {
struct intel_gt *__gt;
unsigned int id;
if (ret != -EIO) {
pr_err("expected -EIO, got (%d)\n", ret);
ret = -EINVAL;
} else {
ret = 0;
}
for_each_gt(__gt, gt->i915, id) {
intel_wakeref_t wakeref;
bool wedged;
mutex_lock(&__gt->reset.mutex);
wedged = test_bit(I915_WEDGED, &__gt->reset.flags);
mutex_unlock(&__gt->reset.mutex);
if (fail_gpu && !fail_alloc) {
if (!wedged) {
pr_err("gt(%u) not wedged\n", id);
ret = -EINVAL;
continue;
}
} else if (wedged) {
pr_err("gt(%u) incorrectly wedged\n", id);
ret = -EINVAL;
} else {
continue;
}
wakeref = intel_runtime_pm_get(__gt->uncore->rpm);
igt_global_reset_lock(__gt);
intel_gt_reset(__gt, ALL_ENGINES, NULL);
igt_global_reset_unlock(__gt);
intel_runtime_pm_put(__gt->uncore->rpm, wakeref);
}
}
if (ret)
goto out_err;
}
}
}
out_err:
i915_ttm_migrate_set_failure_modes(false, false);
i915_ttm_migrate_set_ban_memcpy(false);
return ret;
}
int i915_gem_migrate_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_smem_create_migrate),
SUBTEST(igt_lmem_create_migrate),
SUBTEST(igt_same_create_migrate),
SUBTEST(igt_lmem_pages_failsafe_migrate),
SUBTEST(igt_lmem_async_migrate),
};
if (!HAS_LMEM(i915))
return 0;
return intel_gt_live_subtests(tests, to_gt(i915));
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,99 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#include "i915_selftest.h"
#include "huge_gem_object.h"
#include "selftests/igt_flush_test.h"
#include "selftests/mock_gem_device.h"
static int igt_gem_object(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
int err;
/* Basic test to ensure we can create an object */
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
pr_err("i915_gem_object_create failed, err=%d\n", err);
goto out;
}
err = 0;
i915_gem_object_put(obj);
out:
return err;
}
static int igt_gem_huge(void *arg)
{
const unsigned long nreal = 509; /* just to be awkward */
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
unsigned long n;
int err;
/* Basic sanitycheck of our huge fake object allocation */
obj = huge_gem_object(i915,
nreal * PAGE_SIZE,
to_gt(i915)->ggtt->vm.total + PAGE_SIZE);
if (IS_ERR(obj))
return PTR_ERR(obj);
err = i915_gem_object_pin_pages_unlocked(obj);
if (err) {
pr_err("Failed to allocate %lu pages (%lu total), err=%d\n",
nreal, obj->base.size / PAGE_SIZE, err);
goto out;
}
for (n = 0; n < obj->base.size / PAGE_SIZE; n++) {
if (i915_gem_object_get_page(obj, n) !=
i915_gem_object_get_page(obj, n % nreal)) {
pr_err("Page lookup mismatch at index %lu [%lu]\n",
n, n % nreal);
err = -EINVAL;
goto out_unpin;
}
}
out_unpin:
i915_gem_object_unpin_pages(obj);
out:
i915_gem_object_put(obj);
return err;
}
int i915_gem_object_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_gem_object),
};
struct drm_i915_private *i915;
int err;
i915 = mock_gem_device();
if (!i915)
return -ENOMEM;
err = i915_subtests(tests, i915);
mock_destroy_device(i915);
return err;
}
int i915_gem_object_live_selftests(struct drm_i915_private *i915)
{
static const struct i915_subtest tests[] = {
SUBTEST(igt_gem_huge),
};
return i915_live_subtests(tests, i915);
}
@@ -0,0 +1,87 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#include "i915_selftest.h"
#include "selftests/mock_gem_device.h"
static int mock_phys_object(void *arg)
{
struct drm_i915_private *i915 = arg;
struct drm_i915_gem_object *obj;
int err;
/* Create an object and bind it to a contiguous set of physical pages,
* i.e. exercise the i915_gem_object_phys API.
*/
obj = i915_gem_object_create_shmem(i915, PAGE_SIZE);
if (IS_ERR(obj)) {
err = PTR_ERR(obj);
pr_err("i915_gem_object_create failed, err=%d\n", err);
goto out;
}
i915_gem_object_lock(obj, NULL);
if (!i915_gem_object_has_struct_page(obj)) {
i915_gem_object_unlock(obj);
err = -EINVAL;
pr_err("shmem has no struct page\n");
goto out_obj;
}
err = i915_gem_object_attach_phys(obj, PAGE_SIZE);
i915_gem_object_unlock(obj);
if (err) {
pr_err("i915_gem_object_attach_phys failed, err=%d\n", err);
goto out_obj;
}
if (i915_gem_object_has_struct_page(obj)) {
pr_err("i915_gem_object_attach_phys did not create a phys object\n");
err = -EINVAL;
goto out_obj;
}
if (!atomic_read(&obj->mm.pages_pin_count)) {
pr_err("i915_gem_object_attach_phys did not pin its phys pages\n");
err = -EINVAL;
goto out_obj;
}
/* Make the object dirty so that put_pages must do copy back the data */
i915_gem_object_lock(obj, NULL);
err = i915_gem_object_set_to_gtt_domain(obj, true);
i915_gem_object_unlock(obj);
if (err) {
pr_err("i915_gem_object_set_to_gtt_domain failed with err=%d\n",
err);
goto out_obj;
}
out_obj:
i915_gem_object_put(obj);
out:
return err;
}
int i915_gem_phys_mock_selftests(void)
{
static const struct i915_subtest tests[] = {
SUBTEST(mock_phys_object),
};
struct drm_i915_private *i915;
int err;
i915 = mock_gem_device();
if (!i915)
return -ENOMEM;
err = i915_subtests(tests, i915);
mock_destroy_device(i915);
return err;
}
@@ -0,0 +1,157 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#include "igt_gem_utils.h"
#include "gem/i915_gem_context.h"
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_pm.h"
#include "gt/intel_context.h"
#include "gt/intel_gpu_commands.h"
#include "gt/intel_gt.h"
#include "i915_vma.h"
#include "i915_drv.h"
#include "i915_request.h"
struct i915_request *
igt_request_alloc(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
{
struct intel_context *ce;
struct i915_request *rq;
/*
* Pinning the contexts may generate requests in order to acquire
* GGTT space, so do this first before we reserve a seqno for
* ourselves.
*/
ce = i915_gem_context_get_engine(ctx, engine->legacy_idx);
if (IS_ERR(ce))
return ERR_CAST(ce);
rq = intel_context_create_request(ce);
intel_context_put(ce);
return rq;
}
struct i915_vma *
igt_emit_store_dw(struct i915_vma *vma,
u64 offset,
unsigned long count,
u32 val)
{
struct drm_i915_gem_object *obj;
const int ver = GRAPHICS_VER(vma->vm->i915);
unsigned long n, size;
u32 *cmd;
int err;
size = (4 * count + 1) * sizeof(u32);
size = round_up(size, PAGE_SIZE);
obj = i915_gem_object_create_internal(vma->vm->i915, size);
if (IS_ERR(obj))
return ERR_CAST(obj);
cmd = i915_gem_object_pin_map_unlocked(obj, I915_MAP_WC);
if (IS_ERR(cmd)) {
err = PTR_ERR(cmd);
goto err;
}
GEM_BUG_ON(offset + (count - 1) * PAGE_SIZE > i915_vma_size(vma));
offset += i915_vma_offset(vma);
for (n = 0; n < count; n++) {
if (ver >= 8) {
*cmd++ = MI_STORE_DWORD_IMM_GEN4;
*cmd++ = lower_32_bits(offset);
*cmd++ = upper_32_bits(offset);
*cmd++ = val;
} else if (ver >= 4) {
*cmd++ = MI_STORE_DWORD_IMM_GEN4 |
(ver < 6 ? MI_USE_GGTT : 0);
*cmd++ = 0;
*cmd++ = offset;
*cmd++ = val;
} else {
*cmd++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
*cmd++ = offset;
*cmd++ = val;
}
offset += PAGE_SIZE;
}
*cmd = MI_BATCH_BUFFER_END;
i915_gem_object_flush_map(obj);
i915_gem_object_unpin_map(obj);
intel_gt_chipset_flush(vma->vm->gt);
vma = i915_vma_instance(obj, vma->vm, NULL);
if (IS_ERR(vma)) {
err = PTR_ERR(vma);
goto err;
}
err = i915_vma_pin(vma, 0, 0, PIN_USER);
if (err)
goto err;
return vma;
err:
i915_gem_object_put(obj);
return ERR_PTR(err);
}
int igt_gpu_fill_dw(struct intel_context *ce,
struct i915_vma *vma, u64 offset,
unsigned long count, u32 val)
{
struct i915_request *rq;
struct i915_vma *batch;
unsigned int flags;
int err;
GEM_BUG_ON(!intel_engine_can_store_dword(ce->engine));
GEM_BUG_ON(!i915_vma_is_pinned(vma));
batch = igt_emit_store_dw(vma, offset, count, val);
if (IS_ERR(batch))
return PTR_ERR(batch);
rq = intel_context_create_request(ce);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
goto err_batch;
}
err = igt_vma_move_to_active_unlocked(batch, rq, 0);
if (err)
goto skip_request;
err = igt_vma_move_to_active_unlocked(vma, rq, EXEC_OBJECT_WRITE);
if (err)
goto skip_request;
flags = 0;
if (GRAPHICS_VER(ce->vm->i915) <= 5)
flags |= I915_DISPATCH_SECURE;
err = rq->engine->emit_bb_start(rq,
i915_vma_offset(batch),
i915_vma_size(batch),
flags);
skip_request:
if (err)
i915_request_set_error_once(rq, err);
i915_request_add(rq);
err_batch:
i915_vma_unpin_and_release(&batch, 0);
return err;
}
@@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2018 Intel Corporation
*/
#ifndef __IGT_GEM_UTILS_H__
#define __IGT_GEM_UTILS_H__
#include <linux/types.h>
#include "i915_vma.h"
struct i915_request;
struct i915_gem_context;
struct i915_vma;
struct intel_context;
struct intel_engine_cs;
struct i915_request *
igt_request_alloc(struct i915_gem_context *ctx, struct intel_engine_cs *engine);
struct i915_vma *
igt_emit_store_dw(struct i915_vma *vma,
u64 offset,
unsigned long count,
u32 val);
int igt_gpu_fill_dw(struct intel_context *ce,
struct i915_vma *vma, u64 offset,
unsigned long count, u32 val);
static inline int __must_check
igt_vma_move_to_active_unlocked(struct i915_vma *vma, struct i915_request *rq,
unsigned int flags)
{
int err;
i915_vma_lock(vma);
err = i915_vma_move_to_active(vma, rq, flags);
i915_vma_unlock(vma);
return err;
}
#endif /* __IGT_GEM_UTILS_H__ */
@@ -0,0 +1,142 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#include "i915_file_private.h"
#include "mock_context.h"
#include "selftests/mock_drm.h"
#include "selftests/mock_gtt.h"
struct i915_gem_context *
mock_context(struct drm_i915_private *i915,
const char *name)
{
struct i915_gem_context *ctx;
struct i915_gem_engines *e;
struct intel_sseu null_sseu = {};
ctx = kzalloc_obj(*ctx);
if (!ctx)
return NULL;
kref_init(&ctx->ref);
INIT_LIST_HEAD(&ctx->link);
ctx->i915 = i915;
INIT_WORK(&ctx->release_work, i915_gem_context_release_work);
mutex_init(&ctx->mutex);
spin_lock_init(&ctx->stale.lock);
INIT_LIST_HEAD(&ctx->stale.engines);
i915_gem_context_set_persistence(ctx);
if (name) {
struct i915_ppgtt *ppgtt;
strscpy(ctx->name, name, sizeof(ctx->name));
ppgtt = mock_ppgtt(i915, name);
if (!ppgtt)
goto err_free;
ctx->vm = &ppgtt->vm;
}
mutex_init(&ctx->engines_mutex);
e = default_engines(ctx, null_sseu);
if (IS_ERR(e))
goto err_vm;
RCU_INIT_POINTER(ctx->engines, e);
INIT_RADIX_TREE(&ctx->handles_vma, GFP_KERNEL);
mutex_init(&ctx->lut_mutex);
return ctx;
err_vm:
if (ctx->vm)
i915_vm_put(ctx->vm);
err_free:
kfree(ctx);
return NULL;
}
void mock_context_close(struct i915_gem_context *ctx)
{
context_close(ctx);
}
void mock_init_contexts(struct drm_i915_private *i915)
{
init_contexts(&i915->gem.contexts);
}
struct i915_gem_context *
live_context(struct drm_i915_private *i915, struct file *file)
{
struct drm_i915_file_private *fpriv = to_drm_file(file)->driver_priv;
struct i915_gem_proto_context *pc;
struct i915_gem_context *ctx;
int err;
u32 id;
pc = proto_context_create(fpriv, i915, 0);
if (IS_ERR(pc))
return ERR_CAST(pc);
ctx = i915_gem_create_context(i915, pc);
proto_context_close(i915, pc);
if (IS_ERR(ctx))
return ctx;
i915_gem_context_set_no_error_capture(ctx);
err = xa_alloc(&fpriv->context_xa, &id, NULL, xa_limit_32b, GFP_KERNEL);
if (err < 0)
goto err_ctx;
gem_context_register(ctx, fpriv, id);
return ctx;
err_ctx:
context_close(ctx);
return ERR_PTR(err);
}
struct i915_gem_context *
kernel_context(struct drm_i915_private *i915,
struct i915_address_space *vm)
{
struct i915_gem_context *ctx;
struct i915_gem_proto_context *pc;
pc = proto_context_create(NULL, i915, 0);
if (IS_ERR(pc))
return ERR_CAST(pc);
if (vm) {
if (pc->vm)
i915_vm_put(pc->vm);
pc->vm = i915_vm_get(vm);
}
ctx = i915_gem_create_context(i915, pc);
proto_context_close(i915, pc);
if (IS_ERR(ctx))
return ctx;
i915_gem_context_clear_bannable(ctx);
i915_gem_context_set_persistence(ctx);
i915_gem_context_set_no_error_capture(ctx);
return ctx;
}
void kernel_context_close(struct i915_gem_context *ctx)
{
context_close(ctx);
}
@@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#ifndef __MOCK_CONTEXT_H
#define __MOCK_CONTEXT_H
struct file;
struct drm_i915_private;
struct intel_engine_cs;
struct i915_address_space;
void mock_init_contexts(struct drm_i915_private *i915);
struct i915_gem_context *
mock_context(struct drm_i915_private *i915,
const char *name);
void mock_context_close(struct i915_gem_context *ctx);
struct i915_gem_context *
live_context(struct drm_i915_private *i915, struct file *file);
struct i915_gem_context *kernel_context(struct drm_i915_private *i915,
struct i915_address_space *vm);
void kernel_context_close(struct i915_gem_context *ctx);
#endif /* !__MOCK_CONTEXT_H */
@@ -0,0 +1,133 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#include <linux/vmalloc.h>
#include "mock_dmabuf.h"
static struct sg_table *mock_map_dma_buf(struct dma_buf_attachment *attachment,
enum dma_data_direction dir)
{
struct mock_dmabuf *mock = to_mock(attachment->dmabuf);
struct sg_table *st;
struct scatterlist *sg;
int i, err;
st = kmalloc_obj(*st);
if (!st)
return ERR_PTR(-ENOMEM);
err = sg_alloc_table(st, mock->npages, GFP_KERNEL);
if (err)
goto err_free;
sg = st->sgl;
for (i = 0; i < mock->npages; i++) {
sg_set_page(sg, mock->pages[i], PAGE_SIZE, 0);
sg = sg_next(sg);
}
err = dma_map_sgtable(attachment->dev, st, dir, 0);
if (err)
goto err_st;
return st;
err_st:
sg_free_table(st);
err_free:
kfree(st);
return ERR_PTR(err);
}
static void mock_unmap_dma_buf(struct dma_buf_attachment *attachment,
struct sg_table *st,
enum dma_data_direction dir)
{
dma_unmap_sgtable(attachment->dev, st, dir, 0);
sg_free_table(st);
kfree(st);
}
static void mock_dmabuf_release(struct dma_buf *dma_buf)
{
struct mock_dmabuf *mock = to_mock(dma_buf);
int i;
for (i = 0; i < mock->npages; i++)
put_page(mock->pages[i]);
kfree(mock);
}
static int mock_dmabuf_vmap(struct dma_buf *dma_buf, struct iosys_map *map)
{
struct mock_dmabuf *mock = to_mock(dma_buf);
void *vaddr;
vaddr = vm_map_ram(mock->pages, mock->npages, 0);
if (!vaddr)
return -ENOMEM;
iosys_map_set_vaddr(map, vaddr);
return 0;
}
static void mock_dmabuf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map)
{
struct mock_dmabuf *mock = to_mock(dma_buf);
vm_unmap_ram(map->vaddr, mock->npages);
}
static int mock_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
{
return -ENODEV;
}
static const struct dma_buf_ops mock_dmabuf_ops = {
.map_dma_buf = mock_map_dma_buf,
.unmap_dma_buf = mock_unmap_dma_buf,
.release = mock_dmabuf_release,
.mmap = mock_dmabuf_mmap,
.vmap = mock_dmabuf_vmap,
.vunmap = mock_dmabuf_vunmap,
};
static struct dma_buf *mock_dmabuf(int npages)
{
struct mock_dmabuf *mock;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct dma_buf *dmabuf;
int i;
mock = kmalloc_flex(*mock, pages, npages);
if (!mock)
return ERR_PTR(-ENOMEM);
mock->npages = npages;
for (i = 0; i < npages; i++) {
mock->pages[i] = alloc_page(GFP_KERNEL);
if (!mock->pages[i])
goto err;
}
exp_info.ops = &mock_dmabuf_ops;
exp_info.size = npages * PAGE_SIZE;
exp_info.flags = O_CLOEXEC;
exp_info.priv = mock;
dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(dmabuf))
goto err;
return dmabuf;
err:
while (i--)
put_page(mock->pages[i]);
kfree(mock);
return ERR_PTR(-ENOMEM);
}
@@ -0,0 +1,22 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#ifndef __MOCK_DMABUF_H__
#define __MOCK_DMABUF_H__
#include <linux/dma-buf.h>
struct mock_dmabuf {
int npages;
struct page *pages[];
};
static inline struct mock_dmabuf *to_mock(struct dma_buf *buf)
{
return buf->priv;
}
#endif /* !__MOCK_DMABUF_H__ */
@@ -0,0 +1,16 @@
/*
* SPDX-License-Identifier: MIT
*
* Copyright © 2016 Intel Corporation
*/
#ifndef __MOCK_GEM_OBJECT_H__
#define __MOCK_GEM_OBJECT_H__
#include "gem/i915_gem_object_types.h"
struct mock_object {
struct drm_i915_gem_object base;
};
#endif /* !__MOCK_GEM_OBJECT_H__ */