mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 03:11:11 -04:00
UAPI Changes: - restrict multi-lrc to VCS/VECS engines (Xin Wang) - Introduce a flag to disallow vm overcommit in fault mode (Thomas) - update used tracking kernel-doc (Auld, Fixes) - Some bind queue fixes (Auld, Fixes) Cross-subsystem Changes: - Split drm_suballoc_new() into SA alloc and init helpers (Satya, Fixes) - pass pagemap_addr by reference (Arnd, Fixes) - Revert "drm/pagemap: Disable device-to-device migration" (Thomas) - Fix unbalanced unlock in drm_gpusvm_scan_mm (Maciej, Fixes) - Small GPUSVM fixes (Brost, Fixes) - Fix xe SVM configs (Thomas, Fixes) Core Changes: - Fix a hmm_range_fault() livelock / starvation problem (Thomas, Fixes) Driver Changes: - Fix leak on xa_store failure (Shuicheng, Fixes) - Correct implementation of Wa_16025250150 (Roper, Fixes) - Refactor context init into xe_lrc_ctx_init (Raag) - Fix GSC proxy cleanup on early initialization failure (Zhanjun) - Fix exec queue creation during post-migration recovery (Tomasz, Fixes) - Apply windower hardware filtering setting on Xe3 and Xe3p (Roper) - Free ctx_restore_mid_bb in release (Shuicheng, Fixes) - Drop stale MCR steering TODO comment (Roper) - dGPU memory optimizations (Brost) - Do not preempt fence signaling CS instructions (Brost, Fixes) - Revert "drm/xe/compat: Remove unused i915_reg.h from compat header" (Uma) - Don't expose display modparam if no display support (Wajdeczko) - Some VRAM flag improvements (Wajdeczko) - Misc fix for xe_guc_ct.c (Shuicheng, Fixes) - Remove unused i915_reg.h from compat header (Uma) - Workaround cleanup & simplification (Roper) - Add prefetch pagefault support for Xe3p (Varun) - Fix fs_reclaim deadlock caused by CCS save/restore (Satya, Fixes) - Cleanup partially initialized sync on parse failure (Shuicheng, Fixes) - Allow to change VFs VRAM quota using sysfs (Michal) - Increase GuC log sizes in debug builds (Tomasz) - Wa_18041344222 changes (Harish) - Add Wa_14026781792 (Niton) - Add debugfs facility to catch RTP mistakes (Roper) - Convert GT stats to per-cpu counters (Brost) - Prevent unintended VRAM channel creation (Karthik) - Privatize struct xe_ggtt (Maarten) - remove unnecessary struct dram_info forward declaration (Jani) - pagefault refactors (Brost) - Apply Wa_14024997852 (Arvind) - Redirect faults to dummy page for wedged device (Raag, Fixes) - Force EXEC_QUEUE_FLAG_KERNEL for kernel internal VMs (Piotr) - Stop applying Wa_16018737384 from Xe3 onward (Roper) - Add new XeCore fuse registers to VF runtime regs (Roper) - Update xe_device_declare_wedged() error log (Raag) - Make xe_modparam.force_vram_bar_size signed (Shuicheng, Fixes) - Avoid reading media version when media GT is disabled (Piotr, Fixes) - Fix handling of Wa_14019988906 & Wa_14019877138 (Roper, Fixes) - Basic enabling patches for Xe3p_LPG and NVL-P (Gustavo, Roper, Shekhar) - Avoid double-adjust in 64-bit reads (Shuicheng, Fixes) - Allow VF to initialize MCR tables (Wajdeczko) - Add Wa_14025883347 for GuC DMA failure on reset (Anirban) - Add bounds check on pat_index to prevent OOB kernel read in madvise (Jia, Fixes) - Fix the address range assert in ggtt_get_pte helper (Winiarski) - XeCore fuse register changes (Roper) - Add more info to powergate_info debugfs (Vinay) - Separate out GuC RC code (Vinay) - Fix g2g_test_array indexing (Pallavi) - Mutual exclusivity between CCS-mode and PF (Nareshkumar, Fixes) - Some more _types.h cleanups (Wajdeczko) - Fix sysfs initialization (Wajdeczko, Fixes) - Drop unnecessary goto in xe_device_create (Roper) - Disable D3Cold for BMG only on specific platforms (Karthik, Fixes) - Add sriov.admin_only_pf attribute (Wajdeczko) - replace old wq(s), add WQ_PERCPU to alloc_workqueue (Marco) - Make MMIO communication more robust (Wajdeczko) - Fix warning of kerneldoc (Shuicheng, Fixes) - Fix topology query pointer advance (Shuicheng, Fixes) - use entry_dump callbacks for xe2+ PAT dumps (Xin Wang) - Fix kernel-doc warning in GuC scheduler ABI header (Chaitanya, Fixes) - Fix CFI violation in debugfs access (Daniele, Fixes) - Apply WA_16028005424 to Media (Balasubramani) - Fix typo in function kernel-doc (Wajdeczko) - Protect priority against concurrent access (Niranjana) - Fix nvm aux resource cleanup (Shuicheng, Fixes) - Fix is_bound() pci_dev lifetime (Shuicheng, Fixes) - Use CLASS() for forcewake in xe_gt_enable_comp_1wcoh (Shuicheng) - Reset VF GuC state on fini (Wajdeczko) - Move _THIS_IP_ usage from xe_vm_create() to dedicated function (Nathan Chancellor, Fixes) - Unregister drm device on probe error (Shuicheng, Fixes) - Disable DCC on PTL (Vinay, Fixes) - Fix Wa_18022495364 (Tvrtko, Fixes) - Skip address copy for sync-only execs (Shuicheng, Fixes) - derive mem copy capability from graphics version (Nitin, Fixes) - Use DRM_BUDDY_CONTIGUOUS_ALLOCATION for contiguous allocations (Sanjay) - Context based TLB invalidations (Brost) - Enable multi_queue on xe3p_xpc (Brost, Niranjana) - Remove check for gt in xe_query (Nakshtra) - Reduce LRC timestamp stuck message on VFs to notice (Brost, Fixes) Signed-off-by: Dave Airlie <airlied@redhat.com> From: Matthew Brost <matthew.brost@intel.com> Link: https://patch.msgid.link/aaYR5G2MHjOEMXPW@lstrano-desk.jf.intel.com
610 lines
15 KiB
C
610 lines
15 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2023 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/align.h>
|
|
|
|
#include <drm/drm_managed.h>
|
|
|
|
#include "regs/xe_gt_regs.h"
|
|
#include "regs/xe_mert_regs.h"
|
|
|
|
#include "xe_assert.h"
|
|
#include "xe_bo.h"
|
|
#include "xe_tlb_inval.h"
|
|
#include "xe_lmtt.h"
|
|
#include "xe_map.h"
|
|
#include "xe_mert.h"
|
|
#include "xe_mmio.h"
|
|
#include "xe_res_cursor.h"
|
|
#include "xe_sriov.h"
|
|
#include "xe_tile.h"
|
|
#include "xe_tile_sriov_printk.h"
|
|
|
|
/**
|
|
* DOC: Local Memory Translation Table
|
|
*
|
|
* The Local Memory Translation Table (LMTT) provides additional abstraction
|
|
* when Virtual Function (VF) is accessing device Local Memory (VRAM).
|
|
*
|
|
* The Root LMTT Page Directory contains one entry for each VF. Entries are
|
|
* indexed by the function number (1-based, index 0 is unused).
|
|
*
|
|
* See `Two-Level LMTT Structure`_ and `Multi-Level LMTT Structure`_.
|
|
*/
|
|
|
|
#define lmtt_assert(lmtt, condition) xe_tile_assert(lmtt_to_tile(lmtt), condition)
|
|
#define lmtt_debug(lmtt, msg...) xe_tile_sriov_dbg_verbose(lmtt_to_tile(lmtt), "LMTT: " msg)
|
|
|
|
static bool xe_has_multi_level_lmtt(struct xe_device *xe)
|
|
{
|
|
return GRAPHICS_VERx100(xe) >= 1260;
|
|
}
|
|
|
|
static struct xe_tile *lmtt_to_tile(struct xe_lmtt *lmtt)
|
|
{
|
|
return container_of(lmtt, struct xe_tile, sriov.pf.lmtt);
|
|
}
|
|
|
|
static struct xe_device *lmtt_to_xe(struct xe_lmtt *lmtt)
|
|
{
|
|
return tile_to_xe(lmtt_to_tile(lmtt));
|
|
}
|
|
|
|
static u64 lmtt_page_size(struct xe_lmtt *lmtt)
|
|
{
|
|
return BIT_ULL(lmtt->ops->lmtt_pte_shift(0));
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_page_size() - Get LMTT page size.
|
|
* @lmtt: the &xe_lmtt
|
|
*
|
|
* This function shall be called only by PF.
|
|
*
|
|
* Return: LMTT page size.
|
|
*/
|
|
u64 xe_lmtt_page_size(struct xe_lmtt *lmtt)
|
|
{
|
|
lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt)));
|
|
lmtt_assert(lmtt, xe_device_has_lmtt(lmtt_to_xe(lmtt)));
|
|
lmtt_assert(lmtt, lmtt->ops);
|
|
|
|
return lmtt_page_size(lmtt);
|
|
}
|
|
|
|
static struct xe_lmtt_pt *lmtt_pt_alloc(struct xe_lmtt *lmtt, unsigned int level)
|
|
{
|
|
unsigned int num_entries = level ? lmtt->ops->lmtt_pte_num(level) : 0;
|
|
struct xe_lmtt_pt *pt;
|
|
struct xe_bo *bo;
|
|
int err;
|
|
|
|
pt = kzalloc_flex(*pt, entries, num_entries);
|
|
if (!pt) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
bo = xe_bo_create_pin_map_novm(lmtt_to_xe(lmtt), lmtt_to_tile(lmtt),
|
|
PAGE_ALIGN(lmtt->ops->lmtt_pte_size(level) *
|
|
lmtt->ops->lmtt_pte_num(level)),
|
|
ttm_bo_type_kernel,
|
|
XE_BO_FLAG_VRAM_IF_DGFX(lmtt_to_tile(lmtt)) |
|
|
XE_BO_FLAG_NEEDS_64K, false);
|
|
if (IS_ERR(bo)) {
|
|
err = PTR_ERR(bo);
|
|
goto out_free_pt;
|
|
}
|
|
|
|
lmtt_assert(lmtt, xe_bo_is_vram(bo));
|
|
lmtt_debug(lmtt, "level=%u addr=%#llx\n", level, (u64)xe_bo_main_addr(bo, XE_PAGE_SIZE));
|
|
|
|
xe_map_memset(lmtt_to_xe(lmtt), &bo->vmap, 0, 0, xe_bo_size(bo));
|
|
|
|
pt->level = level;
|
|
pt->bo = bo;
|
|
return pt;
|
|
|
|
out_free_pt:
|
|
kfree(pt);
|
|
out:
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
static void lmtt_pt_free(struct xe_lmtt_pt *pt)
|
|
{
|
|
lmtt_debug(&pt->bo->tile->sriov.pf.lmtt, "level=%u addr=%llx\n",
|
|
pt->level, (u64)xe_bo_main_addr(pt->bo, XE_PAGE_SIZE));
|
|
|
|
xe_bo_unpin_map_no_vm(pt->bo);
|
|
kfree(pt);
|
|
}
|
|
|
|
static int lmtt_init_pd(struct xe_lmtt *lmtt)
|
|
{
|
|
struct xe_lmtt_pt *pd;
|
|
|
|
lmtt_assert(lmtt, !lmtt->pd);
|
|
lmtt_assert(lmtt, lmtt->ops->lmtt_root_pd_level());
|
|
|
|
pd = lmtt_pt_alloc(lmtt, lmtt->ops->lmtt_root_pd_level());
|
|
if (IS_ERR(pd))
|
|
return PTR_ERR(pd);
|
|
|
|
lmtt->pd = pd;
|
|
return 0;
|
|
}
|
|
|
|
static void lmtt_fini_pd(struct xe_lmtt *lmtt)
|
|
{
|
|
struct xe_lmtt_pt *pd = lmtt->pd;
|
|
unsigned int num_entries = lmtt->ops->lmtt_pte_num(pd->level);
|
|
unsigned int n = 0;
|
|
|
|
/* make sure we don't leak */
|
|
for (n = 0; n < num_entries; n++)
|
|
lmtt_assert(lmtt, !pd->entries[n]);
|
|
|
|
lmtt->pd = NULL;
|
|
lmtt_pt_free(pd);
|
|
}
|
|
|
|
static void fini_lmtt(struct drm_device *drm, void *arg)
|
|
{
|
|
struct xe_lmtt *lmtt = arg;
|
|
|
|
lmtt_assert(lmtt, !(!!lmtt->ops ^ !!lmtt->pd));
|
|
|
|
if (!lmtt->pd)
|
|
return;
|
|
|
|
lmtt_fini_pd(lmtt);
|
|
lmtt->ops = NULL;
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_init - LMTT software initialization.
|
|
* @lmtt: the &xe_lmtt to initialize
|
|
*
|
|
* The LMTT initialization requires two steps.
|
|
*
|
|
* The xe_lmtt_init() checks if LMTT is required on current device and selects
|
|
* and initialize proper variant of the LMTT Root Directory. Currently supported
|
|
* variants are `Two-Level LMTT Structure`_ and `Multi-Level LMTT Structure`_.
|
|
*
|
|
* In next step xe_lmtt_init_hw() will register this directory on the hardware.
|
|
*
|
|
* Notes:
|
|
* The LMTT allocations are managed and will be implicitly released on driver unload.
|
|
* This function shall be called only once and only when running as a PF driver.
|
|
* Any LMTT initialization failure should block VFs enabling.
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int xe_lmtt_init(struct xe_lmtt *lmtt)
|
|
{
|
|
struct xe_device *xe = lmtt_to_xe(lmtt);
|
|
int err;
|
|
|
|
lmtt_assert(lmtt, IS_SRIOV_PF(xe));
|
|
lmtt_assert(lmtt, !lmtt->ops);
|
|
|
|
if (!xe_device_has_lmtt(xe))
|
|
return 0;
|
|
|
|
if (xe_has_multi_level_lmtt(xe))
|
|
lmtt->ops = &lmtt_ml_ops;
|
|
else
|
|
lmtt->ops = &lmtt_2l_ops;
|
|
|
|
err = lmtt_init_pd(lmtt);
|
|
if (unlikely(err))
|
|
goto fail;
|
|
|
|
return drmm_add_action_or_reset(&xe->drm, fini_lmtt, lmtt);
|
|
|
|
fail:
|
|
lmtt->ops = NULL;
|
|
return err;
|
|
}
|
|
|
|
static void lmtt_setup_dir_ptr(struct xe_lmtt *lmtt)
|
|
{
|
|
struct xe_tile *tile = lmtt_to_tile(lmtt);
|
|
struct xe_device *xe = tile_to_xe(tile);
|
|
dma_addr_t offset = xe_bo_main_addr(lmtt->pd->bo, XE_PAGE_SIZE);
|
|
struct xe_gt *gt;
|
|
u32 config;
|
|
u8 id;
|
|
|
|
lmtt_debug(lmtt, "DIR offset %pad\n", &offset);
|
|
lmtt_assert(lmtt, xe_bo_is_vram(lmtt->pd->bo));
|
|
lmtt_assert(lmtt, IS_ALIGNED(offset, SZ_64K));
|
|
|
|
config = LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K);
|
|
|
|
for_each_gt_on_tile(gt, tile, id)
|
|
xe_mmio_write32(>->mmio,
|
|
GRAPHICS_VER(xe) >= 20 ? XE2_LMEM_CFG : LMEM_CFG,
|
|
config);
|
|
|
|
if (xe_device_has_mert(xe) && xe_tile_is_root(tile))
|
|
xe_mmio_write32(&tile->mmio, MERT_LMEM_CFG, config);
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_init_hw - Perform LMTT hardware initialization.
|
|
* @lmtt: the &xe_lmtt to initialize
|
|
*
|
|
* This function is a second step of the LMTT initialization.
|
|
* This function registers LMTT Root Directory prepared in xe_lmtt_init().
|
|
*
|
|
* This function shall be called after every hardware reset.
|
|
* This function shall be called only when running as a PF driver.
|
|
*/
|
|
void xe_lmtt_init_hw(struct xe_lmtt *lmtt)
|
|
{
|
|
if (!lmtt->pd)
|
|
return;
|
|
|
|
lmtt_setup_dir_ptr(lmtt);
|
|
}
|
|
|
|
static int lmtt_invalidate_hw(struct xe_lmtt *lmtt)
|
|
{
|
|
struct xe_tlb_inval_fence fences[XE_MAX_GT_PER_TILE];
|
|
struct xe_tlb_inval_fence *fence = fences;
|
|
struct xe_tile *tile = lmtt_to_tile(lmtt);
|
|
struct xe_gt *gt;
|
|
int result = 0;
|
|
int err;
|
|
u8 id;
|
|
|
|
for_each_gt_on_tile(gt, tile, id) {
|
|
xe_tlb_inval_fence_init(>->tlb_inval, fence, true);
|
|
err = xe_tlb_inval_all(>->tlb_inval, fence);
|
|
result = result ?: err;
|
|
fence++;
|
|
}
|
|
|
|
lmtt_debug(lmtt, "num_fences=%d err=%d\n", (int)(fence - fences), result);
|
|
|
|
/*
|
|
* It is fine to wait for all fences, even for those which covers the
|
|
* invalidation request that failed, as such fence should be already
|
|
* marked as signaled.
|
|
*/
|
|
fence = fences;
|
|
for_each_gt_on_tile(gt, tile, id)
|
|
xe_tlb_inval_fence_wait(fence++);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_invalidate_hw - Invalidate LMTT hardware.
|
|
* @lmtt: the &xe_lmtt to invalidate
|
|
*
|
|
* Send requests to all GuCs on this tile to invalidate all TLBs.
|
|
* If the platform has a standalone MERT, also invalidate MERT's TLB.
|
|
*
|
|
* This function should be called only when running as a PF driver.
|
|
*/
|
|
void xe_lmtt_invalidate_hw(struct xe_lmtt *lmtt)
|
|
{
|
|
struct xe_tile *tile = lmtt_to_tile(lmtt);
|
|
struct xe_device *xe = lmtt_to_xe(lmtt);
|
|
int err;
|
|
|
|
lmtt_assert(lmtt, IS_SRIOV_PF(xe));
|
|
|
|
err = lmtt_invalidate_hw(lmtt);
|
|
if (err)
|
|
xe_tile_sriov_err(tile, "LMTT invalidation failed (%pe)",
|
|
ERR_PTR(err));
|
|
|
|
if (xe_device_has_mert(xe) && xe_tile_is_root(tile)) {
|
|
err = xe_mert_invalidate_lmtt(xe);
|
|
if (err)
|
|
xe_tile_sriov_err(tile, "MERT LMTT invalidation failed (%pe)",
|
|
ERR_PTR(err));
|
|
}
|
|
}
|
|
|
|
static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt,
|
|
u64 pte, unsigned int idx)
|
|
{
|
|
unsigned int level = pt->level;
|
|
|
|
lmtt_assert(lmtt, idx <= lmtt->ops->lmtt_pte_num(level));
|
|
lmtt_debug(lmtt, "WRITE level=%u index=%u pte=%#llx\n", level, idx, pte);
|
|
|
|
switch (lmtt->ops->lmtt_pte_size(level)) {
|
|
case sizeof(u32):
|
|
lmtt_assert(lmtt, !overflows_type(pte, u32));
|
|
lmtt_assert(lmtt, !pte || !iosys_map_rd(&pt->bo->vmap, idx * sizeof(u32), u32));
|
|
|
|
xe_map_wr(lmtt_to_xe(lmtt), &pt->bo->vmap, idx * sizeof(u32), u32, pte);
|
|
break;
|
|
case sizeof(u64):
|
|
lmtt_assert(lmtt, !pte || !iosys_map_rd(&pt->bo->vmap, idx * sizeof(u64), u64));
|
|
|
|
xe_map_wr(lmtt_to_xe(lmtt), &pt->bo->vmap, idx * sizeof(u64), u64, pte);
|
|
break;
|
|
default:
|
|
lmtt_assert(lmtt, !!!"invalid pte size");
|
|
}
|
|
}
|
|
|
|
static void lmtt_destroy_pt(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pd)
|
|
{
|
|
unsigned int num_entries = pd->level ? lmtt->ops->lmtt_pte_num(pd->level) : 0;
|
|
struct xe_lmtt_pt *pt;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < num_entries; i++) {
|
|
pt = pd->entries[i];
|
|
pd->entries[i] = NULL;
|
|
if (!pt)
|
|
continue;
|
|
|
|
lmtt_destroy_pt(lmtt, pt);
|
|
}
|
|
|
|
lmtt_pt_free(pd);
|
|
}
|
|
|
|
static void lmtt_drop_pages(struct xe_lmtt *lmtt, unsigned int vfid)
|
|
{
|
|
struct xe_lmtt_pt *pd = lmtt->pd;
|
|
struct xe_lmtt_pt *pt;
|
|
|
|
pt = pd->entries[vfid];
|
|
pd->entries[vfid] = NULL;
|
|
if (!pt)
|
|
return;
|
|
|
|
lmtt_write_pte(lmtt, pd, LMTT_PTE_INVALID, vfid);
|
|
lmtt_invalidate_hw(lmtt);
|
|
|
|
lmtt_assert(lmtt, pd->level > 0);
|
|
lmtt_assert(lmtt, pt->level == pd->level - 1);
|
|
lmtt_destroy_pt(lmtt, pt);
|
|
}
|
|
|
|
static int __lmtt_alloc_range(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pd,
|
|
u64 start, u64 end)
|
|
{
|
|
u64 pte_addr_shift = BIT_ULL(lmtt->ops->lmtt_pte_shift(pd->level));
|
|
u64 offset;
|
|
int err;
|
|
|
|
lmtt_assert(lmtt, pd->level > 0);
|
|
|
|
offset = start;
|
|
while (offset < end) {
|
|
struct xe_lmtt_pt *pt;
|
|
u64 next, pde, pt_addr;
|
|
unsigned int idx;
|
|
|
|
pt = lmtt_pt_alloc(lmtt, pd->level - 1);
|
|
if (IS_ERR(pt))
|
|
return PTR_ERR(pt);
|
|
|
|
pt_addr = xe_bo_main_addr(pt->bo, XE_PAGE_SIZE);
|
|
|
|
idx = lmtt->ops->lmtt_pte_index(offset, pd->level);
|
|
pde = lmtt->ops->lmtt_pte_encode(pt_addr, pd->level);
|
|
|
|
lmtt_write_pte(lmtt, pd, pde, idx);
|
|
|
|
pd->entries[idx] = pt;
|
|
|
|
next = min(end, round_up(offset + 1, pte_addr_shift));
|
|
|
|
if (pt->level != 0) {
|
|
err = __lmtt_alloc_range(lmtt, pt, offset, next);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
offset = next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lmtt_alloc_range(struct xe_lmtt *lmtt, unsigned int vfid, u64 start, u64 end)
|
|
{
|
|
struct xe_lmtt_pt *pd = lmtt->pd;
|
|
struct xe_lmtt_pt *pt;
|
|
u64 pt_addr;
|
|
u64 pde;
|
|
int err;
|
|
|
|
lmtt_assert(lmtt, pd->level > 0);
|
|
lmtt_assert(lmtt, vfid <= lmtt->ops->lmtt_pte_num(pd->level));
|
|
lmtt_assert(lmtt, IS_ALIGNED(start, lmtt_page_size(lmtt)));
|
|
lmtt_assert(lmtt, IS_ALIGNED(end, lmtt_page_size(lmtt)));
|
|
|
|
if (pd->entries[vfid])
|
|
return -ENOTEMPTY;
|
|
|
|
pt = lmtt_pt_alloc(lmtt, pd->level - 1);
|
|
if (IS_ERR(pt))
|
|
return PTR_ERR(pt);
|
|
|
|
pt_addr = xe_bo_main_addr(pt->bo, XE_PAGE_SIZE);
|
|
|
|
pde = lmtt->ops->lmtt_pte_encode(pt_addr, pd->level);
|
|
|
|
lmtt_write_pte(lmtt, pd, pde, vfid);
|
|
|
|
pd->entries[vfid] = pt;
|
|
|
|
if (pt->level != 0) {
|
|
err = __lmtt_alloc_range(lmtt, pt, start, end);
|
|
if (err)
|
|
goto out_free_pt;
|
|
}
|
|
|
|
return 0;
|
|
|
|
out_free_pt:
|
|
lmtt_pt_free(pt);
|
|
return err;
|
|
}
|
|
|
|
static struct xe_lmtt_pt *lmtt_leaf_pt(struct xe_lmtt *lmtt, unsigned int vfid, u64 addr)
|
|
{
|
|
struct xe_lmtt_pt *pd = lmtt->pd;
|
|
struct xe_lmtt_pt *pt;
|
|
|
|
lmtt_assert(lmtt, vfid <= lmtt->ops->lmtt_pte_num(pd->level));
|
|
pt = pd->entries[vfid];
|
|
|
|
while (pt->level) {
|
|
lmtt_assert(lmtt, lmtt->ops->lmtt_pte_index(addr, pt->level) <=
|
|
lmtt->ops->lmtt_pte_num(pt->level));
|
|
|
|
pt = pt->entries[lmtt->ops->lmtt_pte_index(addr, pt->level)];
|
|
|
|
addr >>= lmtt->ops->lmtt_pte_shift(pt->level);
|
|
}
|
|
|
|
lmtt_assert(lmtt, lmtt->ops->lmtt_pte_index(addr, pt->level) <=
|
|
lmtt->ops->lmtt_pte_num(pt->level));
|
|
lmtt_assert(lmtt, pt->level != pd->level);
|
|
lmtt_assert(lmtt, pt->level == 0);
|
|
return pt;
|
|
}
|
|
|
|
static void lmtt_insert_bo(struct xe_lmtt *lmtt, unsigned int vfid, struct xe_bo *bo, u64 start)
|
|
{
|
|
u64 page_size = lmtt_page_size(lmtt);
|
|
struct xe_res_cursor cur;
|
|
struct xe_lmtt_pt *pt;
|
|
u64 addr, vram_offset;
|
|
|
|
lmtt_assert(lmtt, IS_ALIGNED(start, page_size));
|
|
lmtt_assert(lmtt, IS_ALIGNED(xe_bo_size(bo), page_size));
|
|
lmtt_assert(lmtt, xe_bo_is_vram(bo));
|
|
|
|
vram_offset = vram_region_gpu_offset(bo->ttm.resource);
|
|
xe_res_first(bo->ttm.resource, 0, xe_bo_size(bo), &cur);
|
|
while (cur.remaining) {
|
|
addr = xe_res_dma(&cur);
|
|
addr += vram_offset; /* XXX */
|
|
|
|
pt = lmtt_leaf_pt(lmtt, vfid, start);
|
|
|
|
lmtt_write_pte(lmtt, pt, lmtt->ops->lmtt_pte_encode(addr, 0),
|
|
lmtt->ops->lmtt_pte_index(start, 0));
|
|
|
|
xe_res_next(&cur, page_size);
|
|
start += page_size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_prepare_pages - Create VF's LMTT Page Tables.
|
|
* @lmtt: the &xe_lmtt to update
|
|
* @vfid: the VF identifier (1-based)
|
|
* @range: top range of LMEM offset to be supported
|
|
*
|
|
* This function creates empty LMTT page tables for given VF to support
|
|
* up to maximum #range LMEM offset. The LMTT page tables created by this
|
|
* function must be released using xe_lmtt_drop_pages() function.
|
|
*
|
|
* Notes:
|
|
* This function shall be called only after successful LMTT initialization.
|
|
* See xe_lmtt_init().
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int xe_lmtt_prepare_pages(struct xe_lmtt *lmtt, unsigned int vfid, u64 range)
|
|
{
|
|
lmtt_assert(lmtt, lmtt->pd);
|
|
lmtt_assert(lmtt, vfid);
|
|
|
|
return lmtt_alloc_range(lmtt, vfid, 0, range);
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_populate_pages - Update VF's LMTT Page Table Entries.
|
|
* @lmtt: the &xe_lmtt to update
|
|
* @vfid: the VF identifier (1-based)
|
|
* @bo: the buffer object with LMEM allocation to be mapped
|
|
* @offset: the offset at which #bo should be mapped
|
|
*
|
|
* This function updates VF's LMTT entries to use given buffer object as a backstore.
|
|
*
|
|
* Notes:
|
|
* This function shall be called only after successful preparation of the
|
|
* VF's LMTT Page Tables. See xe_lmtt_prepare().
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int xe_lmtt_populate_pages(struct xe_lmtt *lmtt, unsigned int vfid, struct xe_bo *bo, u64 offset)
|
|
{
|
|
lmtt_assert(lmtt, lmtt->pd);
|
|
lmtt_assert(lmtt, vfid);
|
|
|
|
lmtt_insert_bo(lmtt, vfid, bo, offset);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_drop_pages - Remove VF's LMTT Pages.
|
|
* @lmtt: the &xe_lmtt to update
|
|
* @vfid: the VF identifier (1-based)
|
|
*
|
|
* This function removes all LMTT Page Tables prepared by xe_lmtt_prepare_pages().
|
|
*
|
|
* This function shall be called only after successful LMTT initialization.
|
|
* See xe_lmtt_init().
|
|
*/
|
|
void xe_lmtt_drop_pages(struct xe_lmtt *lmtt, unsigned int vfid)
|
|
{
|
|
lmtt_assert(lmtt, lmtt->pd);
|
|
lmtt_assert(lmtt, vfid);
|
|
|
|
lmtt_drop_pages(lmtt, vfid);
|
|
}
|
|
|
|
/**
|
|
* xe_lmtt_estimate_pt_size - Estimate size of LMTT PT allocations.
|
|
* @lmtt: the &xe_lmtt
|
|
* @size: the size of the LMEM to be mapped over LMTT (including any offset)
|
|
*
|
|
* This function shall be called only by PF.
|
|
*
|
|
* Return: size of the PT allocation(s) needed to support given LMEM size.
|
|
*/
|
|
u64 xe_lmtt_estimate_pt_size(struct xe_lmtt *lmtt, u64 size)
|
|
{
|
|
unsigned int level = 0;
|
|
u64 pt_size;
|
|
|
|
lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt)));
|
|
lmtt_assert(lmtt, xe_device_has_lmtt(lmtt_to_xe(lmtt)));
|
|
lmtt_assert(lmtt, lmtt->ops);
|
|
|
|
pt_size = PAGE_ALIGN(lmtt->ops->lmtt_pte_size(level) *
|
|
lmtt->ops->lmtt_pte_num(level));
|
|
|
|
while (++level < lmtt->ops->lmtt_root_pd_level()) {
|
|
pt_size *= lmtt->ops->lmtt_pte_index(size, level) + 1;
|
|
pt_size += PAGE_ALIGN(lmtt->ops->lmtt_pte_size(level) *
|
|
lmtt->ops->lmtt_pte_num(level));
|
|
}
|
|
|
|
return pt_size;
|
|
}
|
|
|
|
#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
|
|
#include "tests/xe_lmtt_test.c"
|
|
#endif
|