#include <linux/kernel.h>
#include <drm/drm_managed.h>
#include "instructions/xe_mi_commands.h"
#include "xe_bo.h"
#include "xe_device_types.h"
#include "xe_map.h"
#include "xe_mem_pool.h"
#include "xe_mem_pool_types.h"
#include "xe_tile_printk.h"
struct xe_mem_pool {
struct drm_mm base;
struct xe_bo *bo;
struct xe_bo *shadow;
struct mutex swap_guard;
void *cpu_addr;
bool is_iomem;
};
static struct xe_mem_pool *node_to_pool(struct xe_mem_pool_node *node)
{
return container_of(node->sa_node.mm, struct xe_mem_pool, base);
}
static struct xe_tile *pool_to_tile(struct xe_mem_pool *pool)
{
return pool->bo->tile;
}
static void fini_pool_action(struct drm_device *drm, void *arg)
{
struct xe_mem_pool *pool = arg;
if (pool->is_iomem)
kvfree(pool->cpu_addr);
drm_mm_takedown(&pool->base);
}
static int pool_shadow_init(struct xe_mem_pool *pool)
{
struct xe_tile *tile = pool->bo->tile;
struct xe_device *xe = tile_to_xe(tile);
struct xe_bo *shadow;
int ret;
xe_assert(xe, !pool->shadow);
ret = drmm_mutex_init(&xe->drm, &pool->swap_guard);
if (ret)
return ret;
if (IS_ENABLED(CONFIG_PROVE_LOCKING)) {
fs_reclaim_acquire(GFP_KERNEL);
might_lock(&pool->swap_guard);
fs_reclaim_release(GFP_KERNEL);
}
shadow = xe_managed_bo_create_pin_map(xe, tile,
xe_bo_size(pool->bo),
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
XE_BO_FLAG_PINNED_NORESTORE);
if (IS_ERR(shadow))
return PTR_ERR(shadow);
pool->shadow = shadow;
return 0;
}
struct xe_mem_pool *xe_mem_pool_init(struct xe_tile *tile, u32 size,
u32 guard, int flags)
{
struct xe_device *xe = tile_to_xe(tile);
struct xe_mem_pool *pool;
struct xe_bo *bo;
u32 managed_size;
int ret;
xe_tile_assert(tile, size > guard);
managed_size = size - guard;
pool = drmm_kzalloc(&xe->drm, sizeof(*pool), GFP_KERNEL);
if (!pool)
return ERR_PTR(-ENOMEM);
bo = xe_managed_bo_create_pin_map(xe, tile, size,
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
XE_BO_FLAG_PINNED_NORESTORE);
if (IS_ERR(bo)) {
xe_tile_err(tile, "Failed to prepare %uKiB BO for mem pool (%pe)\n",
size / SZ_1K, bo);
return ERR_CAST(bo);
}
pool->bo = bo;
pool->is_iomem = bo->vmap.is_iomem;
if (pool->is_iomem) {
pool->cpu_addr = kvzalloc(size, GFP_KERNEL);
if (!pool->cpu_addr)
return ERR_PTR(-ENOMEM);
} else {
pool->cpu_addr = bo->vmap.vaddr;
}
if (flags & XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY) {
ret = pool_shadow_init(pool);
if (ret)
goto out_err;
}
drm_mm_init(&pool->base, 0, managed_size);
ret = drmm_add_action_or_reset(&xe->drm, fini_pool_action, pool);
if (ret)
return ERR_PTR(ret);
return pool;
out_err:
if (flags & XE_MEM_POOL_BO_FLAG_INIT_SHADOW_COPY)
xe_tile_err(tile,
"Failed to initialize shadow BO for mem pool (%d)\n", ret);
if (bo->vmap.is_iomem)
kvfree(pool->cpu_addr);
return ERR_PTR(ret);
}
void xe_mem_pool_sync(struct xe_mem_pool *pool)
{
struct xe_tile *tile = pool_to_tile(pool);
struct xe_device *xe = tile_to_xe(tile);
xe_tile_assert(tile, pool->shadow);
xe_map_memcpy_to(xe, &pool->shadow->vmap, 0,
pool->cpu_addr, xe_bo_size(pool->bo));
}
void xe_mem_pool_swap_shadow_locked(struct xe_mem_pool *pool)
{
struct xe_tile *tile = pool_to_tile(pool);
xe_tile_assert(tile, pool->shadow);
lockdep_assert_held(&pool->swap_guard);
swap(pool->bo, pool->shadow);
if (!pool->bo->vmap.is_iomem)
pool->cpu_addr = pool->bo->vmap.vaddr;
}
void xe_mem_pool_sync_shadow_locked(struct xe_mem_pool_node *node)
{
struct xe_mem_pool *pool = node_to_pool(node);
struct xe_tile *tile = pool_to_tile(pool);
struct xe_device *xe = tile_to_xe(tile);
struct drm_mm_node *sa_node = &node->sa_node;
xe_tile_assert(tile, pool->shadow);
lockdep_assert_held(&pool->swap_guard);
xe_map_memcpy_to(xe, &pool->shadow->vmap,
sa_node->start,
pool->cpu_addr + sa_node->start,
sa_node->size);
}
u64 xe_mem_pool_gpu_addr(struct xe_mem_pool *pool)
{
return xe_bo_ggtt_addr(pool->bo);
}
void *xe_mem_pool_cpu_addr(struct xe_mem_pool *pool)
{
return pool->cpu_addr;
}
struct mutex *xe_mem_pool_bo_swap_guard(struct xe_mem_pool *pool)
{
if (!pool->shadow)
return NULL;
return &pool->swap_guard;
}
void xe_mem_pool_bo_flush_write(struct xe_mem_pool_node *node)
{
struct xe_mem_pool *pool = node_to_pool(node);
struct xe_tile *tile = pool_to_tile(pool);
struct xe_device *xe = tile_to_xe(tile);
struct drm_mm_node *sa_node = &node->sa_node;
if (!pool->bo->vmap.is_iomem)
return;
xe_map_memcpy_to(xe, &pool->bo->vmap, sa_node->start,
pool->cpu_addr + sa_node->start,
sa_node->size);
}
void xe_mem_pool_bo_sync_read(struct xe_mem_pool_node *node)
{
struct xe_mem_pool *pool = node_to_pool(node);
struct xe_tile *tile = pool_to_tile(pool);
struct xe_device *xe = tile_to_xe(tile);
struct drm_mm_node *sa_node = &node->sa_node;
if (!pool->bo->vmap.is_iomem)
return;
xe_map_memcpy_from(xe, pool->cpu_addr + sa_node->start,
&pool->bo->vmap, sa_node->start, sa_node->size);
}
struct xe_mem_pool_node *xe_mem_pool_alloc_node(void)
{
struct xe_mem_pool_node *node = kzalloc_obj(*node);
if (!node)
return ERR_PTR(-ENOMEM);
return node;
}
int xe_mem_pool_insert_node(struct xe_mem_pool *pool,
struct xe_mem_pool_node *node, u32 size)
{
if (!pool)
return -EINVAL;
return drm_mm_insert_node(&pool->base, &node->sa_node, size);
}
void xe_mem_pool_free_node(struct xe_mem_pool_node *node)
{
if (!node)
return;
drm_mm_remove_node(&node->sa_node);
kfree(node);
}
void *xe_mem_pool_node_cpu_addr(struct xe_mem_pool_node *node)
{
struct xe_mem_pool *pool = node_to_pool(node);
return xe_mem_pool_cpu_addr(pool) + node->sa_node.start;
}
void xe_mem_pool_dump(struct xe_mem_pool *pool, struct drm_printer *p)
{
drm_mm_print(&pool->base, p);
}