UAPI Changes:

- Make OA buffer size configurable (Sai)
 
 Display Changes (including i915):
  - Fix ttm_bo_access() usage (Auld)
  - Power request asserting/deasserting for Xe3lpd (Mika)
  - One Type-C conversion towards struct intel_display (Mika)
 
 Driver Changes:
  - GuC capture related fixes (Everest, Zhanjun)
  - Move old workaround to OOB infra (Lucas)
  - Compute mode change refactoring (Bala)
  - Add ufence and g2h flushes for LNL Hybrid timeouts (Nirmoy)
  - Avoid unnecessary OOM kills (Thomas)
  - Restore system memory GGTT mappings (Brost)
  - Fix build error for XE_IOCTL_DBG macro (Gyeyoung)
  - Documentation updates and fixes (Lucas, Randy)
  - A few exec IOCTL fixes (Brost)
  - Fix potential GGTT allocation leak (Michal)
  - Fix races on fdinfo (Lucas)
  - SRIOV VF: Post-migration recovery worker basis (Tomasz)
  - GuC Communication fixes and improvements (Michal, John, Tomasz, Auld, Jonathan)
  - SRIOV PF: Add support for VF scheduling priority
  - Trace improvements (Lucas, Auld, Oak)
  - Hibernation on igpu fixes and improvements (Auld)
  - GT oriented logs/asserts improvements (Michal)
  - Take job list lock in xe_sched_first_pending_job (Nirmoy)
  - GSC: Improve SW proxy error checking and logging (Daniele)
  - GuC crash notifications & drop default log verbosity (John)
  - Fix races on fdinfo (Lucas)
  - Fix runtime_pm handling in OA (Ashutosh)
  - Allow fault injection in vm create and vm bind IOCTLs (Francois)
  - TLB invalidation fixes (Nirmoy, Daniele)
  - Devcoredump Improvements, doc and fixes (Brost, Lucas, Zhanjun, John)
  - Wake up waiters after setting ufence->signalled (Nirmoy)
  - Mark preempt fence workqueue as reclaim (Brost)
  - Trivial header/flags cleanups (Lucas)
  - VRAM drop 2G block restriction (Auld)
  - Drop useless d3cold allowed message (Brost)
  - SRIOV PF: Drop 2GiB limit of fair LMEM allocation (Michal)
  - Add another PTL PCI ID (Atwood)
  - Allow bo mapping on multiple ggtts (Niranjana)
  - Add support for GuC-to-GuC communication (John)
  - Update xe2_graphics name string (Roper)
  - VRAM: fix lpfn check (Auld)
  - Ad Xe3 workaround (Apoorva)
  - Migrate fixes (Auld)
  - Fix non-contiguous VRAM BO access (Brost)
  - Log throttle reasons (Raag)
  - Enable PMT support for BMG (Michael)
  - IRQ related fixes and improvements (Ilia)
  - Avoid evicting object of the same vm in none fault mode (Oak)
  - Fix in tests (Nirmoy)
  - Fix ERR_PTR handling (Mirsad)
  - Some reg_sr/whitelist fixes and refactors (Lucas)
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEbSBwaO7dZQkcLOKj+mJfZA7rE8oFAmdaHkMACgkQ+mJfZA7r
 E8o+twf/XYZTk4O3qQ+yNL3PDQT0NIKjH8mEnmu4udyIw/sYhQe6ji+uh1YutK8Y
 41IQc06qQogTj36bqSwbjThw5asMfRh2sNR/p1uOy7RGUnN25FuYSXEgOeDWi/Ec
 xrZE1TKPotFGeGI09KJmzjzMq94cgv97Pxma+5m8BjVsvzXQSzEJ2r9cC6ruSfNT
 O5Jq5nqxHSkWUbKCxPnixSlGnH4jbsuiqS1E1pnH+u6ijxsfhOJj686wLn2FRkiw
 6FhXmJBrd8AZ0Q2E7h3UswE5O88I0ALDc58OINAzD1GMyzvZj2vB1pXgj5uNr0/x
 Ku4cxu1jprsi+FLUdKAdYpxRBRanow==
 =3Ou7
 -----END PGP SIGNATURE-----

Merge tag 'drm-xe-next-2024-12-11' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next

UAPI Changes:
 - Make OA buffer size configurable (Sai)

Display Changes (including i915):
 - Fix ttm_bo_access() usage (Auld)
 - Power request asserting/deasserting for Xe3lpd (Mika)
 - One Type-C conversion towards struct intel_display (Mika)

Driver Changes:
 - GuC capture related fixes (Everest, Zhanjun)
 - Move old workaround to OOB infra (Lucas)
 - Compute mode change refactoring (Bala)
 - Add ufence and g2h flushes for LNL Hybrid timeouts (Nirmoy)
 - Avoid unnecessary OOM kills (Thomas)
 - Restore system memory GGTT mappings (Brost)
 - Fix build error for XE_IOCTL_DBG macro (Gyeyoung)
 - Documentation updates and fixes (Lucas, Randy)
 - A few exec IOCTL fixes (Brost)
 - Fix potential GGTT allocation leak (Michal)
 - Fix races on fdinfo (Lucas)
 - SRIOV VF: Post-migration recovery worker basis (Tomasz)
 - GuC Communication fixes and improvements (Michal, John, Tomasz, Auld, Jonathan)
 - SRIOV PF: Add support for VF scheduling priority
 - Trace improvements (Lucas, Auld, Oak)
 - Hibernation on igpu fixes and improvements (Auld)
 - GT oriented logs/asserts improvements (Michal)
 - Take job list lock in xe_sched_first_pending_job (Nirmoy)
 - GSC: Improve SW proxy error checking and logging (Daniele)
 - GuC crash notifications & drop default log verbosity (John)
 - Fix races on fdinfo (Lucas)
 - Fix runtime_pm handling in OA (Ashutosh)
 - Allow fault injection in vm create and vm bind IOCTLs (Francois)
 - TLB invalidation fixes (Nirmoy, Daniele)
 - Devcoredump Improvements, doc and fixes (Brost, Lucas, Zhanjun, John)
 - Wake up waiters after setting ufence->signalled (Nirmoy)
 - Mark preempt fence workqueue as reclaim (Brost)
 - Trivial header/flags cleanups (Lucas)
 - VRAM drop 2G block restriction (Auld)
 - Drop useless d3cold allowed message (Brost)
 - SRIOV PF: Drop 2GiB limit of fair LMEM allocation (Michal)
 - Add another PTL PCI ID (Atwood)
 - Allow bo mapping on multiple ggtts (Niranjana)
 - Add support for GuC-to-GuC communication (John)
 - Update xe2_graphics name string (Roper)
 - VRAM: fix lpfn check (Auld)
 - Ad Xe3 workaround (Apoorva)
 - Migrate fixes (Auld)
 - Fix non-contiguous VRAM BO access (Brost)
 - Log throttle reasons (Raag)
 - Enable PMT support for BMG (Michael)
 - IRQ related fixes and improvements (Ilia)
 - Avoid evicting object of the same vm in none fault mode (Oak)
 - Fix in tests (Nirmoy)
 - Fix ERR_PTR handling (Mirsad)
 - Some reg_sr/whitelist fixes and refactors (Lucas)

Signed-off-by: Dave Airlie <airlied@redhat.com>

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEbSBwaO7dZQkcLOKj+mJfZA7rE8oFAmdaHkMACgkQ+mJfZA7r
# E8o+twf/XYZTk4O3qQ+yNL3PDQT0NIKjH8mEnmu4udyIw/sYhQe6ji+uh1YutK8Y
# 41IQc06qQogTj36bqSwbjThw5asMfRh2sNR/p1uOy7RGUnN25FuYSXEgOeDWi/Ec
# xrZE1TKPotFGeGI09KJmzjzMq94cgv97Pxma+5m8BjVsvzXQSzEJ2r9cC6ruSfNT
# O5Jq5nqxHSkWUbKCxPnixSlGnH4jbsuiqS1E1pnH+u6ijxsfhOJj686wLn2FRkiw
# 6FhXmJBrd8AZ0Q2E7h3UswE5O88I0ALDc58OINAzD1GMyzvZj2vB1pXgj5uNr0/x
# Ku4cxu1jprsi+FLUdKAdYpxRBRanow==
# =3Ou7
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 12 Dec 2024 09:20:35 AEST
# gpg:                using RSA key 6D207068EEDD65091C2CE2A3FA625F640EEB13CA
# gpg: Good signature from "Rodrigo Vivi <rodrigo.vivi@intel.com>" [unknown]
# gpg:                 aka "Rodrigo Vivi <rodrigo.vivi@gmail.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 6D20 7068 EEDD 6509 1C2C  E2A3 FA62 5F64 0EEB 13CA
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/Z1ofx-fExLQKV_e4@intel.com
This commit is contained in:
Dave Airlie 2024-12-13 10:19:44 +10:00
commit bdecb30d57
94 changed files with 2463 additions and 590 deletions

View File

@ -23,4 +23,5 @@ DG2, etc is provided to prototype the driver.
xe_firmware
xe_tile
xe_debugging
xe_devcoredump
xe-drm-usage-stats.rst

View File

@ -0,0 +1,14 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
==================
Xe Device Coredump
==================
.. kernel-doc:: drivers/gpu/drm/xe/xe_devcoredump.c
:doc: Xe device coredump
Internal API
============
.. kernel-doc:: drivers/gpu/drm/xe/xe_devcoredump.c
:internal:

View File

@ -200,6 +200,13 @@
#define XELPDP_SSC_ENABLE_PLLA REG_BIT(1)
#define XELPDP_SSC_ENABLE_PLLB REG_BIT(0)
#define TCSS_DISP_MAILBOX_IN_CMD _MMIO(0x161300)
#define TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY REG_BIT(31)
#define TCSS_DISP_MAILBOX_IN_CMD_CMD_MASK REG_GENMASK(7, 0)
#define TCSS_DISP_MAILBOX_IN_CMD_DATA(val) REG_FIELD_PREP(TCSS_DISP_MAILBOX_IN_CMD_CMD_MASK, val)
#define TCSS_DISP_MAILBOX_IN_DATA _MMIO(0x161304)
/* C10 Vendor Registers */
#define PHY_C10_VDR_PLL(idx) (0xC00 + (idx))
#define C10_PLL0_FRACEN REG_BIT8(4)

View File

@ -1013,21 +1013,52 @@ xelpdp_tc_phy_wait_for_tcss_power(struct intel_tc_port *tc, bool enabled)
return true;
}
/*
* Gfx driver WA 14020908590 for PTL tcss_rxdetect_clkswb_req/ack
* handshake violation when pwwreq= 0->1 during TC7/10 entry
*/
static void xelpdp_tc_power_request_wa(struct intel_display *display, bool enable)
{
/* check if mailbox is running busy */
if (intel_de_wait_for_clear(display, TCSS_DISP_MAILBOX_IN_CMD,
TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY, 10)) {
drm_dbg_kms(display->drm,
"Timeout waiting for TCSS mailbox run/busy bit to clear\n");
return;
}
intel_de_write(display, TCSS_DISP_MAILBOX_IN_DATA, enable ? 1 : 0);
intel_de_write(display, TCSS_DISP_MAILBOX_IN_CMD,
TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY |
TCSS_DISP_MAILBOX_IN_CMD_DATA(0x1));
/* wait to clear mailbox running busy bit before continuing */
if (intel_de_wait_for_clear(display, TCSS_DISP_MAILBOX_IN_CMD,
TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY, 10)) {
drm_dbg_kms(display->drm,
"Timeout after writing data to mailbox. Mailbox run/busy bit did not clear\n");
return;
}
}
static void __xelpdp_tc_phy_enable_tcss_power(struct intel_tc_port *tc, bool enable)
{
struct drm_i915_private *i915 = tc_to_i915(tc);
struct intel_display *display = to_intel_display(tc->dig_port);
enum port port = tc->dig_port->base.port;
i915_reg_t reg = XELPDP_PORT_BUF_CTL1(i915, port);
i915_reg_t reg = XELPDP_PORT_BUF_CTL1(display, port);
u32 val;
assert_tc_cold_blocked(tc);
val = intel_de_read(i915, reg);
if (DISPLAY_VER(display) == 30)
xelpdp_tc_power_request_wa(display, enable);
val = intel_de_read(display, reg);
if (enable)
val |= XELPDP_TCSS_POWER_REQUEST;
else
val &= ~XELPDP_TCSS_POWER_REQUEST;
intel_de_write(i915, reg, val);
intel_de_write(display, reg, val);
}
static bool xelpdp_tc_phy_enable_tcss_power(struct intel_tc_port *tc, bool enable)

View File

@ -405,13 +405,25 @@ static int ttm_bo_vm_access_kmap(struct ttm_buffer_object *bo,
return len;
}
int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
void *buf, int len, int write)
/**
* ttm_bo_access - Helper to access a buffer object
*
* @bo: ttm buffer object
* @offset: access offset into buffer object
* @buf: pointer to caller memory to read into or write from
* @len: length of access
* @write: write access
*
* Utility function to access a buffer object. Useful when buffer object cannot
* be easily mapped (non-contiguous, non-visible, etc...). Should not directly
* be exported to user space via a peak / poke interface.
*
* Returns:
* @len if successful, negative error code on failure.
*/
int ttm_bo_access(struct ttm_buffer_object *bo, unsigned long offset,
void *buf, int len, int write)
{
struct ttm_buffer_object *bo = vma->vm_private_data;
unsigned long offset = (addr) - vma->vm_start +
((vma->vm_pgoff - drm_vma_node_start(&bo->base.vma_node))
<< PAGE_SHIFT);
int ret;
if (len < 1 || (offset + len) > bo->base.size)
@ -429,8 +441,8 @@ int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
break;
default:
if (bo->bdev->funcs->access_memory)
ret = bo->bdev->funcs->access_memory(
bo, offset, buf, len, write);
ret = bo->bdev->funcs->access_memory
(bo, offset, buf, len, write);
else
ret = -EIO;
}
@ -439,6 +451,18 @@ int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
return ret;
}
EXPORT_SYMBOL(ttm_bo_access);
int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
void *buf, int len, int write)
{
struct ttm_buffer_object *bo = vma->vm_private_data;
unsigned long offset = (addr) - vma->vm_start +
((vma->vm_pgoff - drm_vma_node_start(&bo->base.vma_node))
<< PAGE_SHIFT);
return ttm_bo_access(bo, offset, buf, len, write);
}
EXPORT_SYMBOL(ttm_bo_vm_access);
static const struct vm_operations_struct ttm_bo_vm_ops = {

View File

@ -101,6 +101,7 @@ xe-y += xe_bb.o \
xe_trace.o \
xe_trace_bo.o \
xe_trace_guc.o \
xe_trace_lrc.o \
xe_ttm_sys_mgr.o \
xe_ttm_stolen_mgr.o \
xe_ttm_vram_mgr.o \
@ -110,6 +111,7 @@ xe-y += xe_bb.o \
xe_vm.o \
xe_vram.o \
xe_vram_freq.o \
xe_vsec.o \
xe_wait_user_fence.o \
xe_wa.o \
xe_wopcm.o
@ -124,7 +126,8 @@ xe-y += \
xe_gt_sriov_vf.o \
xe_guc_relay.o \
xe_memirq.o \
xe_sriov.o
xe_sriov.o \
xe_sriov_vf.o
xe-$(CONFIG_PCI_IOV) += \
xe_gt_sriov_pf.o \

View File

@ -134,6 +134,8 @@ enum xe_guc_action {
XE_GUC_ACTION_DEREGISTER_CONTEXT = 0x4503,
XE_GUC_ACTION_REGISTER_COMMAND_TRANSPORT_BUFFER = 0x4505,
XE_GUC_ACTION_DEREGISTER_COMMAND_TRANSPORT_BUFFER = 0x4506,
XE_GUC_ACTION_REGISTER_G2G = 0x4507,
XE_GUC_ACTION_DEREGISTER_G2G = 0x4508,
XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE = 0x4600,
XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC = 0x4601,
XE_GUC_ACTION_CLIENT_SOFT_RESET = 0x5507,
@ -218,4 +220,22 @@ enum xe_guc_tlb_inval_mode {
XE_GUC_TLB_INVAL_MODE_LITE = 0x1,
};
/*
* GuC to GuC communication (de-)registration fields:
*/
enum xe_guc_g2g_type {
XE_G2G_TYPE_IN = 0x0,
XE_G2G_TYPE_OUT,
XE_G2G_TYPE_LIMIT,
};
#define XE_G2G_REGISTER_DEVICE REG_GENMASK(16, 16)
#define XE_G2G_REGISTER_TILE REG_GENMASK(15, 12)
#define XE_G2G_REGISTER_TYPE REG_GENMASK(11, 8)
#define XE_G2G_REGISTER_SIZE REG_GENMASK(7, 0)
#define XE_G2G_DEREGISTER_DEVICE REG_GENMASK(16, 16)
#define XE_G2G_DEREGISTER_TILE REG_GENMASK(15, 12)
#define XE_G2G_DEREGISTER_TYPE REG_GENMASK(11, 8)
#endif

View File

@ -501,6 +501,44 @@
#define VF2GUC_VF_RESET_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
#define VF2GUC_VF_RESET_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
/**
* DOC: VF2GUC_NOTIFY_RESFIX_DONE
*
* This action is used by VF to notify the GuC that the VF KMD has completed
* post-migration recovery steps.
*
* This message must be sent as `MMIO HXG Message`_.
*
* +---+-------+--------------------------------------------------------------+
* | | Bits | Description |
* +===+=======+==============================================================+
* | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
* | +-------+--------------------------------------------------------------+
* | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
* | +-------+--------------------------------------------------------------+
* | | 27:16 | DATA0 = MBZ |
* | +-------+--------------------------------------------------------------+
* | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE` = 0x5508 |
* +---+-------+--------------------------------------------------------------+
*
* +---+-------+--------------------------------------------------------------+
* | | Bits | Description |
* +===+=======+==============================================================+
* | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_GUC_ |
* | +-------+--------------------------------------------------------------+
* | | 30:28 | TYPE = GUC_HXG_TYPE_RESPONSE_SUCCESS_ |
* | +-------+--------------------------------------------------------------+
* | | 27:0 | DATA0 = MBZ |
* +---+-------+--------------------------------------------------------------+
*/
#define GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE 0x5508u
#define VF2GUC_NOTIFY_RESFIX_DONE_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
#define VF2GUC_NOTIFY_RESFIX_DONE_REQUEST_MSG_0_MBZ GUC_HXG_REQUEST_MSG_0_DATA0
#define VF2GUC_NOTIFY_RESFIX_DONE_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
#define VF2GUC_NOTIFY_RESFIX_DONE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
/**
* DOC: VF2GUC_QUERY_SINGLE_KLV
*

View File

@ -291,6 +291,14 @@ enum {
*
* :0: (default)
* :1-65535: number of contexts (Gen12)
*
* _`GUC_KLV_VF_CFG_SCHED_PRIORITY` : 0x8A0C
* This config controls VFs scheduling priority.
*
* :0: LOW = schedule VF only if it has active work (default)
* :1: NORMAL = schedule VF always, irrespective of whether it has work or not
* :2: HIGH = schedule VF in the next time-slice after current active
* time-slice completes if it has active work
*/
#define GUC_KLV_VF_CFG_GGTT_START_KEY 0x0001
@ -343,6 +351,12 @@ enum {
#define GUC_KLV_VF_CFG_BEGIN_CONTEXT_ID_KEY 0x8a0b
#define GUC_KLV_VF_CFG_BEGIN_CONTEXT_ID_LEN 1u
#define GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY 0x8a0c
#define GUC_KLV_VF_CFG_SCHED_PRIORITY_LEN 1u
#define GUC_SCHED_PRIORITY_LOW 0u
#define GUC_SCHED_PRIORITY_NORMAL 1u
#define GUC_SCHED_PRIORITY_HIGH 2u
/*
* Workaround keys:
*/

View File

@ -53,18 +53,7 @@ void gen2_irq_init(struct intel_uncore *uncore, struct i915_irq_regs regs,
bool intel_irqs_enabled(struct xe_device *xe)
{
/*
* XXX: i915 has a racy handling of the irq.enabled, since it doesn't
* lock its transitions. Because of that, the irq.enabled sometimes
* is not read with the irq.lock in place.
* However, the most critical cases like vblank and page flips are
* properly using the locks.
* We cannot take the lock in here or run any kind of assert because
* of i915 inconsistency.
* But at this point the xe irq is better protected against races,
* although the full solution would be protecting the i915 side.
*/
return xe->irq.enabled;
return atomic_read(&xe->irq.enabled);
}
void intel_synchronize_irq(struct xe_device *xe)

View File

@ -40,31 +40,8 @@ int intel_bo_fb_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
int intel_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, int size)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
struct ttm_bo_kmap_obj map;
void *src;
bool is_iomem;
int ret;
ret = xe_bo_lock(bo, true);
if (ret)
return ret;
ret = ttm_bo_kmap(&bo->ttm, offset >> PAGE_SHIFT, 1, &map);
if (ret)
goto out_unlock;
offset &= ~PAGE_MASK;
src = ttm_kmap_obj_virtual(&map, &is_iomem);
src += offset;
if (is_iomem)
memcpy_fromio(dst, (void __iomem *)src, size);
else
memcpy(dst, src, size);
ttm_bo_kunmap(&map);
out_unlock:
xe_bo_unlock(bo);
return ret;
return xe_bo_read(bo, offset, dst, size);
}
struct intel_frontbuffer *intel_bo_get_frontbuffer(struct drm_gem_object *obj)

View File

@ -161,7 +161,7 @@ static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb,
}
vma->dpt = dpt;
vma->node = dpt->ggtt_node;
vma->node = dpt->ggtt_node[tile0->id];
return 0;
}
@ -213,8 +213,8 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K)
align = max_t(u32, align, SZ_64K);
if (bo->ggtt_node && view->type == I915_GTT_VIEW_NORMAL) {
vma->node = bo->ggtt_node;
if (bo->ggtt_node[ggtt->tile->id] && view->type == I915_GTT_VIEW_NORMAL) {
vma->node = bo->ggtt_node[ggtt->tile->id];
} else if (view->type == I915_GTT_VIEW_NORMAL) {
u32 x, size = bo->ttm.base.size;
@ -345,10 +345,12 @@ static struct i915_vma *__xe_pin_fb_vma(const struct intel_framebuffer *fb,
static void __xe_unpin_fb_vma(struct i915_vma *vma)
{
u8 tile_id = vma->node->ggtt->tile->id;
if (vma->dpt)
xe_bo_unpin_map_no_vm(vma->dpt);
else if (!xe_ggtt_node_allocated(vma->bo->ggtt_node) ||
vma->bo->ggtt_node->base.start != vma->node->base.start)
else if (!xe_ggtt_node_allocated(vma->bo->ggtt_node[tile_id]) ||
vma->bo->ggtt_node[tile_id]->base.start != vma->node->base.start)
xe_ggtt_node_remove(vma->node, false);
ttm_bo_reserve(&vma->bo->ttm, false, false, NULL);

View File

@ -445,6 +445,8 @@
#define SAMPLER_MODE XE_REG_MCR(0xe18c, XE_REG_OPTION_MASKED)
#define ENABLE_SMALLPL REG_BIT(15)
#define SMP_WAIT_FETCH_MERGING_COUNTER REG_GENMASK(11, 10)
#define SMP_FORCE_128B_OVERFETCH REG_FIELD_PREP(SMP_WAIT_FETCH_MERGING_COUNTER, 1)
#define SC_DISABLE_POWER_OPTIMIZATION_EBB REG_BIT(9)
#define SAMPLER_ENABLE_HEADLESS_MSG REG_BIT(5)
#define INDIRECT_STATE_BASE_ADDR_OVERRIDE REG_BIT(0)

View File

@ -41,14 +41,6 @@
#define OAG_OABUFFER XE_REG(0xdb08)
#define OABUFFER_SIZE_MASK REG_GENMASK(5, 3)
#define OABUFFER_SIZE_128K REG_FIELD_PREP(OABUFFER_SIZE_MASK, 0)
#define OABUFFER_SIZE_256K REG_FIELD_PREP(OABUFFER_SIZE_MASK, 1)
#define OABUFFER_SIZE_512K REG_FIELD_PREP(OABUFFER_SIZE_MASK, 2)
#define OABUFFER_SIZE_1M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 3)
#define OABUFFER_SIZE_2M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 4)
#define OABUFFER_SIZE_4M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 5)
#define OABUFFER_SIZE_8M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 6)
#define OABUFFER_SIZE_16M REG_FIELD_PREP(OABUFFER_SIZE_MASK, 7)
#define OAG_OABUFFER_MEMORY_SELECT REG_BIT(0) /* 0: PPGTT, 1: GGTT */
#define OAG_OACONTROL XE_REG(0xdaf4)
@ -63,6 +55,7 @@
#define OAG_OA_DEBUG XE_REG(0xdaf8, XE_REG_OPTION_MASKED)
#define OAG_OA_DEBUG_DISABLE_MMIO_TRG REG_BIT(14)
#define OAG_OA_DEBUG_START_TRIGGER_SCOPE_CONTROL REG_BIT(13)
#define OAG_OA_DEBUG_BUF_SIZE_SELECT REG_BIT(12)
#define OAG_OA_DEBUG_DISABLE_START_TRG_2_COUNT_QUAL REG_BIT(8)
#define OAG_OA_DEBUG_DISABLE_START_TRG_1_COUNT_QUAL REG_BIT(7)
#define OAG_OA_DEBUG_INCLUDE_CLK_RATIO REG_BIT(6)

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2024 Intel Corporation
*/
#ifndef _XE_PMT_H_
#define _XE_PMT_H_
#define SOC_BASE 0x280000
#define BMG_PMT_BASE_OFFSET 0xDB000
#define BMG_DISCOVERY_OFFSET (SOC_BASE + BMG_PMT_BASE_OFFSET)
#define BMG_TELEMETRY_BASE_OFFSET 0xE0000
#define BMG_TELEMETRY_OFFSET (SOC_BASE + BMG_TELEMETRY_BASE_OFFSET)
#define SG_REMAP_INDEX1 XE_REG(SOC_BASE + 0x08)
#define SG_REMAP_BITS REG_GENMASK(31, 24)
#endif

View File

@ -49,6 +49,13 @@ static int ccs_test_migrate(struct xe_tile *tile, struct xe_bo *bo,
KUNIT_FAIL(test, "Failed to submit bo clear.\n");
return PTR_ERR(fence);
}
if (dma_fence_wait_timeout(fence, false, 5 * HZ) <= 0) {
dma_fence_put(fence);
KUNIT_FAIL(test, "Timeout while clearing bo.\n");
return -ETIME;
}
dma_fence_put(fence);
}

View File

@ -83,7 +83,8 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
bo->size,
ttm_bo_type_kernel,
region |
XE_BO_FLAG_NEEDS_CPU_ACCESS);
XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED);
if (IS_ERR(remote)) {
KUNIT_FAIL(test, "Failed to allocate remote bo for %s: %pe\n",
str, remote);
@ -224,8 +225,8 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_PINNED);
if (IS_ERR(tiny)) {
KUNIT_FAIL(test, "Failed to allocate fake pt: %li\n",
PTR_ERR(pt));
KUNIT_FAIL(test, "Failed to allocate tiny fake pt: %li\n",
PTR_ERR(tiny));
goto free_pt;
}
@ -642,7 +643,9 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
sys_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M,
DRM_XE_GEM_CPU_CACHING_WC,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS);
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED);
if (IS_ERR(sys_bo)) {
KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
@ -666,7 +669,8 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
ccs_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M,
DRM_XE_GEM_CPU_CACHING_WC,
bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS);
bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED);
if (IS_ERR(ccs_bo)) {
KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
@ -690,7 +694,8 @@ static void validate_ccs_test_run_tile(struct xe_device *xe, struct xe_tile *til
vram_bo = xe_bo_create_user(xe, NULL, NULL, SZ_4M,
DRM_XE_GEM_CPU_CACHING_WC,
bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS);
bo_flags | XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED);
if (IS_ERR(vram_bo)) {
KUNIT_FAIL(test, "xe_bo_create() failed with err=%ld\n",
PTR_ERR(vram_bo));

View File

@ -14,7 +14,7 @@
#include "xe_step.h"
/**
* DOC: Xe ASSERTs
* DOC: Xe Asserts
*
* While Xe driver aims to be simpler than legacy i915 driver it is still
* complex enough that some changes introduced while adding new functionality
@ -103,7 +103,7 @@
* (&CONFIG_DRM_XE_DEBUG must be enabled) and cannot be used in expressions
* or as a condition.
*
* See `Xe ASSERTs`_ for general usage guidelines.
* See `Xe Asserts`_ for general usage guidelines.
*/
#define xe_assert(xe, condition) xe_assert_msg((xe), condition, "")
#define xe_assert_msg(xe, condition, msg, arg...) ({ \
@ -138,7 +138,7 @@
* (&CONFIG_DRM_XE_DEBUG must be enabled) and cannot be used in expressions
* or as a condition.
*
* See `Xe ASSERTs`_ for general usage guidelines.
* See `Xe Asserts`_ for general usage guidelines.
*/
#define xe_tile_assert(tile, condition) xe_tile_assert_msg((tile), condition, "")
#define xe_tile_assert_msg(tile, condition, msg, arg...) ({ \
@ -162,7 +162,7 @@
* (&CONFIG_DRM_XE_DEBUG must be enabled) and cannot be used in expressions
* or as a condition.
*
* See `Xe ASSERTs`_ for general usage guidelines.
* See `Xe Asserts`_ for general usage guidelines.
*/
#define xe_gt_assert(gt, condition) xe_gt_assert_msg((gt), condition, "")
#define xe_gt_assert_msg(gt, condition, msg, arg...) ({ \

View File

@ -162,6 +162,15 @@ static void try_add_system(struct xe_device *xe, struct xe_bo *bo,
}
}
static bool force_contiguous(u32 bo_flags)
{
/*
* For eviction / restore on suspend / resume objects pinned in VRAM
* must be contiguous, also only contiguous BOs support xe_bo_vmap.
*/
return bo_flags & (XE_BO_FLAG_PINNED | XE_BO_FLAG_GGTT);
}
static void add_vram(struct xe_device *xe, struct xe_bo *bo,
struct ttm_place *places, u32 bo_flags, u32 mem_type, u32 *c)
{
@ -175,12 +184,7 @@ static void add_vram(struct xe_device *xe, struct xe_bo *bo,
xe_assert(xe, vram && vram->usable_size);
io_size = vram->io_size;
/*
* For eviction / restore on suspend / resume objects
* pinned in VRAM must be contiguous
*/
if (bo_flags & (XE_BO_FLAG_PINNED |
XE_BO_FLAG_GGTT))
if (force_contiguous(bo_flags))
place.flags |= TTM_PL_FLAG_CONTIGUOUS;
if (io_size < vram->usable_size) {
@ -212,8 +216,7 @@ static void try_add_stolen(struct xe_device *xe, struct xe_bo *bo,
bo->placements[*c] = (struct ttm_place) {
.mem_type = XE_PL_STOLEN,
.flags = bo_flags & (XE_BO_FLAG_PINNED |
XE_BO_FLAG_GGTT) ?
.flags = force_contiguous(bo_flags) ?
TTM_PL_FLAG_CONTIGUOUS : 0,
};
*c += 1;
@ -442,6 +445,14 @@ static void xe_ttm_tt_destroy(struct ttm_device *ttm_dev, struct ttm_tt *tt)
kfree(tt);
}
static bool xe_ttm_resource_visible(struct ttm_resource *mem)
{
struct xe_ttm_vram_mgr_resource *vres =
to_xe_ttm_vram_mgr_resource(mem);
return vres->used_visible_size == mem->size;
}
static int xe_ttm_io_mem_reserve(struct ttm_device *bdev,
struct ttm_resource *mem)
{
@ -453,11 +464,9 @@ static int xe_ttm_io_mem_reserve(struct ttm_device *bdev,
return 0;
case XE_PL_VRAM0:
case XE_PL_VRAM1: {
struct xe_ttm_vram_mgr_resource *vres =
to_xe_ttm_vram_mgr_resource(mem);
struct xe_mem_region *vram = res_to_mem_region(mem);
if (vres->used_visible_size < mem->size)
if (!xe_ttm_resource_visible(mem))
return -EINVAL;
mem->bus.offset = mem->start << PAGE_SHIFT;
@ -876,6 +885,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
};
struct ttm_operation_ctx ctx = {
.interruptible = false,
.gfp_retry_mayfail = true,
};
struct ttm_resource *new_mem;
int ret;
@ -937,6 +947,7 @@ int xe_bo_restore_pinned(struct xe_bo *bo)
{
struct ttm_operation_ctx ctx = {
.interruptible = false,
.gfp_retry_mayfail = false,
};
struct ttm_resource *new_mem;
struct ttm_place *place = &bo->placements[0];
@ -1106,7 +1117,8 @@ static void xe_ttm_bo_purge(struct ttm_buffer_object *ttm_bo, struct ttm_operati
static void xe_ttm_bo_swap_notify(struct ttm_buffer_object *ttm_bo)
{
struct ttm_operation_ctx ctx = {
.interruptible = false
.interruptible = false,
.gfp_retry_mayfail = false,
};
if (ttm_bo->ttm) {
@ -1118,6 +1130,52 @@ static void xe_ttm_bo_swap_notify(struct ttm_buffer_object *ttm_bo)
}
}
static int xe_ttm_access_memory(struct ttm_buffer_object *ttm_bo,
unsigned long offset, void *buf, int len,
int write)
{
struct xe_bo *bo = ttm_to_xe_bo(ttm_bo);
struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
struct iosys_map vmap;
struct xe_res_cursor cursor;
struct xe_mem_region *vram;
int bytes_left = len;
xe_bo_assert_held(bo);
xe_device_assert_mem_access(xe);
if (!mem_type_is_vram(ttm_bo->resource->mem_type))
return -EIO;
/* FIXME: Use GPU for non-visible VRAM */
if (!xe_ttm_resource_visible(ttm_bo->resource))
return -EIO;
vram = res_to_mem_region(ttm_bo->resource);
xe_res_first(ttm_bo->resource, offset & PAGE_MASK,
bo->size - (offset & PAGE_MASK), &cursor);
do {
unsigned long page_offset = (offset & ~PAGE_MASK);
int byte_count = min((int)(PAGE_SIZE - page_offset), bytes_left);
iosys_map_set_vaddr_iomem(&vmap, (u8 __iomem *)vram->mapping +
cursor.start);
if (write)
xe_map_memcpy_to(xe, &vmap, page_offset, buf, byte_count);
else
xe_map_memcpy_from(xe, buf, &vmap, page_offset, byte_count);
buf += byte_count;
offset += byte_count;
bytes_left -= byte_count;
if (bytes_left)
xe_res_next(&cursor, PAGE_SIZE);
} while (bytes_left);
return len;
}
const struct ttm_device_funcs xe_ttm_funcs = {
.ttm_tt_create = xe_ttm_tt_create,
.ttm_tt_populate = xe_ttm_tt_populate,
@ -1127,6 +1185,7 @@ const struct ttm_device_funcs xe_ttm_funcs = {
.move = xe_bo_move,
.io_mem_reserve = xe_ttm_io_mem_reserve,
.io_mem_pfn = xe_ttm_io_mem_pfn,
.access_memory = xe_ttm_access_memory,
.release_notify = xe_ttm_bo_release_notify,
.eviction_valuable = ttm_bo_eviction_valuable,
.delete_mem_notify = xe_ttm_bo_delete_mem_notify,
@ -1137,6 +1196,8 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
{
struct xe_bo *bo = ttm_to_xe_bo(ttm_bo);
struct xe_device *xe = ttm_to_xe_device(ttm_bo->bdev);
struct xe_tile *tile;
u8 id;
if (bo->ttm.base.import_attach)
drm_prime_gem_destroy(&bo->ttm.base, NULL);
@ -1144,8 +1205,9 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list));
if (bo->ggtt_node && bo->ggtt_node->base.size)
xe_ggtt_remove_bo(bo->tile->mem.ggtt, bo);
for_each_tile(tile, xe, id)
if (bo->ggtt_node[id] && bo->ggtt_node[id]->base.size)
xe_ggtt_remove_bo(tile->mem.ggtt, bo);
#ifdef CONFIG_PROC_FS
if (bo->client)
@ -1243,11 +1305,50 @@ static vm_fault_t xe_gem_fault(struct vm_fault *vmf)
return ret;
}
static int xe_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
void *buf, int len, int write)
{
struct ttm_buffer_object *ttm_bo = vma->vm_private_data;
struct xe_bo *bo = ttm_to_xe_bo(ttm_bo);
struct xe_device *xe = xe_bo_device(bo);
int ret;
xe_pm_runtime_get(xe);
ret = ttm_bo_vm_access(vma, addr, buf, len, write);
xe_pm_runtime_put(xe);
return ret;
}
/**
* xe_bo_read() - Read from an xe_bo
* @bo: The buffer object to read from.
* @offset: The byte offset to start reading from.
* @dst: Location to store the read.
* @size: Size in bytes for the read.
*
* Read @size bytes from the @bo, starting from @offset, storing into @dst.
*
* Return: Zero on success, or negative error.
*/
int xe_bo_read(struct xe_bo *bo, u64 offset, void *dst, int size)
{
int ret;
ret = ttm_bo_access(&bo->ttm, offset, dst, size, 0);
if (ret >= 0 && ret != size)
ret = -EIO;
else if (ret == size)
ret = 0;
return ret;
}
static const struct vm_operations_struct xe_gem_vm_ops = {
.fault = xe_gem_fault,
.open = ttm_bo_vm_open,
.close = ttm_bo_vm_close,
.access = ttm_bo_vm_access
.access = xe_bo_vm_access,
};
static const struct drm_gem_object_funcs xe_gem_object_funcs = {
@ -1301,6 +1402,7 @@ struct xe_bo *___xe_bo_create_locked(struct xe_device *xe, struct xe_bo *bo,
struct ttm_operation_ctx ctx = {
.interruptible = true,
.no_wait_gpu = false,
.gfp_retry_mayfail = true,
};
struct ttm_placement *placement;
uint32_t alignment;
@ -1315,6 +1417,10 @@ struct xe_bo *___xe_bo_create_locked(struct xe_device *xe, struct xe_bo *bo,
return ERR_PTR(-EINVAL);
}
/* XE_BO_FLAG_GGTTx requires XE_BO_FLAG_GGTT also be set */
if ((flags & XE_BO_FLAG_GGTT_ALL) && !(flags & XE_BO_FLAG_GGTT))
return ERR_PTR(-EINVAL);
if (flags & (XE_BO_FLAG_VRAM_MASK | XE_BO_FLAG_STOLEN) &&
!(flags & XE_BO_FLAG_IGNORE_MIN_PAGE_SIZE) &&
((xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K) ||
@ -1505,19 +1611,29 @@ __xe_bo_create_locked(struct xe_device *xe,
bo->vm = vm;
if (bo->flags & XE_BO_FLAG_GGTT) {
if (!tile && flags & XE_BO_FLAG_STOLEN)
tile = xe_device_get_root_tile(xe);
struct xe_tile *t;
u8 id;
xe_assert(xe, tile);
if (!(bo->flags & XE_BO_FLAG_GGTT_ALL)) {
if (!tile && flags & XE_BO_FLAG_STOLEN)
tile = xe_device_get_root_tile(xe);
if (flags & XE_BO_FLAG_FIXED_PLACEMENT) {
err = xe_ggtt_insert_bo_at(tile->mem.ggtt, bo,
start + bo->size, U64_MAX);
} else {
err = xe_ggtt_insert_bo(tile->mem.ggtt, bo);
xe_assert(xe, tile);
}
for_each_tile(t, xe, id) {
if (t != tile && !(bo->flags & XE_BO_FLAG_GGTTx(t)))
continue;
if (flags & XE_BO_FLAG_FIXED_PLACEMENT) {
err = xe_ggtt_insert_bo_at(t->mem.ggtt, bo,
start + bo->size, U64_MAX);
} else {
err = xe_ggtt_insert_bo(t->mem.ggtt, bo);
}
if (err)
goto err_unlock_put_bo;
}
if (err)
goto err_unlock_put_bo;
}
return bo;
@ -1900,6 +2016,7 @@ int xe_bo_validate(struct xe_bo *bo, struct xe_vm *vm, bool allow_res_evict)
struct ttm_operation_ctx ctx = {
.interruptible = true,
.no_wait_gpu = false,
.gfp_retry_mayfail = true,
};
if (vm) {
@ -1910,6 +2027,7 @@ int xe_bo_validate(struct xe_bo *bo, struct xe_vm *vm, bool allow_res_evict)
ctx.resv = xe_vm_resv(vm);
}
trace_xe_bo_validate(bo);
return ttm_bo_validate(&bo->ttm, &bo->placement, &ctx);
}
@ -1961,13 +2079,15 @@ dma_addr_t xe_bo_addr(struct xe_bo *bo, u64 offset, size_t page_size)
int xe_bo_vmap(struct xe_bo *bo)
{
struct xe_device *xe = ttm_to_xe_device(bo->ttm.bdev);
void *virtual;
bool is_iomem;
int ret;
xe_bo_assert_held(bo);
if (!(bo->flags & XE_BO_FLAG_NEEDS_CPU_ACCESS))
if (drm_WARN_ON(&xe->drm, !(bo->flags & XE_BO_FLAG_NEEDS_CPU_ACCESS) ||
!force_contiguous(bo->flags)))
return -EINVAL;
if (!iosys_map_is_null(&bo->vmap))
@ -2243,6 +2363,7 @@ int xe_bo_migrate(struct xe_bo *bo, u32 mem_type)
struct ttm_operation_ctx ctx = {
.interruptible = true,
.no_wait_gpu = false,
.gfp_retry_mayfail = true,
};
struct ttm_placement placement;
struct ttm_place requested;
@ -2293,6 +2414,7 @@ int xe_bo_evict(struct xe_bo *bo, bool force_alloc)
.interruptible = false,
.no_wait_gpu = false,
.force_alloc = force_alloc,
.gfp_retry_mayfail = true,
};
struct ttm_placement placement;
int ret;
@ -2372,14 +2494,18 @@ void xe_bo_put_commit(struct llist_head *deferred)
void xe_bo_put(struct xe_bo *bo)
{
struct xe_tile *tile;
u8 id;
might_sleep();
if (bo) {
#ifdef CONFIG_PROC_FS
if (bo->client)
might_lock(&bo->client->bos_lock);
#endif
if (bo->ggtt_node && bo->ggtt_node->ggtt)
might_lock(&bo->ggtt_node->ggtt->lock);
for_each_tile(tile, xe_bo_device(bo), id)
if (bo->ggtt_node[id] && bo->ggtt_node[id]->ggtt)
might_lock(&bo->ggtt_node[id]->ggtt->lock);
drm_gem_object_put(&bo->ttm.base);
}
}

View File

@ -39,10 +39,22 @@
#define XE_BO_FLAG_NEEDS_64K BIT(15)
#define XE_BO_FLAG_NEEDS_2M BIT(16)
#define XE_BO_FLAG_GGTT_INVALIDATE BIT(17)
#define XE_BO_FLAG_GGTT0 BIT(18)
#define XE_BO_FLAG_GGTT1 BIT(19)
#define XE_BO_FLAG_GGTT2 BIT(20)
#define XE_BO_FLAG_GGTT3 BIT(21)
#define XE_BO_FLAG_GGTT_ALL (XE_BO_FLAG_GGTT0 | \
XE_BO_FLAG_GGTT1 | \
XE_BO_FLAG_GGTT2 | \
XE_BO_FLAG_GGTT3)
/* this one is trigger internally only */
#define XE_BO_FLAG_INTERNAL_TEST BIT(30)
#define XE_BO_FLAG_INTERNAL_64K BIT(31)
#define XE_BO_FLAG_GGTTx(tile) \
(XE_BO_FLAG_GGTT0 << (tile)->id)
#define XE_PTE_SHIFT 12
#define XE_PAGE_SIZE (1 << XE_PTE_SHIFT)
#define XE_PTE_MASK (XE_PAGE_SIZE - 1)
@ -194,18 +206,29 @@ xe_bo_main_addr(struct xe_bo *bo, size_t page_size)
}
static inline u32
xe_bo_ggtt_addr(struct xe_bo *bo)
__xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id)
{
if (XE_WARN_ON(!bo->ggtt_node))
struct xe_ggtt_node *ggtt_node = bo->ggtt_node[tile_id];
if (XE_WARN_ON(!ggtt_node))
return 0;
XE_WARN_ON(bo->ggtt_node->base.size > bo->size);
XE_WARN_ON(bo->ggtt_node->base.start + bo->ggtt_node->base.size > (1ull << 32));
return bo->ggtt_node->base.start;
XE_WARN_ON(ggtt_node->base.size > bo->size);
XE_WARN_ON(ggtt_node->base.start + ggtt_node->base.size > (1ull << 32));
return ggtt_node->base.start;
}
static inline u32
xe_bo_ggtt_addr(struct xe_bo *bo)
{
xe_assert(xe_bo_device(bo), bo->tile);
return __xe_bo_ggtt_addr(bo, bo->tile->id);
}
int xe_bo_vmap(struct xe_bo *bo);
void xe_bo_vunmap(struct xe_bo *bo);
int xe_bo_read(struct xe_bo *bo, u64 offset, void *dst, int size);
bool mem_type_is_vram(u32 mem_type);
bool xe_bo_is_vram(struct xe_bo *bo);

View File

@ -152,11 +152,17 @@ int xe_bo_restore_kernel(struct xe_device *xe)
}
if (bo->flags & XE_BO_FLAG_GGTT) {
struct xe_tile *tile = bo->tile;
struct xe_tile *tile;
u8 id;
mutex_lock(&tile->mem.ggtt->lock);
xe_ggtt_map_bo(tile->mem.ggtt, bo);
mutex_unlock(&tile->mem.ggtt->lock);
for_each_tile(tile, xe, id) {
if (tile != bo->tile && !(bo->flags & XE_BO_FLAG_GGTTx(tile)))
continue;
mutex_lock(&tile->mem.ggtt->lock);
xe_ggtt_map_bo(tile->mem.ggtt, bo);
mutex_unlock(&tile->mem.ggtt->lock);
}
}
/*

View File

@ -12,6 +12,7 @@
#include <drm/ttm/ttm_device.h>
#include <drm/ttm/ttm_placement.h>
#include "xe_device_types.h"
#include "xe_ggtt_types.h"
struct xe_device;
@ -38,8 +39,8 @@ struct xe_bo {
struct ttm_place placements[XE_BO_MAX_PLACEMENTS];
/** @placement: current placement for this BO */
struct ttm_placement placement;
/** @ggtt_node: GGTT node if this BO is mapped in the GGTT */
struct xe_ggtt_node *ggtt_node;
/** @ggtt_node: Array of GGTT nodes if this BO is mapped in the GGTTs */
struct xe_ggtt_node *ggtt_node[XE_MAX_TILES_PER_DEVICE];
/** @vmap: iosys map of this buffer */
struct iosys_map vmap;
/** @ttm_kmap: TTM bo kmap object for internal use only. Keep off. */

View File

@ -30,30 +30,39 @@
/**
* DOC: Xe device coredump
*
* Devices overview:
* Xe uses dev_coredump infrastructure for exposing the crash errors in a
* standardized way.
* devcoredump exposes a temporary device under /sys/class/devcoredump/
* which is linked with our card device directly.
* The core dump can be accessed either from
* /sys/class/drm/card<n>/device/devcoredump/ or from
* /sys/class/devcoredump/devcd<m> where
* /sys/class/devcoredump/devcd<m>/failing_device is a link to
* /sys/class/drm/card<n>/device/.
* standardized way. Once a crash occurs, devcoredump exposes a temporary
* node under ``/sys/class/devcoredump/devcd<m>/``. The same node is also
* accessible in ``/sys/class/drm/card<n>/device/devcoredump/``. The
* ``failing_device`` symlink points to the device that crashed and created the
* coredump.
*
* Snapshot at hang:
* The 'data' file is printed with a drm_printer pointer at devcoredump read
* time. For this reason, we need to take snapshots from when the hang has
* happened, and not only when the user is reading the file. Otherwise the
* information is outdated since the resets might have happened in between.
* The following characteristics are observed by xe when creating a device
* coredump:
*
* 'First' failure snapshot:
* In general, the first hang is the most critical one since the following hangs
* can be a consequence of the initial hang. For this reason we only take the
* snapshot of the 'first' failure and ignore subsequent calls of this function,
* at least while the coredump device is alive. Dev_coredump has a delayed work
* queue that will eventually delete the device and free all the dump
* information.
* **Snapshot at hang**:
* The 'data' file contains a snapshot of the HW and driver states at the time
* the hang happened. Due to the driver recovering from resets/crashes, it may
* not correspond to the state of the system when the file is read by
* userspace.
*
* **Coredump release**:
* After a coredump is generated, it stays in kernel memory until released by
* userpace by writing anything to it, or after an internal timer expires. The
* exact timeout may vary and should not be relied upon. Example to release
* a coredump:
*
* .. code-block:: shell
*
* $ > /sys/class/drm/card0/device/devcoredump/data
*
* **First failure only**:
* In general, the first hang is the most critical one since the following
* hangs can be a consequence of the initial hang. For this reason a snapshot
* is taken only for the first failure. Until the devcoredump is released by
* userspace or kernel, all subsequent hangs do not override the snapshot nor
* create new ones. Devcoredump has a delayed work queue that will eventually
* delete the file node and free all the dump information.
*/
#ifdef CONFIG_DEV_COREDUMP
@ -91,6 +100,7 @@ static ssize_t __xe_devcoredump_read(char *buffer, size_t count,
p = drm_coredump_printer(&iter);
drm_puts(&p, "**** Xe Device Coredump ****\n");
drm_printf(&p, "Reason: %s\n", ss->reason);
drm_puts(&p, "kernel: " UTS_RELEASE "\n");
drm_puts(&p, "module: " KBUILD_MODNAME "\n");
@ -98,7 +108,7 @@ static ssize_t __xe_devcoredump_read(char *buffer, size_t count,
drm_printf(&p, "Snapshot time: %lld.%09ld\n", ts.tv_sec, ts.tv_nsec);
ts = ktime_to_timespec64(ss->boot_time);
drm_printf(&p, "Uptime: %lld.%09ld\n", ts.tv_sec, ts.tv_nsec);
drm_printf(&p, "Process: %s\n", ss->process_name);
drm_printf(&p, "Process: %s [%d]\n", ss->process_name, ss->pid);
xe_device_snapshot_print(xe, &p);
drm_printf(&p, "\n**** GT #%d ****\n", ss->gt->info.id);
@ -130,6 +140,9 @@ static void xe_devcoredump_snapshot_free(struct xe_devcoredump_snapshot *ss)
{
int i;
kfree(ss->reason);
ss->reason = NULL;
xe_guc_log_snapshot_free(ss->guc.log);
ss->guc.log = NULL;
@ -170,16 +183,24 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
/* Ensure delayed work is captured before continuing */
flush_work(&ss->work);
if (!ss->read.buffer)
return -ENODEV;
mutex_lock(&coredump->lock);
if (offset >= ss->read.size)
if (!ss->read.buffer) {
mutex_unlock(&coredump->lock);
return -ENODEV;
}
if (offset >= ss->read.size) {
mutex_unlock(&coredump->lock);
return 0;
}
byte_copied = count < ss->read.size - offset ? count :
ss->read.size - offset;
memcpy(buffer, ss->read.buffer + offset, byte_copied);
mutex_unlock(&coredump->lock);
return byte_copied;
}
@ -193,15 +214,18 @@ static void xe_devcoredump_free(void *data)
cancel_work_sync(&coredump->snapshot.work);
mutex_lock(&coredump->lock);
xe_devcoredump_snapshot_free(&coredump->snapshot);
kvfree(coredump->snapshot.read.buffer);
/* To prevent stale data on next snapshot, clear everything */
memset(&coredump->snapshot, 0, sizeof(coredump->snapshot));
coredump->captured = false;
coredump->job = NULL;
drm_info(&coredump_to_xe(coredump)->drm,
"Xe device coredump has been deleted.\n");
mutex_unlock(&coredump->lock);
}
static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
@ -244,10 +268,10 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
}
static void devcoredump_snapshot(struct xe_devcoredump *coredump,
struct xe_exec_queue *q,
struct xe_sched_job *job)
{
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
struct xe_exec_queue *q = job->q;
struct xe_guc *guc = exec_queue_to_guc(q);
u32 adj_logical_mask = q->logical_mask;
u32 width_mask = (0x1 << q->width) - 1;
@ -260,12 +284,14 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
ss->snapshot_time = ktime_get_real();
ss->boot_time = ktime_get_boottime();
if (q->vm && q->vm->xef)
if (q->vm && q->vm->xef) {
process_name = q->vm->xef->process_name;
ss->pid = q->vm->xef->pid;
}
strscpy(ss->process_name, process_name);
ss->gt = q->gt;
coredump->job = job;
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
cookie = dma_fence_begin_signalling();
@ -284,10 +310,11 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
ss->guc.log = xe_guc_log_snapshot_capture(&guc->log, true);
ss->guc.ct = xe_guc_ct_snapshot_capture(&guc->ct);
ss->ge = xe_guc_exec_queue_snapshot_capture(q);
ss->job = xe_sched_job_snapshot_capture(job);
if (job)
ss->job = xe_sched_job_snapshot_capture(job);
ss->vm = xe_vm_snapshot_capture(q->vm);
xe_engine_snapshot_capture_for_job(job);
xe_engine_snapshot_capture_for_queue(q);
queue_work(system_unbound_wq, &ss->work);
@ -297,28 +324,42 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
/**
* xe_devcoredump - Take the required snapshots and initialize coredump device.
* @q: The faulty xe_exec_queue, where the issue was detected.
* @job: The faulty xe_sched_job, where the issue was detected.
* @fmt: Printf format + args to describe the reason for the core dump
*
* This function should be called at the crash time within the serialized
* gt_reset. It is skipped if we still have the core dump device available
* with the information of the 'first' snapshot.
*/
void xe_devcoredump(struct xe_sched_job *job)
__printf(3, 4)
void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const char *fmt, ...)
{
struct xe_device *xe = gt_to_xe(job->q->gt);
struct xe_device *xe = gt_to_xe(q->gt);
struct xe_devcoredump *coredump = &xe->devcoredump;
va_list varg;
mutex_lock(&coredump->lock);
if (coredump->captured) {
drm_dbg(&xe->drm, "Multiple hangs are occurring, but only the first snapshot was taken\n");
mutex_unlock(&coredump->lock);
return;
}
coredump->captured = true;
devcoredump_snapshot(coredump, job);
va_start(varg, fmt);
coredump->snapshot.reason = kvasprintf(GFP_ATOMIC, fmt, varg);
va_end(varg);
devcoredump_snapshot(coredump, q, job);
drm_info(&xe->drm, "Xe device coredump has been created\n");
drm_info(&xe->drm, "Check your /sys/class/drm/card%d/device/devcoredump/data\n",
xe->drm.primary->index);
mutex_unlock(&coredump->lock);
}
static void xe_driver_devcoredump_fini(void *arg)
@ -330,6 +371,18 @@ static void xe_driver_devcoredump_fini(void *arg)
int xe_devcoredump_init(struct xe_device *xe)
{
int err;
err = drmm_mutex_init(&xe->drm, &xe->devcoredump.lock);
if (err)
return err;
if (IS_ENABLED(CONFIG_LOCKDEP)) {
fs_reclaim_acquire(GFP_KERNEL);
might_lock(&xe->devcoredump.lock);
fs_reclaim_release(GFP_KERNEL);
}
return devm_add_action_or_reset(xe->drm.dev, xe_driver_devcoredump_fini, &xe->drm);
}

View File

@ -10,13 +10,16 @@
struct drm_printer;
struct xe_device;
struct xe_exec_queue;
struct xe_sched_job;
#ifdef CONFIG_DEV_COREDUMP
void xe_devcoredump(struct xe_sched_job *job);
void xe_devcoredump(struct xe_exec_queue *q, struct xe_sched_job *job, const char *fmt, ...);
int xe_devcoredump_init(struct xe_device *xe);
#else
static inline void xe_devcoredump(struct xe_sched_job *job)
static inline void xe_devcoredump(struct xe_exec_queue *q,
struct xe_sched_job *job,
const char *fmt, ...)
{
}

View File

@ -28,6 +28,10 @@ struct xe_devcoredump_snapshot {
ktime_t boot_time;
/** @process_name: Name of process that triggered this gpu hang */
char process_name[TASK_COMM_LEN];
/** @pid: Process id of process that triggered this gpu hang */
pid_t pid;
/** @reason: The reason the coredump was triggered */
char *reason;
/** @gt: Affected GT, used by forcewake for delayed capture */
struct xe_gt *gt;
@ -76,12 +80,12 @@ struct xe_devcoredump_snapshot {
* for reading the information.
*/
struct xe_devcoredump {
/** @captured: The snapshot of the first hang has already been taken. */
/** @lock: protects access to entire structure */
struct mutex lock;
/** @captured: The snapshot of the first hang has already been taken */
bool captured;
/** @snapshot: Snapshot is captured at time of the first crash */
struct xe_devcoredump_snapshot snapshot;
/** @job: Point to the faulting job */
struct xe_sched_job *job;
};
#endif

View File

@ -44,6 +44,7 @@
#include "xe_memirq.h"
#include "xe_mmio.h"
#include "xe_module.h"
#include "xe_oa.h"
#include "xe_observation.h"
#include "xe_pat.h"
#include "xe_pcode.h"
@ -55,6 +56,7 @@
#include "xe_ttm_sys_mgr.h"
#include "xe_vm.h"
#include "xe_vram.h"
#include "xe_vsec.h"
#include "xe_wait_user_fence.h"
#include "xe_wa.h"
@ -365,6 +367,10 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
goto err;
}
err = drmm_mutex_init(&xe->drm, &xe->pmt.lock);
if (err)
goto err;
err = xe_display_create(xe);
if (WARN_ON(err))
goto err;
@ -759,6 +765,8 @@ int xe_device_probe(struct xe_device *xe)
for_each_gt(gt, xe, id)
xe_gt_sanitize_freq(gt);
xe_vsec_init(xe);
return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
err_fini_display:

View File

@ -16,7 +16,7 @@
#include "xe_heci_gsc.h"
#include "xe_lmtt_types.h"
#include "xe_memirq_types.h"
#include "xe_oa.h"
#include "xe_oa_types.h"
#include "xe_platform_types.h"
#include "xe_pt_types.h"
#include "xe_sriov_types.h"
@ -42,8 +42,6 @@ struct xe_pat_ops;
#define GRAPHICS_VERx100(xe) ((xe)->info.graphics_verx100)
#define MEDIA_VERx100(xe) ((xe)->info.media_verx100)
#define IS_DGFX(xe) ((xe)->info.is_dgfx)
#define HAS_HECI_GSCFI(xe) ((xe)->info.has_heci_gscfi)
#define HAS_HECI_CSCFI(xe) ((xe)->info.has_heci_cscfi)
#define XE_VRAM_FLAGS_NEED64K BIT(0)
@ -296,14 +294,24 @@ struct xe_device {
/** @info.va_bits: Maximum bits of a virtual address */
u8 va_bits;
/** @info.is_dgfx: is discrete device */
u8 is_dgfx:1;
/** @info.has_asid: Has address space ID */
u8 has_asid:1;
/*
* Keep all flags below alphabetically sorted
*/
/** @info.force_execlist: Forced execlist submission */
u8 force_execlist:1;
/** @info.has_asid: Has address space ID */
u8 has_asid:1;
/** @info.has_atomic_enable_pte_bit: Device has atomic enable PTE bit */
u8 has_atomic_enable_pte_bit:1;
/** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
u8 has_device_atomics_on_smem:1;
/** @info.has_flat_ccs: Whether flat CCS metadata is used */
u8 has_flat_ccs:1;
/** @info.has_heci_cscfi: device has heci cscfi */
u8 has_heci_cscfi:1;
/** @info.has_heci_gscfi: device has heci gscfi */
u8 has_heci_gscfi:1;
/** @info.has_llc: Device has a shared CPU+GPU last level cache */
u8 has_llc:1;
/** @info.has_mmio_ext: Device has extra MMIO address range */
@ -314,6 +322,8 @@ struct xe_device {
u8 has_sriov:1;
/** @info.has_usm: Device has unified shared memory support */
u8 has_usm:1;
/** @info.is_dgfx: is discrete device */
u8 is_dgfx:1;
/**
* @info.probe_display: Probe display hardware. If set to
* false, the driver will behave as if there is no display
@ -323,20 +333,12 @@ struct xe_device {
* state the firmware or bootloader left it in.
*/
u8 probe_display:1;
/** @info.skip_guc_pc: Skip GuC based PM feature init */
u8 skip_guc_pc:1;
/** @info.skip_mtcfg: skip Multi-Tile configuration from MTCFG register */
u8 skip_mtcfg:1;
/** @info.skip_pcode: skip access to PCODE uC */
u8 skip_pcode:1;
/** @info.has_heci_gscfi: device has heci gscfi */
u8 has_heci_gscfi:1;
/** @info.has_heci_cscfi: device has heci cscfi */
u8 has_heci_cscfi:1;
/** @info.skip_guc_pc: Skip GuC based PM feature init */
u8 skip_guc_pc:1;
/** @info.has_atomic_enable_pte_bit: Device has atomic enable PTE bit */
u8 has_atomic_enable_pte_bit:1;
/** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
u8 has_device_atomics_on_smem:1;
} info;
/** @irq: device interrupt state */
@ -345,7 +347,7 @@ struct xe_device {
spinlock_t lock;
/** @irq.enabled: interrupts enabled on this device */
bool enabled;
atomic_t enabled;
} irq;
/** @ttm: ttm device */
@ -374,6 +376,8 @@ struct xe_device {
/** @sriov.pf: PF specific data */
struct xe_device_pf pf;
/** @sriov.vf: VF specific data */
struct xe_device_vf vf;
/** @sriov.wq: workqueue used by the virtualization workers */
struct workqueue_struct *wq;
@ -481,6 +485,12 @@ struct xe_device {
struct mutex lock;
} d3cold;
/** @pmt: Support the PMT driver callback interface */
struct {
/** @pmt.lock: protect access for telemetry data */
struct mutex lock;
} pmt;
/**
* @pm_callback_task: Track the active task that is running in either
* the runtime_suspend or runtime_resume callbacks.
@ -588,7 +598,7 @@ struct xe_file {
/** @vm.xe: xarray to store VMs */
struct xarray xa;
/**
* @vm.lock: Protects VM lookup + reference and removal a from
* @vm.lock: Protects VM lookup + reference and removal from
* file xarray. Not an intended to be an outer lock which does
* thing while being held.
*/
@ -601,10 +611,15 @@ struct xe_file {
struct xarray xa;
/**
* @exec_queue.lock: Protects exec queue lookup + reference and
* removal a frommfile xarray. Not an intended to be an outer
* lock which does thing while being held.
* removal from file xarray. Not intended to be an outer lock
* which does things while being held.
*/
struct mutex lock;
/**
* @exec_queue.pending_removal: items pending to be removed to
* synchronize GPU state update with ongoing query.
*/
atomic_t pending_removal;
} exec_queue;
/** @run_ticks: hw engine class run time in ticks for this drm client */

View File

@ -269,6 +269,49 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
}
}
static struct xe_hw_engine *any_engine(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned long gt_id;
for_each_gt(gt, xe, gt_id) {
struct xe_hw_engine *hwe = xe_gt_any_hw_engine(gt);
if (hwe)
return hwe;
}
return NULL;
}
static bool force_wake_get_any_engine(struct xe_device *xe,
struct xe_hw_engine **phwe,
unsigned int *pfw_ref)
{
enum xe_force_wake_domains domain;
unsigned int fw_ref;
struct xe_hw_engine *hwe;
struct xe_force_wake *fw;
hwe = any_engine(xe);
if (!hwe)
return false;
domain = xe_hw_engine_to_fw_domain(hwe);
fw = gt_to_fw(hwe->gt);
fw_ref = xe_force_wake_get(fw, domain);
if (!xe_force_wake_ref_has_domain(fw_ref, domain)) {
xe_force_wake_put(fw, fw_ref);
return false;
}
*phwe = hwe;
*pfw_ref = fw_ref;
return true;
}
static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
{
unsigned long class, i, gt_id, capacity[XE_ENGINE_CLASS_MAX] = { };
@ -280,7 +323,18 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
u64 gpu_timestamp;
unsigned int fw_ref;
/*
* Wait for any exec queue going away: their cycles will get updated on
* context switch out, so wait for that to happen
*/
wait_var_event(&xef->exec_queue.pending_removal,
!atomic_read(&xef->exec_queue.pending_removal));
xe_pm_runtime_get(xe);
if (!force_wake_get_any_engine(xe, &hwe, &fw_ref)) {
xe_pm_runtime_put(xe);
return;
}
/* Accumulate all the exec queues from this client */
mutex_lock(&xef->exec_queue.lock);
@ -295,33 +349,11 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
}
mutex_unlock(&xef->exec_queue.lock);
/* Get the total GPU cycles */
for_each_gt(gt, xe, gt_id) {
enum xe_force_wake_domains fw;
hwe = xe_gt_any_hw_engine(gt);
if (!hwe)
continue;
fw = xe_hw_engine_to_fw_domain(hwe);
fw_ref = xe_force_wake_get(gt_to_fw(gt), fw);
if (!xe_force_wake_ref_has_domain(fw_ref, fw)) {
hwe = NULL;
xe_force_wake_put(gt_to_fw(gt), fw_ref);
break;
}
gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
break;
}
gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
xe_force_wake_put(gt_to_fw(hwe->gt), fw_ref);
xe_pm_runtime_put(xe);
if (unlikely(!hwe))
return;
for (class = 0; class < XE_ENGINE_CLASS_MAX; class++) {
const char *class_name;

View File

@ -240,6 +240,7 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
return q;
}
ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
void xe_exec_queue_destroy(struct kref *ref)
{
@ -262,8 +263,11 @@ void xe_exec_queue_fini(struct xe_exec_queue *q)
/*
* Before releasing our ref to lrc and xef, accumulate our run ticks
* and wakeup any waiters.
*/
xe_exec_queue_update_run_ticks(q);
if (q->xef && atomic_dec_and_test(&q->xef->exec_queue.pending_removal))
wake_up_var(&q->xef->exec_queue.pending_removal);
for (i = 0; i < q->width; ++i)
xe_lrc_put(q->lrc[i]);
@ -826,7 +830,10 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
mutex_lock(&xef->exec_queue.lock);
q = xa_erase(&xef->exec_queue.xa, args->exec_queue_id);
if (q)
atomic_inc(&xef->exec_queue.pending_removal);
mutex_unlock(&xef->exec_queue.lock);
if (XE_IOCTL_DBG(xe, !q))
return -ENOENT;

View File

@ -598,10 +598,10 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
u64 start;
u64 offset, pte;
if (XE_WARN_ON(!bo->ggtt_node))
if (XE_WARN_ON(!bo->ggtt_node[ggtt->tile->id]))
return;
start = bo->ggtt_node->base.start;
start = bo->ggtt_node[ggtt->tile->id]->base.start;
for (offset = 0; offset < bo->size; offset += XE_PAGE_SIZE) {
pte = ggtt->pt_ops->pte_encode_bo(bo, offset, pat_index);
@ -612,15 +612,16 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
u64 start, u64 end)
{
int err;
u64 alignment = bo->min_align > 0 ? bo->min_align : XE_PAGE_SIZE;
u8 tile_id = ggtt->tile->id;
int err;
if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K)
alignment = SZ_64K;
if (XE_WARN_ON(bo->ggtt_node)) {
if (XE_WARN_ON(bo->ggtt_node[tile_id])) {
/* Someone's already inserted this BO in the GGTT */
xe_tile_assert(ggtt->tile, bo->ggtt_node->base.size == bo->size);
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == bo->size);
return 0;
}
@ -630,19 +631,19 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
xe_pm_runtime_get_noresume(tile_to_xe(ggtt->tile));
bo->ggtt_node = xe_ggtt_node_init(ggtt);
if (IS_ERR(bo->ggtt_node)) {
err = PTR_ERR(bo->ggtt_node);
bo->ggtt_node = NULL;
bo->ggtt_node[tile_id] = xe_ggtt_node_init(ggtt);
if (IS_ERR(bo->ggtt_node[tile_id])) {
err = PTR_ERR(bo->ggtt_node[tile_id]);
bo->ggtt_node[tile_id] = NULL;
goto out;
}
mutex_lock(&ggtt->lock);
err = drm_mm_insert_node_in_range(&ggtt->mm, &bo->ggtt_node->base, bo->size,
alignment, 0, start, end, 0);
err = drm_mm_insert_node_in_range(&ggtt->mm, &bo->ggtt_node[tile_id]->base,
bo->size, alignment, 0, start, end, 0);
if (err) {
xe_ggtt_node_fini(bo->ggtt_node);
bo->ggtt_node = NULL;
xe_ggtt_node_fini(bo->ggtt_node[tile_id]);
bo->ggtt_node[tile_id] = NULL;
} else {
xe_ggtt_map_bo(ggtt, bo);
}
@ -691,13 +692,15 @@ int xe_ggtt_insert_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
*/
void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
{
if (XE_WARN_ON(!bo->ggtt_node))
u8 tile_id = ggtt->tile->id;
if (XE_WARN_ON(!bo->ggtt_node[tile_id]))
return;
/* This BO is not currently in the GGTT */
xe_tile_assert(ggtt->tile, bo->ggtt_node->base.size == bo->size);
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == bo->size);
xe_ggtt_node_remove(bo->ggtt_node,
xe_ggtt_node_remove(bo->ggtt_node[tile_id],
bo->flags & XE_BO_FLAG_GGTT_INVALIDATE);
}

View File

@ -71,8 +71,14 @@ static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched,
static inline
struct xe_sched_job *xe_sched_first_pending_job(struct xe_gpu_scheduler *sched)
{
return list_first_entry_or_null(&sched->base.pending_list,
struct xe_sched_job, drm.list);
struct xe_sched_job *job;
spin_lock(&sched->base.job_list_lock);
job = list_first_entry_or_null(&sched->base.pending_list,
struct xe_sched_job, drm.list);
spin_unlock(&sched->base.job_list_lock);
return job;
}
static inline int

View File

@ -139,17 +139,29 @@ static int proxy_send_to_gsc(struct xe_gsc *gsc, u32 size)
return 0;
}
static int validate_proxy_header(struct xe_gsc_proxy_header *header,
static int validate_proxy_header(struct xe_gt *gt,
struct xe_gsc_proxy_header *header,
u32 source, u32 dest, u32 max_size)
{
u32 type = FIELD_GET(GSC_PROXY_TYPE, header->hdr);
u32 length = FIELD_GET(GSC_PROXY_PAYLOAD_LENGTH, header->hdr);
int ret = 0;
if (header->destination != dest || header->source != source)
return -ENOEXEC;
if (header->destination != dest || header->source != source) {
ret = -ENOEXEC;
goto out;
}
if (length + PROXY_HDR_SIZE > max_size)
return -E2BIG;
if (length + PROXY_HDR_SIZE > max_size) {
ret = -E2BIG;
goto out;
}
/* We only care about the status if this is a message for the driver */
if (dest == GSC_PROXY_ADDRESSING_KMD && header->status != 0) {
ret = -EIO;
goto out;
}
switch (type) {
case GSC_PROXY_MSG_TYPE_PROXY_PAYLOAD:
@ -157,12 +169,20 @@ static int validate_proxy_header(struct xe_gsc_proxy_header *header,
break;
fallthrough;
case GSC_PROXY_MSG_TYPE_PROXY_INVALID:
return -EIO;
ret = -EIO;
break;
default:
break;
}
return 0;
out:
if (ret)
xe_gt_err(gt,
"GSC proxy error: s=0x%x[0x%x], d=0x%x[0x%x], t=%u, l=0x%x, st=0x%x\n",
header->source, source, header->destination, dest,
type, length, header->status);
return ret;
}
#define proxy_header_wr(xe_, map_, offset_, field_, val_) \
@ -228,12 +248,17 @@ static int proxy_query(struct xe_gsc *gsc)
xe_map_memcpy_from(xe, to_csme_hdr, &gsc->proxy.from_gsc,
reply_offset, PROXY_HDR_SIZE);
/* stop if this was the last message */
if (FIELD_GET(GSC_PROXY_TYPE, to_csme_hdr->hdr) == GSC_PROXY_MSG_TYPE_PROXY_END)
/* Check the status and stop if this was the last message */
if (FIELD_GET(GSC_PROXY_TYPE, to_csme_hdr->hdr) == GSC_PROXY_MSG_TYPE_PROXY_END) {
ret = validate_proxy_header(gt, to_csme_hdr,
GSC_PROXY_ADDRESSING_GSC,
GSC_PROXY_ADDRESSING_KMD,
GSC_PROXY_BUFFER_SIZE - reply_offset);
break;
}
/* make sure the GSC-to-CSME proxy header is sane */
ret = validate_proxy_header(to_csme_hdr,
ret = validate_proxy_header(gt, to_csme_hdr,
GSC_PROXY_ADDRESSING_GSC,
GSC_PROXY_ADDRESSING_CSME,
GSC_PROXY_BUFFER_SIZE - reply_offset);
@ -262,7 +287,7 @@ static int proxy_query(struct xe_gsc *gsc)
}
/* make sure the CSME-to-GSC proxy header is sane */
ret = validate_proxy_header(gsc->proxy.from_csme,
ret = validate_proxy_header(gt, gsc->proxy.from_csme,
GSC_PROXY_ADDRESSING_CSME,
GSC_PROXY_ADDRESSING_GSC,
GSC_PROXY_BUFFER_SIZE - reply_offset);

View File

@ -748,10 +748,8 @@ static int do_gt_restart(struct xe_gt *gt)
if (err)
return err;
for_each_hw_engine(hwe, gt, id) {
for_each_hw_engine(hwe, gt, id)
xe_reg_sr_apply_mmio(&hwe->reg_sr, gt);
xe_reg_sr_apply_whitelist(hwe);
}
/* Get CCS mode in sync between sw/hw */
xe_gt_apply_ccs_mode(gt);

View File

@ -60,6 +60,21 @@ static inline void __xe_gt_printfn_info(struct drm_printer *p, struct va_format
xe_gt_info(gt, "%pV", vaf);
}
static inline void __xe_gt_printfn_dbg(struct drm_printer *p, struct va_format *vaf)
{
struct xe_gt *gt = p->arg;
struct drm_printer dbg;
/*
* The original xe_gt_dbg() callsite annotations are useless here,
* redirect to the tweaked drm_dbg_printer() instead.
*/
dbg = drm_dbg_printer(&gt_to_xe(gt)->drm, DRM_UT_DRIVER, NULL);
dbg.origin = p->origin;
drm_printf(&dbg, "GT%u: %pV", gt->info.id, vaf);
}
/**
* xe_gt_err_printer - Construct a &drm_printer that outputs to xe_gt_err()
* @gt: the &xe_gt pointer to use in xe_gt_err()
@ -90,4 +105,20 @@ static inline struct drm_printer xe_gt_info_printer(struct xe_gt *gt)
return p;
}
/**
* xe_gt_dbg_printer - Construct a &drm_printer that outputs like xe_gt_dbg()
* @gt: the &xe_gt pointer to use in xe_gt_dbg()
*
* Return: The &drm_printer object.
*/
static inline struct drm_printer xe_gt_dbg_printer(struct xe_gt *gt)
{
struct drm_printer p = {
.printfn = __xe_gt_printfn_dbg,
.arg = gt,
.origin = (const void *)_THIS_IP_,
};
return p;
}
#endif

View File

@ -207,6 +207,11 @@ static int pf_push_vf_cfg_preempt_timeout(struct xe_gt *gt, unsigned int vfid, u
return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_PREEMPT_TIMEOUT_KEY, *preempt_timeout);
}
static int pf_push_vf_cfg_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority)
{
return pf_push_vf_cfg_u32(gt, vfid, GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY, priority);
}
static int pf_push_vf_cfg_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
{
return pf_push_vf_cfg_u64(gt, vfid, GUC_KLV_VF_CFG_LMEM_SIZE_KEY, size);
@ -1540,8 +1545,6 @@ static u64 pf_query_max_lmem(struct xe_gt *gt)
#ifdef CONFIG_DRM_XE_DEBUG_SRIOV
#define MAX_FAIR_LMEM SZ_128M /* XXX: make it small for the driver bringup */
#else
#define MAX_FAIR_LMEM SZ_2G /* XXX: known issue with allocating BO over 2GiB */
#endif
static u64 pf_estimate_fair_lmem(struct xe_gt *gt, unsigned int num_vfs)
@ -1767,6 +1770,77 @@ u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfi
return preempt_timeout;
}
static const char *sched_priority_unit(u32 priority)
{
return priority == GUC_SCHED_PRIORITY_LOW ? "(low)" :
priority == GUC_SCHED_PRIORITY_NORMAL ? "(normal)" :
priority == GUC_SCHED_PRIORITY_HIGH ? "(high)" :
"(?)";
}
static int pf_provision_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
int err;
err = pf_push_vf_cfg_sched_priority(gt, vfid, priority);
if (unlikely(err))
return err;
config->sched_priority = priority;
return 0;
}
static int pf_get_sched_priority(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
return config->sched_priority;
}
/**
* xe_gt_sriov_pf_config_set_sched_priority() - Configure scheduling priority.
* @gt: the &xe_gt
* @vfid: the VF identifier
* @priority: requested scheduling priority
*
* This function can only be called on PF.
*
* Return: 0 on success or a negative error code on failure.
*/
int xe_gt_sriov_pf_config_set_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority)
{
int err;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
err = pf_provision_sched_priority(gt, vfid, priority);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, priority,
xe_gt_sriov_pf_config_get_sched_priority(gt, vfid),
"scheduling priority", sched_priority_unit, err);
}
/**
* xe_gt_sriov_pf_config_get_sched_priority - Get VF's scheduling priority.
* @gt: the &xe_gt
* @vfid: the VF identifier
*
* This function can only be called on PF.
*
* Return: VF's (or PF's) scheduling priority.
*/
u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid)
{
u32 priority;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
priority = pf_get_sched_priority(gt, vfid);
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return priority;
}
static void pf_reset_config_sched(struct xe_gt *gt, struct xe_gt_sriov_config *config)
{
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));

View File

@ -44,6 +44,9 @@ u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfi
int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout);
u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_set_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority);
u32 xe_gt_sriov_pf_config_get_threshold(struct xe_gt *gt, unsigned int vfid,
enum xe_guc_klv_threshold_index index);
int xe_gt_sriov_pf_config_set_threshold(struct xe_gt *gt, unsigned int vfid,

View File

@ -33,6 +33,8 @@ struct xe_gt_sriov_config {
u32 exec_quantum;
/** @preempt_timeout: preemption timeout in microseconds. */
u32 preempt_timeout;
/** @sched_priority: scheduling priority. */
u32 sched_priority;
/** @thresholds: GuC thresholds for adverse events notifications. */
u32 thresholds[XE_GUC_KLV_NUM_THRESHOLDS];
};

View File

@ -164,6 +164,7 @@ static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
*       contexts_spare
*       exec_quantum_ms
*       preempt_timeout_us
*       sched_priority
*    vf1
*       ggtt_quota
*       lmem_quota
@ -171,6 +172,7 @@ static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
*       contexts_quota
*       exec_quantum_ms
*       preempt_timeout_us
*       sched_priority
*/
#define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \
@ -209,6 +211,7 @@ DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n");
/*
* /sys/kernel/debug/dri/0/
@ -295,6 +298,8 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
&exec_quantum_fops);
debugfs_create_file_unsafe("preempt_timeout_us", 0644, parent, parent,
&preempt_timeout_fops);
debugfs_create_file_unsafe("sched_priority", 0644, parent, parent,
&sched_priority_fops);
/* register all threshold attributes */
#define register_threshold_attribute(TAG, NAME, ...) \

View File

@ -18,7 +18,7 @@
* is within a range of supported VF numbers (up to maximum number of VFs that
* driver can support, including VF0 that represents the PF itself).
*
* Note: Effective only on debug builds. See `Xe ASSERTs`_ for more information.
* Note: Effective only on debug builds. See `Xe Asserts`_ for more information.
*/
#define xe_gt_sriov_pf_assert_vfid(gt, vfid) xe_sriov_pf_assert_vfid(gt_to_xe(gt), (vfid))

View File

@ -135,14 +135,33 @@ static int pf_update_policy_u32(struct xe_gt *gt, u16 key, u32 *policy, u32 valu
return 0;
}
static int pf_provision_sched_if_idle(struct xe_gt *gt, bool enable)
static void pf_bulk_reset_sched_priority(struct xe_gt *gt, u32 priority)
{
unsigned int total_vfs = 1 + xe_gt_sriov_pf_get_totalvfs(gt);
unsigned int n;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
return pf_update_policy_bool(gt, GUC_KLV_VGT_POLICY_SCHED_IF_IDLE_KEY,
&gt->sriov.pf.policy.guc.sched_if_idle,
enable);
for (n = 0; n < total_vfs; n++)
gt->sriov.pf.vfs[n].config.sched_priority = priority;
}
static int pf_provision_sched_if_idle(struct xe_gt *gt, bool enable)
{
int err;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
err = pf_update_policy_bool(gt, GUC_KLV_VGT_POLICY_SCHED_IF_IDLE_KEY,
&gt->sriov.pf.policy.guc.sched_if_idle,
enable);
if (!err)
pf_bulk_reset_sched_priority(gt, enable ? GUC_SCHED_PRIORITY_NORMAL :
GUC_SCHED_PRIORITY_LOW);
return err;
}
static int pf_reprovision_sched_if_idle(struct xe_gt *gt)

View File

@ -27,6 +27,7 @@
#include "xe_guc_relay.h"
#include "xe_mmio.h"
#include "xe_sriov.h"
#include "xe_sriov_vf.h"
#include "xe_uc_fw.h"
#include "xe_wopcm.h"
@ -223,6 +224,44 @@ int xe_gt_sriov_vf_bootstrap(struct xe_gt *gt)
return 0;
}
static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
{
u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = {
FIELD_PREP(GUC_HXG_MSG_0_ORIGIN, GUC_HXG_ORIGIN_HOST) |
FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE),
};
int ret;
ret = xe_guc_mmio_send(guc, request, ARRAY_SIZE(request));
return ret > 0 ? -EPROTO : ret;
}
/**
* xe_gt_sriov_vf_notify_resfix_done - Notify GuC about resource fixups apply completed.
* @gt: the &xe_gt struct instance linked to target GuC
*
* Returns: 0 if the operation completed successfully, or a negative error
* code otherwise.
*/
int xe_gt_sriov_vf_notify_resfix_done(struct xe_gt *gt)
{
struct xe_guc *guc = &gt->uc.guc;
int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
err = guc_action_vf_notify_resfix_done(guc);
if (unlikely(err))
xe_gt_sriov_err(gt, "Failed to notify GuC about resource fixup done (%pe)\n",
ERR_PTR(err));
else
xe_gt_sriov_dbg_verbose(gt, "sent GuC resource fixup done\n");
return err;
}
static int guc_action_query_single_klv(struct xe_guc *guc, u32 key,
u32 *value, u32 value_len)
{
@ -692,6 +731,30 @@ int xe_gt_sriov_vf_connect(struct xe_gt *gt)
return err;
}
/**
* xe_gt_sriov_vf_migrated_event_handler - Start a VF migration recovery,
* or just mark that a GuC is ready for it.
* @gt: the &xe_gt struct instance linked to target GuC
*
* This function shall be called only by VF.
*/
void xe_gt_sriov_vf_migrated_event_handler(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
xe_gt_assert(gt, IS_SRIOV_VF(xe));
set_bit(gt->info.id, &xe->sriov.vf.migration.gt_flags);
/*
* We need to be certain that if all flags were set, at least one
* thread will notice that and schedule the recovery.
*/
smp_mb__after_atomic();
xe_gt_sriov_info(gt, "ready for recovery after migration\n");
xe_sriov_vf_start_migration_recovery(xe);
}
static bool vf_is_negotiated(struct xe_gt *gt, u16 major, u16 minor)
{
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));

View File

@ -17,6 +17,8 @@ int xe_gt_sriov_vf_query_config(struct xe_gt *gt);
int xe_gt_sriov_vf_connect(struct xe_gt *gt);
int xe_gt_sriov_vf_query_runtime(struct xe_gt *gt);
int xe_gt_sriov_vf_prepare_ggtt(struct xe_gt *gt);
int xe_gt_sriov_vf_notify_resfix_done(struct xe_gt *gt);
void xe_gt_sriov_vf_migrated_event_handler(struct xe_gt *gt);
u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt);
u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt);

View File

@ -6,15 +6,11 @@
#ifndef _XE_GT_STATS_H_
#define _XE_GT_STATS_H_
#include "xe_gt_stats_types.h"
struct xe_gt;
struct drm_printer;
enum xe_gt_stats_id {
XE_GT_STATS_ID_TLB_INVAL,
/* must be the last entry */
__XE_GT_STATS_NUM_IDS,
};
#ifdef CONFIG_DEBUG_FS
int xe_gt_stats_print_info(struct xe_gt *gt, struct drm_printer *p);
void xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id, int incr);

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2024 Intel Corporation
*/
#ifndef _XE_GT_STATS_TYPES_H_
#define _XE_GT_STATS_TYPES_H_
enum xe_gt_stats_id {
XE_GT_STATS_ID_TLB_INVAL,
/* must be the last entry */
__XE_GT_STATS_NUM_IDS,
};
#endif

View File

@ -8,6 +8,7 @@
#include <regs/xe_gt_regs.h>
#include "xe_device.h"
#include "xe_gt.h"
#include "xe_gt_printk.h"
#include "xe_gt_sysfs.h"
#include "xe_gt_throttle.h"
#include "xe_mmio.h"
@ -53,6 +54,7 @@ static u32 read_status(struct xe_gt *gt)
{
u32 status = xe_gt_throttle_get_limit_reasons(gt) & GT0_PERF_LIMIT_REASONS_MASK;
xe_gt_dbg(gt, "throttle reasons: 0x%08x\n", status);
return status;
}

View File

@ -65,6 +65,14 @@ invalidation_fence_signal(struct xe_device *xe, struct xe_gt_tlb_invalidation_fe
__invalidation_fence_signal(xe, fence);
}
void xe_gt_tlb_invalidation_fence_signal(struct xe_gt_tlb_invalidation_fence *fence)
{
if (WARN_ON_ONCE(!fence->gt))
return;
__invalidation_fence_signal(gt_to_xe(fence->gt), fence);
}
static void xe_gt_tlb_fence_timeout(struct work_struct *work)
{
struct xe_gt *gt = container_of(work, struct xe_gt,
@ -253,9 +261,18 @@ static int xe_gt_tlb_invalidation_guc(struct xe_gt *gt,
0, /* seqno, replaced in send_tlb_invalidation */
MAKE_INVAL_OP(XE_GUC_TLB_INVAL_GUC),
};
int ret;
return send_tlb_invalidation(&gt->uc.guc, fence, action,
ARRAY_SIZE(action));
ret = send_tlb_invalidation(&gt->uc.guc, fence, action,
ARRAY_SIZE(action));
/*
* -ECANCELED indicates the CT is stopped for a GT reset. TLB caches
* should be nuked on a GT reset so this error can be ignored.
*/
if (ret == -ECANCELED)
return 0;
return ret;
}
/**

View File

@ -28,6 +28,7 @@ int xe_guc_tlb_invalidation_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
void xe_gt_tlb_invalidation_fence_init(struct xe_gt *gt,
struct xe_gt_tlb_invalidation_fence *fence,
bool stack);
void xe_gt_tlb_invalidation_fence_signal(struct xe_gt_tlb_invalidation_fence *fence);
static inline void
xe_gt_tlb_invalidation_fence_wait(struct xe_gt_tlb_invalidation_fence *fence)

View File

@ -11,10 +11,10 @@
#include "xe_gt_idle_types.h"
#include "xe_gt_sriov_pf_types.h"
#include "xe_gt_sriov_vf_types.h"
#include "xe_gt_stats.h"
#include "xe_gt_stats_types.h"
#include "xe_hw_engine_types.h"
#include "xe_hw_fence_types.h"
#include "xe_oa.h"
#include "xe_oa_types.h"
#include "xe_reg_sr_types.h"
#include "xe_sa_types.h"
#include "xe_uc_types.h"

View File

@ -44,7 +44,15 @@ static u32 guc_bo_ggtt_addr(struct xe_guc *guc,
struct xe_bo *bo)
{
struct xe_device *xe = guc_to_xe(guc);
u32 addr = xe_bo_ggtt_addr(bo);
u32 addr;
/*
* For most BOs, the address on the allocating tile is fine. However for
* some, e.g. G2G CTB, the address on a specific tile is required as it
* might be different for each tile. So, just always ask for the address
* on the target GuC.
*/
addr = __xe_bo_ggtt_addr(bo, gt_to_tile(guc_to_gt(guc))->id);
/* GuC addresses above GUC_GGTT_TOP don't map through the GTT */
xe_assert(xe, addr >= xe_wopcm_size(guc_to_xe(guc)));
@ -244,6 +252,293 @@ static void guc_write_params(struct xe_guc *guc)
xe_mmio_write32(&gt->mmio, SOFT_SCRATCH(1 + i), guc->params[i]);
}
static int guc_action_register_g2g_buffer(struct xe_guc *guc, u32 type, u32 dst_tile, u32 dst_dev,
u32 desc_addr, u32 buff_addr, u32 size)
{
struct xe_gt *gt = guc_to_gt(guc);
struct xe_device *xe = gt_to_xe(gt);
u32 action[] = {
XE_GUC_ACTION_REGISTER_G2G,
FIELD_PREP(XE_G2G_REGISTER_SIZE, size / SZ_4K - 1) |
FIELD_PREP(XE_G2G_REGISTER_TYPE, type) |
FIELD_PREP(XE_G2G_REGISTER_TILE, dst_tile) |
FIELD_PREP(XE_G2G_REGISTER_DEVICE, dst_dev),
desc_addr,
buff_addr,
};
xe_assert(xe, (type == XE_G2G_TYPE_IN) || (type == XE_G2G_TYPE_OUT));
xe_assert(xe, !(size % SZ_4K));
return xe_guc_ct_send_block(&guc->ct, action, ARRAY_SIZE(action));
}
static int guc_action_deregister_g2g_buffer(struct xe_guc *guc, u32 type, u32 dst_tile, u32 dst_dev)
{
struct xe_gt *gt = guc_to_gt(guc);
struct xe_device *xe = gt_to_xe(gt);
u32 action[] = {
XE_GUC_ACTION_DEREGISTER_G2G,
FIELD_PREP(XE_G2G_DEREGISTER_TYPE, type) |
FIELD_PREP(XE_G2G_DEREGISTER_TILE, dst_tile) |
FIELD_PREP(XE_G2G_DEREGISTER_DEVICE, dst_dev),
};
xe_assert(xe, (type == XE_G2G_TYPE_IN) || (type == XE_G2G_TYPE_OUT));
return xe_guc_ct_send_block(&guc->ct, action, ARRAY_SIZE(action));
}
#define G2G_DEV(gt) (((gt)->info.type == XE_GT_TYPE_MAIN) ? 0 : 1)
#define G2G_BUFFER_SIZE (SZ_4K)
#define G2G_DESC_SIZE (64)
#define G2G_DESC_AREA_SIZE (SZ_4K)
/*
* Generate a unique id for each bi-directional CTB for each pair of
* near and far tiles/devices. The id can then be used as an index into
* a single allocation that is sub-divided into multiple CTBs.
*
* For example, with two devices per tile and two tiles, the table should
* look like:
* Far <tile>.<dev>
* 0.0 0.1 1.0 1.1
* N 0.0 --/-- 00/01 02/03 04/05
* e 0.1 01/00 --/-- 06/07 08/09
* a 1.0 03/02 07/06 --/-- 10/11
* r 1.1 05/04 09/08 11/10 --/--
*
* Where each entry is Rx/Tx channel id.
*
* So GuC #3 (tile 1, dev 1) talking to GuC #2 (tile 1, dev 0) would
* be reading from channel #11 and writing to channel #10. Whereas,
* GuC #2 talking to GuC #3 would be read on #10 and write to #11.
*/
static unsigned int g2g_slot(u32 near_tile, u32 near_dev, u32 far_tile, u32 far_dev,
u32 type, u32 max_inst, bool have_dev)
{
u32 near = near_tile, far = far_tile;
u32 idx = 0, x, y, direction;
int i;
if (have_dev) {
near = (near << 1) | near_dev;
far = (far << 1) | far_dev;
}
/* No need to send to one's self */
if (far == near)
return -1;
if (far > near) {
/* Top right table half */
x = far;
y = near;
/* T/R is 'forwards' direction */
direction = type;
} else {
/* Bottom left table half */
x = near;
y = far;
/* B/L is 'backwards' direction */
direction = (1 - type);
}
/* Count the rows prior to the target */
for (i = y; i > 0; i--)
idx += max_inst - i;
/* Count this row up to the target */
idx += (x - 1 - y);
/* Slots are in Rx/Tx pairs */
idx *= 2;
/* Pick Rx/Tx direction */
idx += direction;
return idx;
}
static int guc_g2g_register(struct xe_guc *near_guc, struct xe_gt *far_gt, u32 type, bool have_dev)
{
struct xe_gt *near_gt = guc_to_gt(near_guc);
struct xe_device *xe = gt_to_xe(near_gt);
struct xe_bo *g2g_bo;
u32 near_tile = gt_to_tile(near_gt)->id;
u32 near_dev = G2G_DEV(near_gt);
u32 far_tile = gt_to_tile(far_gt)->id;
u32 far_dev = G2G_DEV(far_gt);
u32 max = xe->info.gt_count;
u32 base, desc, buf;
int slot;
/* G2G is not allowed between different cards */
xe_assert(xe, xe == gt_to_xe(far_gt));
g2g_bo = near_guc->g2g.bo;
xe_assert(xe, g2g_bo);
slot = g2g_slot(near_tile, near_dev, far_tile, far_dev, type, max, have_dev);
xe_assert(xe, slot >= 0);
base = guc_bo_ggtt_addr(near_guc, g2g_bo);
desc = base + slot * G2G_DESC_SIZE;
buf = base + G2G_DESC_AREA_SIZE + slot * G2G_BUFFER_SIZE;
xe_assert(xe, (desc - base + G2G_DESC_SIZE) <= G2G_DESC_AREA_SIZE);
xe_assert(xe, (buf - base + G2G_BUFFER_SIZE) <= g2g_bo->size);
return guc_action_register_g2g_buffer(near_guc, type, far_tile, far_dev,
desc, buf, G2G_BUFFER_SIZE);
}
static void guc_g2g_deregister(struct xe_guc *guc, u32 far_tile, u32 far_dev, u32 type)
{
guc_action_deregister_g2g_buffer(guc, type, far_tile, far_dev);
}
static u32 guc_g2g_size(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
struct xe_device *xe = gt_to_xe(gt);
unsigned int count = xe->info.gt_count;
u32 num_channels = (count * (count - 1)) / 2;
xe_assert(xe, num_channels * XE_G2G_TYPE_LIMIT * G2G_DESC_SIZE <= G2G_DESC_AREA_SIZE);
return num_channels * XE_G2G_TYPE_LIMIT * G2G_BUFFER_SIZE + G2G_DESC_AREA_SIZE;
}
static bool xe_guc_g2g_wanted(struct xe_device *xe)
{
/* Can't do GuC to GuC communication if there is only one GuC */
if (xe->info.gt_count <= 1)
return false;
/* No current user */
return false;
}
static int guc_g2g_alloc(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
struct xe_device *xe = gt_to_xe(gt);
struct xe_tile *tile = gt_to_tile(gt);
struct xe_bo *bo;
u32 g2g_size;
if (guc->g2g.bo)
return 0;
if (gt->info.id != 0) {
struct xe_gt *root_gt = xe_device_get_gt(xe, 0);
struct xe_guc *root_guc = &root_gt->uc.guc;
struct xe_bo *bo;
bo = xe_bo_get(root_guc->g2g.bo);
if (!bo)
return -ENODEV;
guc->g2g.bo = bo;
guc->g2g.owned = false;
return 0;
}
g2g_size = guc_g2g_size(guc);
bo = xe_managed_bo_create_pin_map(xe, tile, g2g_size,
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_ALL |
XE_BO_FLAG_GGTT_INVALIDATE);
if (IS_ERR(bo))
return PTR_ERR(bo);
xe_map_memset(xe, &bo->vmap, 0, 0, g2g_size);
guc->g2g.bo = bo;
guc->g2g.owned = true;
return 0;
}
static void guc_g2g_fini(struct xe_guc *guc)
{
if (!guc->g2g.bo)
return;
/* Unpinning the owned object is handled by generic shutdown */
if (!guc->g2g.owned)
xe_bo_put(guc->g2g.bo);
guc->g2g.bo = NULL;
}
static int guc_g2g_start(struct xe_guc *guc)
{
struct xe_gt *far_gt, *gt = guc_to_gt(guc);
struct xe_device *xe = gt_to_xe(gt);
unsigned int i, j;
int t, err;
bool have_dev;
if (!guc->g2g.bo) {
int ret;
ret = guc_g2g_alloc(guc);
if (ret)
return ret;
}
/* GuC interface will need extending if more GT device types are ever created. */
xe_gt_assert(gt, (gt->info.type == XE_GT_TYPE_MAIN) || (gt->info.type == XE_GT_TYPE_MEDIA));
/* Channel numbering depends on whether there are multiple GTs per tile */
have_dev = xe->info.gt_count > xe->info.tile_count;
for_each_gt(far_gt, xe, i) {
u32 far_tile, far_dev;
if (far_gt->info.id == gt->info.id)
continue;
far_tile = gt_to_tile(far_gt)->id;
far_dev = G2G_DEV(far_gt);
for (t = 0; t < XE_G2G_TYPE_LIMIT; t++) {
err = guc_g2g_register(guc, far_gt, t, have_dev);
if (err) {
while (--t >= 0)
guc_g2g_deregister(guc, far_tile, far_dev, t);
goto err_deregister;
}
}
}
return 0;
err_deregister:
for_each_gt(far_gt, xe, j) {
u32 tile, dev;
if (far_gt->info.id == gt->info.id)
continue;
if (j >= i)
break;
tile = gt_to_tile(far_gt)->id;
dev = G2G_DEV(far_gt);
for (t = 0; t < XE_G2G_TYPE_LIMIT; t++)
guc_g2g_deregister(guc, tile, dev, t);
}
return err;
}
static void guc_fini_hw(void *arg)
{
struct xe_guc *guc = arg;
@ -253,6 +548,8 @@ static void guc_fini_hw(void *arg)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
xe_uc_fini_hw(&guc_to_gt(guc)->uc);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
guc_g2g_fini(guc);
}
/**
@ -423,7 +720,16 @@ int xe_guc_init_post_hwconfig(struct xe_guc *guc)
int xe_guc_post_load_init(struct xe_guc *guc)
{
int ret;
xe_guc_ads_populate_post_load(&guc->ads);
if (xe_guc_g2g_wanted(guc_to_xe(guc))) {
ret = guc_g2g_start(guc);
if (ret)
return ret;
}
guc->submission_state.enabled = true;
return 0;
@ -945,7 +1251,6 @@ int xe_guc_mmio_send_recv(struct xe_guc *guc, const u32 *request,
BUILD_BUG_ON(VF_SW_FLAG_COUNT != MED_VF_SW_FLAG_COUNT);
xe_assert(xe, !xe_guc_ct_enabled(&guc->ct));
xe_assert(xe, len);
xe_assert(xe, len <= VF_SW_FLAG_COUNT);
xe_assert(xe, len <= MED_VF_SW_FLAG_COUNT);
@ -1099,10 +1404,21 @@ int xe_guc_self_cfg64(struct xe_guc *guc, u16 key, u64 val)
return guc_self_cfg(guc, key, 2, val);
}
static void xe_guc_sw_0_irq_handler(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
if (IS_SRIOV_VF(gt_to_xe(gt)))
xe_gt_sriov_vf_migrated_event_handler(gt);
}
void xe_guc_irq_handler(struct xe_guc *guc, const u16 iir)
{
if (iir & GUC_INTR_GUC2HOST)
xe_guc_ct_irq_handler(&guc->ct);
if (iir & GUC_INTR_SW_INT_0)
xe_guc_sw_0_irq_handler(guc);
}
void xe_guc_sanitize(struct xe_guc *guc)

View File

@ -231,11 +231,6 @@ static size_t guc_ads_size(struct xe_guc_ads *ads)
guc_ads_private_data_size(ads);
}
static bool needs_wa_1607983814(struct xe_device *xe)
{
return GRAPHICS_VERx100(xe) < 1250;
}
static size_t calculate_regset_size(struct xe_gt *gt)
{
struct xe_reg_sr_entry *sr_entry;
@ -250,7 +245,7 @@ static size_t calculate_regset_size(struct xe_gt *gt)
count += ADS_REGSET_EXTRA_MAX * XE_NUM_HW_ENGINES;
if (needs_wa_1607983814(gt_to_xe(gt)))
if (XE_WA(gt, 1607983814))
count += LNCFCMOCS_REG_COUNT;
return count * sizeof(struct guc_mmio_reg);
@ -709,7 +704,6 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads,
struct iosys_map *regset_map,
struct xe_hw_engine *hwe)
{
struct xe_device *xe = ads_to_xe(ads);
struct xe_hw_engine *hwe_rcs_reset_domain =
xe_gt_any_hw_engine_by_reset_domain(hwe->gt, XE_ENGINE_CLASS_RENDER);
struct xe_reg_sr_entry *entry;
@ -740,8 +734,7 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads,
guc_mmio_regset_write_one(ads, regset_map, e->reg, count++);
}
/* Wa_1607983814 */
if (needs_wa_1607983814(xe) && hwe->class == XE_ENGINE_CLASS_RENDER) {
if (XE_WA(hwe->gt, 1607983814) && hwe->class == XE_ENGINE_CLASS_RENDER) {
for (i = 0; i < LNCFCMOCS_REG_COUNT; i++) {
guc_mmio_regset_write_one(ads, regset_map,
XELP_LNCFCMOCS(i), count++);

View File

@ -1806,7 +1806,6 @@ void xe_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot, struct drm
if (!devcore_snapshot->matched_node)
return;
xe_gt_assert(gt, snapshot->source <= XE_ENGINE_CAPTURE_SOURCE_GUC);
xe_gt_assert(gt, snapshot->hwe);
capture_class = xe_engine_class_to_guc_capture_class(snapshot->hwe->class);
@ -1815,7 +1814,8 @@ void xe_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot, struct drm
snapshot->name ? snapshot->name : "",
snapshot->logical_instance);
drm_printf(p, "\tCapture_source: %s\n",
snapshot->source == XE_ENGINE_CAPTURE_SOURCE_GUC ? "GuC" : "Manual");
devcore_snapshot->matched_node->source == XE_ENGINE_CAPTURE_SOURCE_GUC ?
"GuC" : "Manual");
drm_printf(p, "\tCoverage: %s\n", grptype[devcore_snapshot->matched_node->is_partial]);
drm_printf(p, "\tForcewake: domain 0x%x, ref %d\n",
snapshot->forcewake.domain, snapshot->forcewake.ref);
@ -1840,29 +1840,24 @@ void xe_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot, struct drm
}
/**
* xe_guc_capture_get_matching_and_lock - Matching GuC capture for the job.
* @job: The job object.
* xe_guc_capture_get_matching_and_lock - Matching GuC capture for the queue.
* @q: The exec queue object
*
* Search within the capture outlist for the job, could be used for check if
* GuC capture is ready for the job.
* Search within the capture outlist for the queue, could be used for check if
* GuC capture is ready for the queue.
* If found, the locked boolean of the node will be flagged.
*
* Returns: found guc-capture node ptr else NULL
*/
struct __guc_capture_parsed_output *
xe_guc_capture_get_matching_and_lock(struct xe_sched_job *job)
xe_guc_capture_get_matching_and_lock(struct xe_exec_queue *q)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
struct xe_exec_queue *q;
struct xe_device *xe;
u16 guc_class = GUC_LAST_ENGINE_CLASS + 1;
struct xe_devcoredump_snapshot *ss;
if (!job)
return NULL;
q = job->q;
if (!q || !q->gt)
return NULL;
@ -1874,7 +1869,7 @@ xe_guc_capture_get_matching_and_lock(struct xe_sched_job *job)
if (ss->matched_node && ss->matched_node->source == XE_ENGINE_CAPTURE_SOURCE_GUC)
return ss->matched_node;
/* Find hwe for the job */
/* Find hwe for the queue */
for_each_hw_engine(hwe, q->gt, id) {
if (hwe != q->hwe)
continue;
@ -1906,17 +1901,16 @@ xe_guc_capture_get_matching_and_lock(struct xe_sched_job *job)
}
/**
* xe_engine_snapshot_capture_for_job - Take snapshot of associated engine
* @job: The job object
* xe_engine_snapshot_capture_for_queue - Take snapshot of associated engine
* @q: The exec queue object
*
* Take snapshot of associated HW Engine
*
* Returns: None.
*/
void
xe_engine_snapshot_capture_for_job(struct xe_sched_job *job)
xe_engine_snapshot_capture_for_queue(struct xe_exec_queue *q)
{
struct xe_exec_queue *q = job->q;
struct xe_device *xe = gt_to_xe(q->gt);
struct xe_devcoredump *coredump = &xe->devcoredump;
struct xe_hw_engine *hwe;
@ -1934,11 +1928,12 @@ xe_engine_snapshot_capture_for_job(struct xe_sched_job *job)
}
if (!coredump->snapshot.hwe[id]) {
coredump->snapshot.hwe[id] = xe_hw_engine_snapshot_capture(hwe, job);
coredump->snapshot.hwe[id] =
xe_hw_engine_snapshot_capture(hwe, q);
} else {
struct __guc_capture_parsed_output *new;
new = xe_guc_capture_get_matching_and_lock(job);
new = xe_guc_capture_get_matching_and_lock(q);
if (new) {
struct xe_guc *guc = &q->gt->uc.guc;

View File

@ -11,10 +11,10 @@
#include "xe_guc.h"
#include "xe_guc_fwif.h"
struct xe_exec_queue;
struct xe_guc;
struct xe_hw_engine;
struct xe_hw_engine_snapshot;
struct xe_sched_job;
static inline enum guc_capture_list_class_type xe_guc_class_to_capture_class(u16 class)
{
@ -50,10 +50,10 @@ size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc);
const struct __guc_mmio_reg_descr_group *
xe_guc_capture_get_reg_desc_list(struct xe_gt *gt, u32 owner, u32 type,
enum guc_capture_list_class_type capture_class, bool is_ext);
struct __guc_capture_parsed_output *xe_guc_capture_get_matching_and_lock(struct xe_sched_job *job);
struct __guc_capture_parsed_output *xe_guc_capture_get_matching_and_lock(struct xe_exec_queue *q);
void xe_engine_manual_capture(struct xe_hw_engine *hwe, struct xe_hw_engine_snapshot *snapshot);
void xe_engine_snapshot_print(struct xe_hw_engine_snapshot *snapshot, struct drm_printer *p);
void xe_engine_snapshot_capture_for_job(struct xe_sched_job *job);
void xe_engine_snapshot_capture_for_queue(struct xe_exec_queue *q);
void xe_guc_capture_steered_list_init(struct xe_guc *guc);
void xe_guc_capture_put_matched_nodes(struct xe_guc *guc);
int xe_guc_capture_init(struct xe_guc *guc);

View File

@ -54,6 +54,7 @@ enum {
CT_DEAD_PARSE_G2H_UNKNOWN, /* 0x1000 */
CT_DEAD_PARSE_G2H_ORIGIN, /* 0x2000 */
CT_DEAD_PARSE_G2H_TYPE, /* 0x4000 */
CT_DEAD_CRASH, /* 0x8000 */
};
static void ct_dead_worker_func(struct work_struct *w);
@ -469,8 +470,10 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
* after any existing dead state has been dumped.
*/
spin_lock_irq(&ct->dead.lock);
if (ct->dead.reason)
if (ct->dead.reason) {
ct->dead.reason |= (1 << CT_DEAD_STATE_REARM);
queue_work(system_unbound_wq, &ct->dead.worker);
}
spin_unlock_irq(&ct->dead.lock);
#endif
@ -1017,7 +1020,6 @@ static int guc_ct_send_recv(struct xe_guc_ct *ct, const u32 *action, u32 len,
}
ret = wait_event_timeout(ct->g2h_fence_wq, g2h_fence.done, HZ);
if (!ret) {
LNL_FLUSH_WORK(&ct->g2h_worker);
if (g2h_fence.done) {
@ -1121,6 +1123,24 @@ static int parse_g2h_event(struct xe_guc_ct *ct, u32 *msg, u32 len)
return 0;
}
static int guc_crash_process_msg(struct xe_guc_ct *ct, u32 action)
{
struct xe_gt *gt = ct_to_gt(ct);
if (action == XE_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED)
xe_gt_err(gt, "GuC Crash dump notification\n");
else if (action == XE_GUC_ACTION_NOTIFY_EXCEPTION)
xe_gt_err(gt, "GuC Exception notification\n");
else
xe_gt_err(gt, "Unknown GuC crash notification: 0x%04X\n", action);
CT_DEAD(ct, NULL, CRASH);
kick_reset(ct);
return 0;
}
static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len)
{
struct xe_gt *gt = ct_to_gt(ct);
@ -1295,13 +1315,17 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
case GUC_ACTION_GUC2PF_ADVERSE_EVENT:
ret = xe_gt_sriov_pf_monitor_process_guc2pf(gt, hxg, hxg_len);
break;
case XE_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED:
case XE_GUC_ACTION_NOTIFY_EXCEPTION:
ret = guc_crash_process_msg(ct, action);
break;
default:
xe_gt_err(gt, "unexpected G2H action 0x%04x\n", action);
}
if (ret) {
xe_gt_err(gt, "G2H action 0x%04x failed (%pe)\n",
action, ERR_PTR(ret));
xe_gt_err(gt, "G2H action %#04x failed (%pe) len %u msg %*ph\n",
action, ERR_PTR(ret), hxg_len, (int)sizeof(u32) * hxg_len, hxg);
CT_DEAD(ct, NULL, PROCESS_FAILED);
}

View File

@ -17,6 +17,7 @@
#define G2H_LEN_DW_TLB_INVALIDATE 3
#define GUC_ID_MAX 65535
#define GUC_ID_UNKNOWN 0xffffffff
#define GUC_CONTEXT_DISABLE 0
#define GUC_CONTEXT_ENABLE 1

View File

@ -49,6 +49,8 @@ const char *xe_guc_klv_key_to_string(u16 key)
return "begin_db_id";
case GUC_KLV_VF_CFG_BEGIN_CONTEXT_ID_KEY:
return "begin_ctx_id";
case GUC_KLV_VF_CFG_SCHED_PRIORITY_KEY:
return "sched_priority";
/* VF CFG threshold keys */
#define define_threshold_key_to_string_case(TAG, NAME, ...) \

View File

@ -412,12 +412,11 @@ static const int xe_exec_queue_prio_to_guc[] = {
static void init_policies(struct xe_guc *guc, struct xe_exec_queue *q)
{
struct exec_queue_policy policy;
struct xe_device *xe = guc_to_xe(guc);
enum xe_exec_queue_priority prio = q->sched_props.priority;
u32 timeslice_us = q->sched_props.timeslice_us;
u32 preempt_timeout_us = q->sched_props.preempt_timeout_us;
xe_assert(xe, exec_queue_registered(q));
xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
__guc_exec_queue_policy_start_klv(&policy, q->guc->id);
__guc_exec_queue_policy_add_priority(&policy, xe_exec_queue_prio_to_guc[prio]);
@ -451,12 +450,11 @@ static void __register_mlrc_exec_queue(struct xe_guc *guc,
struct guc_ctxt_registration_info *info)
{
#define MAX_MLRC_REG_SIZE (13 + XE_HW_ENGINE_MAX_INSTANCE * 2)
struct xe_device *xe = guc_to_xe(guc);
u32 action[MAX_MLRC_REG_SIZE];
int len = 0;
int i;
xe_assert(xe, xe_exec_queue_is_parallel(q));
xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_parallel(q));
action[len++] = XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC;
action[len++] = info->flags;
@ -479,7 +477,7 @@ static void __register_mlrc_exec_queue(struct xe_guc *guc,
action[len++] = upper_32_bits(xe_lrc_descriptor(lrc));
}
xe_assert(xe, len <= MAX_MLRC_REG_SIZE);
xe_gt_assert(guc_to_gt(guc), len <= MAX_MLRC_REG_SIZE);
#undef MAX_MLRC_REG_SIZE
xe_guc_ct_send(&guc->ct, action, len, 0, 0);
@ -513,7 +511,7 @@ static void register_exec_queue(struct xe_exec_queue *q)
struct xe_lrc *lrc = q->lrc[0];
struct guc_ctxt_registration_info info;
xe_assert(xe, !exec_queue_registered(q));
xe_gt_assert(guc_to_gt(guc), !exec_queue_registered(q));
memset(&info, 0, sizeof(info));
info.context_idx = q->guc->id;
@ -603,7 +601,7 @@ static int wq_noop_append(struct xe_exec_queue *q)
if (wq_wait_for_space(q, wq_space_until_wrap(q)))
return -ENODEV;
xe_assert(xe, FIELD_FIT(WQ_LEN_MASK, len_dw));
xe_gt_assert(guc_to_gt(guc), FIELD_FIT(WQ_LEN_MASK, len_dw));
parallel_write(xe, map, wq[q->guc->wqi_tail / sizeof(u32)],
FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_NOOP) |
@ -643,13 +641,13 @@ static void wq_item_append(struct xe_exec_queue *q)
wqi[i++] = lrc->ring.tail / sizeof(u64);
}
xe_assert(xe, i == wqi_size / sizeof(u32));
xe_gt_assert(guc_to_gt(guc), i == wqi_size / sizeof(u32));
iosys_map_incr(&map, offsetof(struct guc_submit_parallel_scratch,
wq[q->guc->wqi_tail / sizeof(u32)]));
xe_map_memcpy_to(xe, &map, 0, wqi, wqi_size);
q->guc->wqi_tail += wqi_size;
xe_assert(xe, q->guc->wqi_tail <= WQ_SIZE);
xe_gt_assert(guc_to_gt(guc), q->guc->wqi_tail <= WQ_SIZE);
xe_device_wmb(xe);
@ -661,7 +659,6 @@ static void wq_item_append(struct xe_exec_queue *q)
static void submit_exec_queue(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct xe_lrc *lrc = q->lrc[0];
u32 action[3];
u32 g2h_len = 0;
@ -669,7 +666,7 @@ static void submit_exec_queue(struct xe_exec_queue *q)
int len = 0;
bool extra_submit = false;
xe_assert(xe, exec_queue_registered(q));
xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
if (xe_exec_queue_is_parallel(q))
wq_item_append(q);
@ -716,12 +713,11 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job)
struct xe_sched_job *job = to_xe_sched_job(drm_job);
struct xe_exec_queue *q = job->q;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct dma_fence *fence = NULL;
bool lr = xe_exec_queue_is_lr(q);
xe_assert(xe, !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
exec_queue_banned(q) || exec_queue_suspended(q));
xe_gt_assert(guc_to_gt(guc), !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
exec_queue_banned(q) || exec_queue_suspended(q));
trace_xe_sched_job_run(job);
@ -823,7 +819,7 @@ static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
*/
void xe_guc_submit_wedge(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
struct xe_exec_queue *q;
unsigned long index;
int err;
@ -833,7 +829,8 @@ void xe_guc_submit_wedge(struct xe_guc *guc)
err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev,
guc_submit_wedged_fini, guc);
if (err) {
drm_err(&xe->drm, "Failed to register xe_guc_submit clean-up on wedged.mode=2. Although device is wedged.\n");
xe_gt_err(gt, "Failed to register clean-up on wedged.mode=2; "
"Although device is wedged.\n");
return;
}
@ -865,11 +862,10 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
container_of(w, struct xe_guc_exec_queue, lr_tdr);
struct xe_exec_queue *q = ge->q;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct xe_gpu_scheduler *sched = &ge->sched;
bool wedged;
xe_assert(xe, xe_exec_queue_is_lr(q));
xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_lr(q));
trace_xe_exec_queue_lr_cleanup(q);
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
@ -903,13 +899,19 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
!exec_queue_pending_disable(q) ||
xe_guc_read_stopped(guc), HZ * 5);
if (!ret) {
drm_warn(&xe->drm, "Schedule disable failed to respond");
xe_gt_warn(q->gt, "Schedule disable failed to respond, guc_id=%d\n",
q->guc->id);
xe_devcoredump(q, NULL, "Schedule disable failed to respond, guc_id=%d\n",
q->guc->id);
xe_sched_submission_start(sched);
xe_gt_reset_async(q->gt);
return;
}
}
if (!exec_queue_killed(q) && !xe_lrc_ring_is_idle(q->lrc[0]))
xe_devcoredump(q, NULL, "LR job cleanup, guc_id=%d", q->guc->id);
xe_sched_submission_start(sched);
}
@ -1068,13 +1070,13 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
* do manual capture first and decide later if we need to use it
*/
if (!exec_queue_killed(q) && !xe->devcoredump.captured &&
!xe_guc_capture_get_matching_and_lock(job)) {
!xe_guc_capture_get_matching_and_lock(q)) {
/* take force wake before engine register manual capture */
fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
xe_gt_info(q->gt, "failed to get forcewake for coredump capture\n");
xe_engine_snapshot_capture_for_job(job);
xe_engine_snapshot_capture_for_queue(q);
xe_force_wake_put(gt_to_fw(q->gt), fw_ref);
}
@ -1132,7 +1134,12 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
if (!ret || xe_guc_read_stopped(guc)) {
trigger_reset:
if (!ret)
xe_gt_warn(guc_to_gt(guc), "Schedule disable failed to respond");
xe_gt_warn(guc_to_gt(guc),
"Schedule disable failed to respond, guc_id=%d",
q->guc->id);
xe_devcoredump(q, job,
"Schedule disable failed to respond, guc_id=%d, ret=%d, guc_read=%d",
q->guc->id, ret, xe_guc_read_stopped(guc));
set_exec_queue_extra_ref(q);
xe_exec_queue_get(q); /* GT reset owns this */
set_exec_queue_banned(q);
@ -1162,7 +1169,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
trace_xe_sched_job_timedout(job);
if (!exec_queue_killed(q))
xe_devcoredump(job);
xe_devcoredump(q, job,
"Timedout job - seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx",
xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
q->guc->id, q->flags);
/*
* Kernel jobs should never fail, nor should VM jobs if they do
@ -1277,9 +1287,8 @@ static void __guc_exec_queue_process_msg_cleanup(struct xe_sched_msg *msg)
{
struct xe_exec_queue *q = msg->private_data;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_PERMANENT));
xe_gt_assert(guc_to_gt(guc), !(q->flags & EXEC_QUEUE_FLAG_PERMANENT));
trace_xe_exec_queue_cleanup_entity(q);
if (exec_queue_registered(q))
@ -1315,11 +1324,10 @@ static void __suspend_fence_signal(struct xe_exec_queue *q)
static void suspend_fence_signal(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, exec_queue_suspended(q) || exec_queue_killed(q) ||
xe_guc_read_stopped(guc));
xe_assert(xe, q->guc->suspend_pending);
xe_gt_assert(guc_to_gt(guc), exec_queue_suspended(q) || exec_queue_killed(q) ||
xe_guc_read_stopped(guc));
xe_gt_assert(guc_to_gt(guc), q->guc->suspend_pending);
__suspend_fence_signal(q);
}
@ -1415,12 +1423,11 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct xe_guc_exec_queue *ge;
long timeout;
int err, i;
xe_assert(xe, xe_device_uc_enabled(guc_to_xe(guc)));
xe_gt_assert(guc_to_gt(guc), xe_device_uc_enabled(guc_to_xe(guc)));
ge = kzalloc(sizeof(*ge), GFP_KERNEL);
if (!ge)
@ -1633,9 +1640,8 @@ static void guc_exec_queue_resume(struct xe_exec_queue *q)
struct xe_gpu_scheduler *sched = &q->guc->sched;
struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_RESUME;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, !q->guc->suspend_pending);
xe_gt_assert(guc_to_gt(guc), !q->guc->suspend_pending);
xe_sched_msg_lock(sched);
guc_exec_queue_try_add_msg(q, msg, RESUME);
@ -1708,7 +1714,7 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
ban = true;
}
} else if (xe_exec_queue_is_lr(q) &&
(xe_lrc_ring_head(q->lrc[0]) != xe_lrc_ring_tail(q->lrc[0]))) {
!xe_lrc_ring_is_idle(q->lrc[0])) {
ban = true;
}
@ -1747,9 +1753,8 @@ void xe_guc_submit_stop(struct xe_guc *guc)
{
struct xe_exec_queue *q;
unsigned long index;
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, xe_guc_read_stopped(guc) == 1);
xe_gt_assert(guc_to_gt(guc), xe_guc_read_stopped(guc) == 1);
mutex_lock(&guc->submission_state.lock);
@ -1791,9 +1796,8 @@ int xe_guc_submit_start(struct xe_guc *guc)
{
struct xe_exec_queue *q;
unsigned long index;
struct xe_device *xe = guc_to_xe(guc);
xe_assert(xe, xe_guc_read_stopped(guc) == 1);
xe_gt_assert(guc_to_gt(guc), xe_guc_read_stopped(guc) == 1);
mutex_lock(&guc->submission_state.lock);
atomic_dec(&guc->submission_state.stopped);
@ -1814,22 +1818,22 @@ int xe_guc_submit_start(struct xe_guc *guc)
static struct xe_exec_queue *
g2h_exec_queue_lookup(struct xe_guc *guc, u32 guc_id)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
struct xe_exec_queue *q;
if (unlikely(guc_id >= GUC_ID_MAX)) {
drm_err(&xe->drm, "Invalid guc_id %u", guc_id);
xe_gt_err(gt, "Invalid guc_id %u\n", guc_id);
return NULL;
}
q = xa_load(&guc->submission_state.exec_queue_lookup, guc_id);
if (unlikely(!q)) {
drm_err(&xe->drm, "Not engine present for guc_id %u", guc_id);
xe_gt_err(gt, "Not engine present for guc_id %u\n", guc_id);
return NULL;
}
xe_assert(xe, guc_id >= q->guc->id);
xe_assert(xe, guc_id < (q->guc->id + q->width));
xe_gt_assert(guc_to_gt(guc), guc_id >= q->guc->id);
xe_gt_assert(guc_to_gt(guc), guc_id < (q->guc->id + q->width));
return q;
}
@ -1898,15 +1902,14 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_exec_queue *q;
u32 guc_id = msg[0];
u32 runnable_state = msg[1];
u32 guc_id, runnable_state;
if (unlikely(len < 2)) {
drm_err(&xe->drm, "Invalid length %u", len);
if (unlikely(len < 2))
return -EPROTO;
}
guc_id = msg[0];
runnable_state = msg[1];
q = g2h_exec_queue_lookup(guc, guc_id);
if (unlikely(!q))
@ -1940,14 +1943,13 @@ static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q)
int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_exec_queue *q;
u32 guc_id = msg[0];
u32 guc_id;
if (unlikely(len < 1)) {
drm_err(&xe->drm, "Invalid length %u", len);
if (unlikely(len < 1))
return -EPROTO;
}
guc_id = msg[0];
q = g2h_exec_queue_lookup(guc, guc_id);
if (unlikely(!q))
@ -1969,14 +1971,13 @@ int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
struct xe_gt *gt = guc_to_gt(guc);
struct xe_device *xe = guc_to_xe(guc);
struct xe_exec_queue *q;
u32 guc_id = msg[0];
u32 guc_id;
if (unlikely(len < 1)) {
drm_err(&xe->drm, "Invalid length %u", len);
if (unlikely(len < 1))
return -EPROTO;
}
guc_id = msg[0];
q = g2h_exec_queue_lookup(guc, guc_id);
if (unlikely(!q))
@ -2016,10 +2017,8 @@ int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
u32 status;
if (unlikely(len != XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION_DATA_LEN)) {
xe_gt_dbg(guc_to_gt(guc), "Invalid length %u", len);
if (unlikely(len != XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION_DATA_LEN))
return -EPROTO;
}
status = msg[0] & XE_GUC_STATE_CAPTURE_EVENT_STATUS_MASK;
if (status == XE_GUC_STATE_CAPTURE_EVENT_STATUS_NOSPACE)
@ -2034,13 +2033,21 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
u32 len)
{
struct xe_gt *gt = guc_to_gt(guc);
struct xe_device *xe = guc_to_xe(guc);
struct xe_exec_queue *q;
u32 guc_id = msg[0];
u32 guc_id;
if (unlikely(len < 1)) {
drm_err(&xe->drm, "Invalid length %u", len);
if (unlikely(len < 1))
return -EPROTO;
guc_id = msg[0];
if (guc_id == GUC_ID_UNKNOWN) {
/*
* GuC uses GUC_ID_UNKNOWN if it can not map the CAT fault to any PF/VF
* context. In such case only PF will be notified about that fault.
*/
xe_gt_err_ratelimited(gt, "Memory CAT error reported by GuC!\n");
return 0;
}
q = g2h_exec_queue_lookup(guc, guc_id);
@ -2062,24 +2069,22 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
u8 guc_class, instance;
u32 reason;
if (unlikely(len != 3)) {
drm_err(&xe->drm, "Invalid length %u", len);
if (unlikely(len != 3))
return -EPROTO;
}
guc_class = msg[0];
instance = msg[1];
reason = msg[2];
/* Unexpected failure of a hardware feature, log an actual error */
drm_err(&xe->drm, "GuC engine reset request failed on %d:%d because 0x%08X",
guc_class, instance, reason);
xe_gt_err(gt, "GuC engine reset request failed on %d:%d because 0x%08X",
guc_class, instance, reason);
xe_gt_reset_async(guc_to_gt(guc));
xe_gt_reset_async(gt);
return 0;
}

View File

@ -64,6 +64,15 @@ struct xe_guc {
struct xe_guc_pc pc;
/** @dbm: GuC Doorbell Manager */
struct xe_guc_db_mgr dbm;
/** @g2g: GuC to GuC communication state */
struct {
/** @g2g.bo: Storage for GuC to GuC communication channels */
struct xe_bo *bo;
/** @g2g.owned: Is the BO owned by this GT or just mapped in */
bool owned;
} g2g;
/** @submission_state: GuC submission state */
struct {
/** @submission_state.idm: GuC context ID Manager */
@ -79,6 +88,7 @@ struct xe_guc {
/** @submission_state.fini_wq: submit fini wait queue */
wait_queue_head_t fini_wq;
} submission_state;
/** @hwconfig: Hardware config state */
struct {
/** @hwconfig.bo: buffer object of the hardware config */

View File

@ -92,7 +92,7 @@ void xe_heci_gsc_fini(struct xe_device *xe)
{
struct xe_heci_gsc *heci_gsc = &xe->heci_gsc;
if (!HAS_HECI_GSCFI(xe) && !HAS_HECI_CSCFI(xe))
if (!xe->info.has_heci_gscfi && !xe->info.has_heci_cscfi)
return;
if (heci_gsc->adev) {
@ -177,7 +177,7 @@ void xe_heci_gsc_init(struct xe_device *xe)
const struct heci_gsc_def *def;
int ret;
if (!HAS_HECI_GSCFI(xe) && !HAS_HECI_CSCFI(xe))
if (!xe->info.has_heci_gscfi && !xe->info.has_heci_cscfi)
return;
heci_gsc->irq = -1;
@ -222,7 +222,7 @@ void xe_heci_gsc_irq_handler(struct xe_device *xe, u32 iir)
if ((iir & GSC_IRQ_INTF(1)) == 0)
return;
if (!HAS_HECI_GSCFI(xe)) {
if (!xe->info.has_heci_gscfi) {
drm_warn_once(&xe->drm, "GSC irq: not supported");
return;
}
@ -242,7 +242,7 @@ void xe_heci_csc_irq_handler(struct xe_device *xe, u32 iir)
if ((iir & CSC_IRQ_INTF(1)) == 0)
return;
if (!HAS_HECI_CSCFI(xe)) {
if (!xe->info.has_heci_cscfi) {
drm_warn_once(&xe->drm, "CSC irq: not supported");
return;
}

View File

@ -574,7 +574,6 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe,
xe_gt_assert(gt, gt->info.engine_mask & BIT(id));
xe_reg_sr_apply_mmio(&hwe->reg_sr, gt);
xe_reg_sr_apply_whitelist(hwe);
hwe->hwsp = xe_managed_bo_create_pin_map(xe, tile, SZ_4K,
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
@ -829,7 +828,7 @@ void xe_hw_engine_handle_irq(struct xe_hw_engine *hwe, u16 intr_vec)
/**
* xe_hw_engine_snapshot_capture - Take a quick snapshot of the HW Engine.
* @hwe: Xe HW Engine.
* @job: The job object.
* @q: The exec queue object.
*
* This can be printed out in a later stage like during dev_coredump
* analysis.
@ -838,7 +837,7 @@ void xe_hw_engine_handle_irq(struct xe_hw_engine *hwe, u16 intr_vec)
* caller, using `xe_hw_engine_snapshot_free`.
*/
struct xe_hw_engine_snapshot *
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe, struct xe_sched_job *job)
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe, struct xe_exec_queue *q)
{
struct xe_hw_engine_snapshot *snapshot;
struct __guc_capture_parsed_output *node;
@ -864,15 +863,14 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe, struct xe_sched_job *job
if (IS_SRIOV_VF(gt_to_xe(hwe->gt)))
return snapshot;
if (job) {
if (q) {
/* If got guc capture, set source to GuC */
node = xe_guc_capture_get_matching_and_lock(job);
node = xe_guc_capture_get_matching_and_lock(q);
if (node) {
struct xe_device *xe = gt_to_xe(hwe->gt);
struct xe_devcoredump *coredump = &xe->devcoredump;
coredump->snapshot.matched_node = node;
snapshot->source = XE_ENGINE_CAPTURE_SOURCE_GUC;
xe_gt_dbg(hwe->gt, "Found and locked GuC-err-capture node");
return snapshot;
}
@ -880,7 +878,6 @@ xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe, struct xe_sched_job *job
/* otherwise, do manual capture */
xe_engine_manual_capture(hwe, snapshot);
snapshot->source = XE_ENGINE_CAPTURE_SOURCE_MANUAL;
xe_gt_dbg(hwe->gt, "Proceeding with manual engine snapshot");
return snapshot;

View File

@ -11,7 +11,7 @@
struct drm_printer;
struct drm_xe_engine_class_instance;
struct xe_device;
struct xe_sched_job;
struct xe_exec_queue;
#ifdef CONFIG_DRM_XE_JOB_TIMEOUT_MIN
#define XE_HW_ENGINE_JOB_TIMEOUT_MIN CONFIG_DRM_XE_JOB_TIMEOUT_MIN
@ -56,7 +56,7 @@ void xe_hw_engine_enable_ring(struct xe_hw_engine *hwe);
u32 xe_hw_engine_mask_per_class(struct xe_gt *gt,
enum xe_engine_class engine_class);
struct xe_hw_engine_snapshot *
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe, struct xe_sched_job *job);
xe_hw_engine_snapshot_capture(struct xe_hw_engine *hwe, struct xe_exec_queue *q);
void xe_hw_engine_snapshot_free(struct xe_hw_engine_snapshot *snapshot);
void xe_hw_engine_print(struct xe_hw_engine *hwe, struct drm_printer *p);
void xe_hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe);

View File

@ -165,8 +165,6 @@ enum xe_hw_engine_snapshot_source_id {
struct xe_hw_engine_snapshot {
/** @name: name of the hw engine */
char *name;
/** @source: Data source, either manual or GuC */
enum xe_hw_engine_snapshot_source_id source;
/** @hwe: hw engine */
struct xe_hw_engine *hwe;
/** @logical_instance: logical instance of this hw engine */

View File

@ -192,7 +192,7 @@ void xe_irq_enable_hwe(struct xe_gt *gt)
if (xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER)) {
gsc_mask = irqs | GSC_ER_COMPLETE;
heci_mask = GSC_IRQ_INTF(1);
} else if (HAS_HECI_GSCFI(xe)) {
} else if (xe->info.has_heci_gscfi) {
gsc_mask = GSC_IRQ_INTF(1);
}
@ -325,7 +325,7 @@ static void gt_irq_handler(struct xe_tile *tile,
if (class == XE_ENGINE_CLASS_OTHER) {
/* HECI GSCFI interrupts come from outside of GT */
if (HAS_HECI_GSCFI(xe) && instance == OTHER_GSC_INSTANCE)
if (xe->info.has_heci_gscfi && instance == OTHER_GSC_INSTANCE)
xe_heci_gsc_irq_handler(xe, intr_vec);
else
gt_other_irq_handler(engine_gt, instance, intr_vec);
@ -348,12 +348,8 @@ static irqreturn_t xelp_irq_handler(int irq, void *arg)
unsigned long intr_dw[2];
u32 identity[32];
spin_lock(&xe->irq.lock);
if (!xe->irq.enabled) {
spin_unlock(&xe->irq.lock);
if (!atomic_read(&xe->irq.enabled))
return IRQ_NONE;
}
spin_unlock(&xe->irq.lock);
master_ctl = xelp_intr_disable(xe);
if (!master_ctl) {
@ -417,12 +413,8 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
/* TODO: This really shouldn't be copied+pasted */
spin_lock(&xe->irq.lock);
if (!xe->irq.enabled) {
spin_unlock(&xe->irq.lock);
if (!atomic_read(&xe->irq.enabled))
return IRQ_NONE;
}
spin_unlock(&xe->irq.lock);
master_tile_ctl = dg1_intr_disable(xe);
if (!master_tile_ctl) {
@ -459,7 +451,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
* the primary tile.
*/
if (id == 0) {
if (HAS_HECI_CSCFI(xe))
if (xe->info.has_heci_cscfi)
xe_heci_csc_irq_handler(xe, master_ctl);
xe_display_irq_handler(xe, master_ctl);
gu_misc_iir = gu_misc_irq_ack(xe, master_ctl);
@ -508,7 +500,7 @@ static void gt_irq_reset(struct xe_tile *tile)
if ((tile->media_gt &&
xe_hw_engine_mask_per_class(tile->media_gt, XE_ENGINE_CLASS_OTHER)) ||
HAS_HECI_GSCFI(tile_to_xe(tile))) {
tile_to_xe(tile)->info.has_heci_gscfi) {
xe_mmio_write32(mmio, GUNIT_GSC_INTR_ENABLE, 0);
xe_mmio_write32(mmio, GUNIT_GSC_INTR_MASK, ~0);
xe_mmio_write32(mmio, HECI2_RSVD_INTR_MASK, ~0);
@ -644,12 +636,8 @@ static irqreturn_t vf_mem_irq_handler(int irq, void *arg)
struct xe_tile *tile;
unsigned int id;
spin_lock(&xe->irq.lock);
if (!xe->irq.enabled) {
spin_unlock(&xe->irq.lock);
if (!atomic_read(&xe->irq.enabled))
return IRQ_NONE;
}
spin_unlock(&xe->irq.lock);
for_each_tile(tile, xe, id)
xe_memirq_handler(&tile->memirq);
@ -674,10 +662,9 @@ static void irq_uninstall(void *arg)
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
int irq;
if (!xe->irq.enabled)
if (!atomic_xchg(&xe->irq.enabled, 0))
return;
xe->irq.enabled = false;
xe_irq_reset(xe);
irq = pci_irq_vector(pdev, 0);
@ -724,7 +711,7 @@ int xe_irq_install(struct xe_device *xe)
return err;
}
xe->irq.enabled = true;
atomic_set(&xe->irq.enabled, 1);
xe_irq_postinstall(xe);
@ -744,9 +731,7 @@ void xe_irq_suspend(struct xe_device *xe)
{
int irq = to_pci_dev(xe->drm.dev)->irq;
spin_lock_irq(&xe->irq.lock);
xe->irq.enabled = false; /* no new irqs */
spin_unlock_irq(&xe->irq.lock);
atomic_set(&xe->irq.enabled, 0); /* no new irqs */
synchronize_irq(irq); /* flush irqs */
xe_irq_reset(xe); /* turn irqs off */
@ -762,7 +747,7 @@ void xe_irq_resume(struct xe_device *xe)
* 1. no irq will arrive before the postinstall
* 2. display is not yet resumed
*/
xe->irq.enabled = true;
atomic_set(&xe->irq.enabled, 1);
xe_irq_reset(xe);
xe_irq_postinstall(xe); /* turn irqs on */

View File

@ -25,6 +25,7 @@
#include "xe_map.h"
#include "xe_memirq.h"
#include "xe_sriov.h"
#include "xe_trace_lrc.h"
#include "xe_vm.h"
#include "xe_wa.h"
@ -1060,6 +1061,14 @@ u32 xe_lrc_ring_tail(struct xe_lrc *lrc)
return xe_lrc_read_ctx_reg(lrc, CTX_RING_TAIL) & TAIL_ADDR;
}
static u32 xe_lrc_ring_start(struct xe_lrc *lrc)
{
if (xe_lrc_has_indirect_ring_state(lrc))
return xe_lrc_read_indirect_ctx_reg(lrc, INDIRECT_CTX_RING_START);
else
return xe_lrc_read_ctx_reg(lrc, CTX_RING_START);
}
void xe_lrc_set_ring_head(struct xe_lrc *lrc, u32 head)
{
if (xe_lrc_has_indirect_ring_state(lrc))
@ -1635,10 +1644,12 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
xe_vm_get(lrc->bo->vm);
snapshot->context_desc = xe_lrc_ggtt_addr(lrc);
snapshot->ring_addr = __xe_lrc_ring_ggtt_addr(lrc);
snapshot->indirect_context_desc = xe_lrc_indirect_ring_ggtt_addr(lrc);
snapshot->head = xe_lrc_ring_head(lrc);
snapshot->tail.internal = lrc->ring.tail;
snapshot->tail.memory = xe_lrc_ring_tail(lrc);
snapshot->start = xe_lrc_ring_start(lrc);
snapshot->start_seqno = xe_lrc_start_seqno(lrc);
snapshot->seqno = xe_lrc_seqno(lrc);
snapshot->lrc_bo = xe_bo_get(lrc->bo);
@ -1692,11 +1703,14 @@ void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer
return;
drm_printf(p, "\tHW Context Desc: 0x%08x\n", snapshot->context_desc);
drm_printf(p, "\tHW Ring address: 0x%08x\n",
snapshot->ring_addr);
drm_printf(p, "\tHW Indirect Ring State: 0x%08x\n",
snapshot->indirect_context_desc);
drm_printf(p, "\tLRC Head: (memory) %u\n", snapshot->head);
drm_printf(p, "\tLRC Tail: (internal) %u, (memory) %u\n",
snapshot->tail.internal, snapshot->tail.memory);
drm_printf(p, "\tRing start: (memory) 0x%08x\n", snapshot->start);
drm_printf(p, "\tStart seqno: (memory) %d\n", snapshot->start_seqno);
drm_printf(p, "\tSeqno: (memory) %d\n", snapshot->seqno);
drm_printf(p, "\tTimestamp: 0x%08x\n", snapshot->ctx_timestamp);
@ -1758,5 +1772,20 @@ u32 xe_lrc_update_timestamp(struct xe_lrc *lrc, u32 *old_ts)
lrc->ctx_timestamp = xe_lrc_ctx_timestamp(lrc);
trace_xe_lrc_update_timestamp(lrc, *old_ts);
return lrc->ctx_timestamp;
}
/**
* xe_lrc_ring_is_idle() - LRC is idle
* @lrc: Pointer to the lrc.
*
* Compare LRC ring head and tail to determine if idle.
*
* Return: True is ring is idle, False otherwise
*/
bool xe_lrc_ring_is_idle(struct xe_lrc *lrc)
{
return xe_lrc_ring_head(lrc) == xe_lrc_ring_tail(lrc);
}

View File

@ -25,8 +25,10 @@ struct xe_lrc_snapshot {
unsigned long lrc_size, lrc_offset;
u32 context_desc;
u32 ring_addr;
u32 indirect_context_desc;
u32 head;
u32 start;
struct {
u32 internal;
u32 memory;
@ -78,6 +80,8 @@ u32 xe_lrc_ring_head(struct xe_lrc *lrc);
u32 xe_lrc_ring_space(struct xe_lrc *lrc);
void xe_lrc_write_ring(struct xe_lrc *lrc, const void *data, size_t size);
bool xe_lrc_ring_is_idle(struct xe_lrc *lrc);
u32 xe_lrc_indirect_ring_ggtt_addr(struct xe_lrc *lrc);
u32 xe_lrc_ggtt_addr(struct xe_lrc *lrc);
u32 *xe_lrc_regs(struct xe_lrc *lrc);

View File

@ -10,9 +10,13 @@
#define XE_WARN_ON WARN_ON
#define XE_IOCTL_DBG(xe, cond) \
((cond) && (drm_dbg(&(xe)->drm, \
"Ioctl argument check failed at %s:%d: %s", \
__FILE__, __LINE__, #cond), 1))
#define XE_IOCTL_DBG(xe, cond) ({ \
int cond__ = !!(cond); \
if (cond__) \
drm_dbg(&(xe)->drm, \
"Ioctl argument check failed at %s:%d: %s", \
__FILE__, __LINE__, #cond); \
cond__; \
})
#endif

View File

@ -155,13 +155,6 @@ static const char *guc_name(struct xe_guc *guc)
*
*/
static void __release_xe_bo(struct drm_device *drm, void *arg)
{
struct xe_bo *bo = arg;
xe_bo_unpin_map_no_vm(bo);
}
static inline bool hw_reports_to_instance_zero(struct xe_memirq *memirq)
{
/*
@ -184,14 +177,12 @@ static int memirq_alloc_pages(struct xe_memirq *memirq)
BUILD_BUG_ON(!IS_ALIGNED(XE_MEMIRQ_SOURCE_OFFSET(0), SZ_64));
BUILD_BUG_ON(!IS_ALIGNED(XE_MEMIRQ_STATUS_OFFSET(0), SZ_4K));
/* XXX: convert to managed bo */
bo = xe_bo_create_pin_map(xe, tile, NULL, bo_size,
ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
XE_BO_FLAG_NEEDS_UC |
XE_BO_FLAG_NEEDS_CPU_ACCESS);
bo = xe_managed_bo_create_pin_map(xe, tile, bo_size,
XE_BO_FLAG_SYSTEM |
XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE |
XE_BO_FLAG_NEEDS_UC |
XE_BO_FLAG_NEEDS_CPU_ACCESS);
if (IS_ERR(bo)) {
err = PTR_ERR(bo);
goto out;
@ -215,7 +206,7 @@ static int memirq_alloc_pages(struct xe_memirq *memirq)
xe_bo_ggtt_addr(bo), bo_size, XE_MEMIRQ_SOURCE_OFFSET(0),
XE_MEMIRQ_STATUS_OFFSET(0));
return drmm_add_action_or_reset(&xe->drm, __release_xe_bo, memirq->bo);
return 0;
out:
memirq_err(memirq, "Failed to allocate memirq page (%pe)\n", ERR_PTR(err));
@ -442,6 +433,9 @@ static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *stat
if (memirq_received(memirq, status, ilog2(GUC_INTR_GUC2HOST), name))
xe_guc_irq_handler(guc, GUC_INTR_GUC2HOST);
if (memirq_received(memirq, status, ilog2(GUC_INTR_SW_INT_0), name))
xe_guc_irq_handler(guc, GUC_INTR_SW_INT_0);
}
/**

View File

@ -19,7 +19,7 @@
struct xe_modparam xe_modparam = {
.probe_display = true,
.guc_log_level = 5,
.guc_log_level = 3,
.force_probe = CONFIG_DRM_XE_FORCE_PROBE,
.wedged_mode = 1,
/* the rest are 0 by default */

View File

@ -96,6 +96,7 @@ struct xe_oa_open_param {
struct drm_xe_sync __user *syncs_user;
int num_syncs;
struct xe_sync_entry *syncs;
size_t oa_buffer_size;
};
struct xe_oa_config_bo {
@ -403,11 +404,19 @@ static int xe_oa_append_reports(struct xe_oa_stream *stream, char __user *buf,
static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
{
struct xe_mmio *mmio = &stream->gt->mmio;
u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
u32 oa_buf = gtt_offset | OABUFFER_SIZE_16M | OAG_OABUFFER_MEMORY_SELECT;
int size_exponent = __ffs(stream->oa_buffer.bo->size);
u32 oa_buf = gtt_offset | OAG_OABUFFER_MEMORY_SELECT;
struct xe_mmio *mmio = &stream->gt->mmio;
unsigned long flags;
/*
* If oa buffer size is more than 16MB (exponent greater than 24), the
* oa buffer size field is multiplied by 8 in xe_oa_enable_metric_set.
*/
oa_buf |= REG_FIELD_PREP(OABUFFER_SIZE_MASK,
size_exponent > 24 ? size_exponent - 20 : size_exponent - 17);
spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags);
xe_mmio_write32(mmio, __oa_regs(stream)->oa_status, 0);
@ -901,15 +910,12 @@ static void xe_oa_stream_destroy(struct xe_oa_stream *stream)
xe_file_put(stream->xef);
}
static int xe_oa_alloc_oa_buffer(struct xe_oa_stream *stream)
static int xe_oa_alloc_oa_buffer(struct xe_oa_stream *stream, size_t size)
{
struct xe_bo *bo;
BUILD_BUG_ON_NOT_POWER_OF_2(XE_OA_BUFFER_SIZE);
BUILD_BUG_ON(XE_OA_BUFFER_SIZE < SZ_128K || XE_OA_BUFFER_SIZE > SZ_16M);
bo = xe_bo_create_pin_map(stream->oa->xe, stream->gt->tile, NULL,
XE_OA_BUFFER_SIZE, ttm_bo_type_kernel,
size, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_GGTT);
if (IS_ERR(bo))
return PTR_ERR(bo);
@ -1087,6 +1093,13 @@ static u32 oag_report_ctx_switches(const struct xe_oa_stream *stream)
0 : OAG_OA_DEBUG_DISABLE_CTX_SWITCH_REPORTS);
}
static u32 oag_buf_size_select(const struct xe_oa_stream *stream)
{
return _MASKED_FIELD(OAG_OA_DEBUG_BUF_SIZE_SELECT,
stream->oa_buffer.bo->size > SZ_16M ?
OAG_OA_DEBUG_BUF_SIZE_SELECT : 0);
}
static int xe_oa_enable_metric_set(struct xe_oa_stream *stream)
{
struct xe_mmio *mmio = &stream->gt->mmio;
@ -1119,6 +1132,7 @@ static int xe_oa_enable_metric_set(struct xe_oa_stream *stream)
xe_mmio_write32(mmio, __oa_regs(stream)->oa_debug,
_MASKED_BIT_ENABLE(oa_debug) |
oag_report_ctx_switches(stream) |
oag_buf_size_select(stream) |
oag_configure_mmio_trigger(stream, true));
xe_mmio_write32(mmio, __oa_regs(stream)->oa_ctx_ctrl, stream->periodic ?
@ -1260,6 +1274,17 @@ static int xe_oa_set_prop_syncs_user(struct xe_oa *oa, u64 value,
return 0;
}
static int xe_oa_set_prop_oa_buffer_size(struct xe_oa *oa, u64 value,
struct xe_oa_open_param *param)
{
if (!is_power_of_2(value) || value < SZ_128K || value > SZ_128M) {
drm_dbg(&oa->xe->drm, "OA buffer size invalid %llu\n", value);
return -EINVAL;
}
param->oa_buffer_size = value;
return 0;
}
static int xe_oa_set_prop_ret_inval(struct xe_oa *oa, u64 value,
struct xe_oa_open_param *param)
{
@ -1280,6 +1305,7 @@ static const xe_oa_set_property_fn xe_oa_set_property_funcs_open[] = {
[DRM_XE_OA_PROPERTY_NO_PREEMPT] = xe_oa_set_no_preempt,
[DRM_XE_OA_PROPERTY_NUM_SYNCS] = xe_oa_set_prop_num_syncs,
[DRM_XE_OA_PROPERTY_SYNCS] = xe_oa_set_prop_syncs_user,
[DRM_XE_OA_PROPERTY_OA_BUFFER_SIZE] = xe_oa_set_prop_oa_buffer_size,
};
static const xe_oa_set_property_fn xe_oa_set_property_funcs_config[] = {
@ -1294,6 +1320,7 @@ static const xe_oa_set_property_fn xe_oa_set_property_funcs_config[] = {
[DRM_XE_OA_PROPERTY_NO_PREEMPT] = xe_oa_set_prop_ret_inval,
[DRM_XE_OA_PROPERTY_NUM_SYNCS] = xe_oa_set_prop_num_syncs,
[DRM_XE_OA_PROPERTY_SYNCS] = xe_oa_set_prop_syncs_user,
[DRM_XE_OA_PROPERTY_OA_BUFFER_SIZE] = xe_oa_set_prop_ret_inval,
};
static int xe_oa_user_ext_set_property(struct xe_oa *oa, enum xe_oa_user_extn_from from,
@ -1553,7 +1580,7 @@ static long xe_oa_status_locked(struct xe_oa_stream *stream, unsigned long arg)
static long xe_oa_info_locked(struct xe_oa_stream *stream, unsigned long arg)
{
struct drm_xe_oa_stream_info info = { .oa_buf_size = XE_OA_BUFFER_SIZE, };
struct drm_xe_oa_stream_info info = { .oa_buf_size = stream->oa_buffer.bo->size, };
void __user *uaddr = (void __user *)arg;
if (copy_to_user(uaddr, &info, sizeof(info)))
@ -1639,7 +1666,7 @@ static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
}
/* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */
if (vma->vm_end - vma->vm_start != XE_OA_BUFFER_SIZE) {
if (vma->vm_end - vma->vm_start != stream->oa_buffer.bo->size) {
drm_dbg(&stream->oa->xe->drm, "Wrong mmap size, must be OA buffer size\n");
return -EINVAL;
}
@ -1783,9 +1810,10 @@ static int xe_oa_stream_init(struct xe_oa_stream *stream,
if (GRAPHICS_VER(stream->oa->xe) >= 20 &&
stream->hwe->oa_unit->type == DRM_XE_OA_UNIT_TYPE_OAG && stream->sample)
stream->oa_buffer.circ_size =
XE_OA_BUFFER_SIZE - XE_OA_BUFFER_SIZE % stream->oa_buffer.format->size;
param->oa_buffer_size -
param->oa_buffer_size % stream->oa_buffer.format->size;
else
stream->oa_buffer.circ_size = XE_OA_BUFFER_SIZE;
stream->oa_buffer.circ_size = param->oa_buffer_size;
if (stream->exec_q && engine_supports_mi_query(stream->hwe)) {
/* If we don't find the context offset, just return error */
@ -1828,7 +1856,7 @@ static int xe_oa_stream_init(struct xe_oa_stream *stream,
goto err_fw_put;
}
ret = xe_oa_alloc_oa_buffer(stream);
ret = xe_oa_alloc_oa_buffer(stream, param->oa_buffer_size);
if (ret)
goto err_fw_put;
@ -2125,6 +2153,9 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
drm_dbg(&oa->xe->drm, "Using periodic sampling freq %lld Hz\n", oa_freq_hz);
}
if (!param.oa_buffer_size)
param.oa_buffer_size = DEFAULT_XE_OA_BUFFER_SIZE;
ret = xe_oa_parse_syncs(oa, &param);
if (ret)
goto err_exec_q;

View File

@ -15,7 +15,7 @@
#include "regs/xe_reg_defs.h"
#include "xe_hw_engine_types.h"
#define XE_OA_BUFFER_SIZE SZ_16M
#define DEFAULT_XE_OA_BUFFER_SIZE SZ_16M
enum xe_oa_report_header {
HDR_32_BIT = 0,

View File

@ -738,9 +738,6 @@ void xe_pm_d3cold_allowed_toggle(struct xe_device *xe)
xe->d3cold.allowed = false;
mutex_unlock(&xe->d3cold.lock);
drm_dbg(&xe->drm,
"d3cold: allowed=%s\n", str_yes_no(xe->d3cold.allowed));
}
/**

View File

@ -136,6 +136,7 @@ struct xe_pt *xe_pt_create(struct xe_vm *vm, struct xe_tile *tile,
xe_pt_free(pt);
return ERR_PTR(err);
}
ALLOW_ERROR_INJECTION(xe_pt_create, ERRNO);
/**
* xe_pt_populate_empty() - Populate a page-table bo with scratch- or zero
@ -1333,8 +1334,7 @@ static void invalidation_fence_cb(struct dma_fence *fence,
queue_work(system_wq, &ifence->work);
} else {
ifence->base.base.error = ifence->fence->error;
dma_fence_signal(&ifence->base.base);
dma_fence_put(&ifence->base.base);
xe_gt_tlb_invalidation_fence_signal(&ifence->base);
}
dma_fence_put(ifence->fence);
}
@ -1851,6 +1851,7 @@ int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops)
return 0;
}
ALLOW_ERROR_INJECTION(xe_pt_update_ops_prepare, ERRNO);
static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
struct xe_vm_pgtable_update_ops *pt_update_ops,
@ -2131,6 +2132,7 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
return ERR_PTR(err);
}
ALLOW_ERROR_INJECTION(xe_pt_update_ops_run, ERRNO);
/**
* xe_pt_update_ops_fini() - Finish PT update operations

View File

@ -23,6 +23,7 @@
#include "xe_guc_hwconfig.h"
#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_oa.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_wa.h"
@ -670,7 +671,8 @@ static int query_oa_units(struct xe_device *xe,
du->oa_unit_id = u->oa_unit_id;
du->oa_unit_type = u->type;
du->oa_timestamp_freq = xe_oa_timestamp_frequency(gt);
du->capabilities = DRM_XE_OA_CAPS_BASE | DRM_XE_OA_CAPS_SYNCS;
du->capabilities = DRM_XE_OA_CAPS_BASE | DRM_XE_OA_CAPS_SYNCS |
DRM_XE_OA_CAPS_OA_BUFFER_SIZE;
j = 0;
for_each_hw_engine(hwe, gt, hwe_id) {

View File

@ -24,49 +24,29 @@
#include "xe_hw_engine_types.h"
#include "xe_macros.h"
#include "xe_mmio.h"
#include "xe_reg_whitelist.h"
#include "xe_rtp_types.h"
#define XE_REG_SR_GROW_STEP_DEFAULT 16
static void reg_sr_fini(struct drm_device *drm, void *arg)
{
struct xe_reg_sr *sr = arg;
struct xe_reg_sr_entry *entry;
unsigned long reg;
xa_for_each(&sr->xa, reg, entry)
kfree(entry);
xa_destroy(&sr->xa);
kfree(sr->pool.arr);
memset(&sr->pool, 0, sizeof(sr->pool));
}
int xe_reg_sr_init(struct xe_reg_sr *sr, const char *name, struct xe_device *xe)
{
xa_init(&sr->xa);
memset(&sr->pool, 0, sizeof(sr->pool));
sr->pool.grow_step = XE_REG_SR_GROW_STEP_DEFAULT;
sr->name = name;
return drmm_add_action_or_reset(&xe->drm, reg_sr_fini, sr);
}
EXPORT_SYMBOL_IF_KUNIT(xe_reg_sr_init);
static struct xe_reg_sr_entry *alloc_entry(struct xe_reg_sr *sr)
{
if (sr->pool.used == sr->pool.allocated) {
struct xe_reg_sr_entry *arr;
arr = krealloc_array(sr->pool.arr,
ALIGN(sr->pool.allocated + 1, sr->pool.grow_step),
sizeof(*arr), GFP_KERNEL);
if (!arr)
return NULL;
sr->pool.arr = arr;
sr->pool.allocated += sr->pool.grow_step;
}
return &sr->pool.arr[sr->pool.used++];
}
static bool compatible_entries(const struct xe_reg_sr_entry *e1,
const struct xe_reg_sr_entry *e2)
{
@ -112,7 +92,7 @@ int xe_reg_sr_add(struct xe_reg_sr *sr,
return 0;
}
pentry = alloc_entry(sr);
pentry = kmalloc(sizeof(*pentry), GFP_KERNEL);
if (!pentry) {
ret = -ENOMEM;
goto fail;
@ -211,58 +191,6 @@ void xe_reg_sr_apply_mmio(struct xe_reg_sr *sr, struct xe_gt *gt)
xe_gt_err(gt, "Failed to apply, err=-ETIMEDOUT\n");
}
void xe_reg_sr_apply_whitelist(struct xe_hw_engine *hwe)
{
struct xe_reg_sr *sr = &hwe->reg_whitelist;
struct xe_gt *gt = hwe->gt;
struct xe_device *xe = gt_to_xe(gt);
struct xe_reg_sr_entry *entry;
struct drm_printer p;
u32 mmio_base = hwe->mmio_base;
unsigned long reg;
unsigned int slot = 0;
unsigned int fw_ref;
if (xa_empty(&sr->xa))
return;
drm_dbg(&xe->drm, "Whitelisting %s registers\n", sr->name);
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
goto err_force_wake;
p = drm_dbg_printer(&xe->drm, DRM_UT_DRIVER, NULL);
xa_for_each(&sr->xa, reg, entry) {
if (slot == RING_MAX_NONPRIV_SLOTS) {
xe_gt_err(gt,
"hwe %s: maximum register whitelist slots (%d) reached, refusing to add more\n",
hwe->name, RING_MAX_NONPRIV_SLOTS);
break;
}
xe_reg_whitelist_print_entry(&p, 0, reg, entry);
xe_mmio_write32(&gt->mmio, RING_FORCE_TO_NONPRIV(mmio_base, slot),
reg | entry->set_bits);
slot++;
}
/* And clear the rest just in case of garbage */
for (; slot < RING_MAX_NONPRIV_SLOTS; slot++) {
u32 addr = RING_NOPID(mmio_base).addr;
xe_mmio_write32(&gt->mmio, RING_FORCE_TO_NONPRIV(mmio_base, slot), addr);
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
return;
err_force_wake:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
drm_err(&xe->drm, "Failed to apply, err=-ETIMEDOUT\n");
}
/**
* xe_reg_sr_dump - print all save/restore entries
* @sr: Save/restore entries

View File

@ -20,12 +20,6 @@ struct xe_reg_sr_entry {
};
struct xe_reg_sr {
struct {
struct xe_reg_sr_entry *arr;
unsigned int used;
unsigned int allocated;
unsigned int grow_step;
} pool;
struct xarray xa;
const char *name;

View File

@ -10,7 +10,9 @@
#include "regs/xe_oa_regs.h"
#include "regs/xe_regs.h"
#include "xe_gt_types.h"
#include "xe_gt_printk.h"
#include "xe_platform_types.h"
#include "xe_reg_sr.h"
#include "xe_rtp.h"
#include "xe_step.h"
@ -89,6 +91,40 @@ static const struct xe_rtp_entry_sr register_whitelist[] = {
{}
};
static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe)
{
struct xe_reg_sr *sr = &hwe->reg_whitelist;
struct xe_reg_sr_entry *entry;
struct drm_printer p;
unsigned long reg;
unsigned int slot;
xe_gt_dbg(hwe->gt, "Add %s whitelist to engine\n", sr->name);
p = xe_gt_dbg_printer(hwe->gt);
slot = 0;
xa_for_each(&sr->xa, reg, entry) {
struct xe_reg_sr_entry hwe_entry = {
.reg = RING_FORCE_TO_NONPRIV(hwe->mmio_base, slot),
.set_bits = entry->reg.addr | entry->set_bits,
.clr_bits = ~0u,
.read_mask = entry->read_mask,
};
if (slot == RING_MAX_NONPRIV_SLOTS) {
xe_gt_err(hwe->gt,
"hwe %s: maximum register whitelist slots (%d) reached, refusing to add more\n",
hwe->name, RING_MAX_NONPRIV_SLOTS);
break;
}
xe_reg_whitelist_print_entry(&p, 0, reg, entry);
xe_reg_sr_add(&hwe->reg_sr, &hwe_entry, hwe->gt);
slot++;
}
}
/**
* xe_reg_whitelist_process_engine - process table of registers to whitelist
* @hwe: engine instance to process whitelist for
@ -102,6 +138,7 @@ void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe)
struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe);
xe_rtp_process_to_sr(&ctx, register_whitelist, &hwe->reg_whitelist);
whitelist_apply_to_hwe(hwe);
}
/**

View File

@ -14,6 +14,7 @@
#include "xe_mmio.h"
#include "xe_sriov.h"
#include "xe_sriov_pf.h"
#include "xe_sriov_vf.h"
/**
* xe_sriov_mode_to_string - Convert enum value to string.
@ -114,6 +115,9 @@ int xe_sriov_init(struct xe_device *xe)
return err;
}
if (IS_SRIOV_VF(xe))
xe_sriov_vf_init_early(xe);
xe_assert(xe, !xe->sriov.wq);
xe->sriov.wq = alloc_workqueue("xe-sriov-wq", 0, 0);
if (!xe->sriov.wq)

View File

@ -20,7 +20,7 @@
* is within a range of supported VF numbers (up to maximum number of VFs that
* driver can support, including VF0 that represents the PF itself).
*
* Note: Effective only on debug builds. See `Xe ASSERTs`_ for more information.
* Note: Effective only on debug builds. See `Xe Asserts`_ for more information.
*/
#define xe_sriov_pf_assert_vfid(xe, vfid) \
xe_assert((xe), (vfid) <= xe_sriov_pf_get_totalvfs(xe))

View File

@ -9,6 +9,7 @@
#include <linux/build_bug.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/workqueue_types.h>
/**
* VFID - Virtual Function Identifier
@ -56,4 +57,20 @@ struct xe_device_pf {
struct mutex master_lock;
};
/**
* struct xe_device_vf - Xe Virtual Function related data
*
* The data in this structure is valid only if driver is running in the
* @XE_SRIOV_MODE_VF mode.
*/
struct xe_device_vf {
/** @migration: VF Migration state data */
struct {
/** @migration.worker: VF migration recovery worker */
struct work_struct worker;
/** @migration.gt_flags: Per-GT request flags for VF migration recovery */
unsigned long gt_flags;
} migration;
};
#endif

View File

@ -0,0 +1,263 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2023-2024 Intel Corporation
*/
#include <drm/drm_managed.h>
#include "xe_assert.h"
#include "xe_device.h"
#include "xe_gt_sriov_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_sriov_printk.h"
#include "xe_sriov_vf.h"
/**
* DOC: VF restore procedure in PF KMD and VF KMD
*
* Restoring previously saved state of a VF is one of core features of
* SR-IOV. All major VM Management applications allow saving and restoring
* the VM state, and doing that to a VM which uses SRIOV VF as one of
* the accessible devices requires support from KMD on both PF and VF side.
* VMM initiates all required operations through VFIO module, which then
* translates them into PF KMD calls. This description will focus on these
* calls, leaving out the module which initiates these steps (VFIO).
*
* In order to start the restore procedure, GuC needs to keep the VF in
* proper state. The PF driver can ensure GuC set it to VF_READY state
* by provisioning the VF, which in turn can be done after Function Level
* Reset of said VF (or after it was freshly created - in that case FLR
* is not needed). The FLR procedure ends with GuC sending message
* `GUC_PF_NOTIFY_VF_FLR_DONE`, and then provisioning data is sent to GuC.
* After the provisioning is completed, the VF needs to be paused, and
* at that point the actual restore can begin.
*
* During VF Restore, state of several resources is restored. These may
* include local memory content (system memory is restored by VMM itself),
* values of MMIO registers, stateless compression metadata and others.
* The final resource which also needs restoring is state of the VF
* submission maintained within GuC. For that, `GUC_PF_OPCODE_VF_RESTORE`
* message is used, with reference to the state blob to be consumed by
* GuC.
*
* Next, when VFIO is asked to set the VM into running state, the PF driver
* sends `GUC_PF_TRIGGER_VF_RESUME` to GuC. When sent after restore, this
* changes VF state within GuC to `VF_RESFIX_BLOCKED` rather than the
* usual `VF_RUNNING`. At this point GuC triggers an interrupt to inform
* the VF KMD within the VM that it was migrated.
*
* As soon as Virtual GPU of the VM starts, the VF driver within receives
* the MIGRATED interrupt and schedules post-migration recovery worker.
* That worker queries GuC for new provisioning (using MMIO communication),
* and applies fixups to any non-virtualized resources used by the VF.
*
* When the VF driver is ready to continue operation on the newly connected
* hardware, it sends `VF2GUC_NOTIFY_RESFIX_DONE` which causes it to
* enter the long awaited `VF_RUNNING` state, and therefore start handling
* CTB messages and scheduling workloads from the VF::
*
* PF GuC VF
* [ ] | |
* [ ] PF2GUC_VF_CONTROL(pause) | |
* [ ]---------------------------> [ ] |
* [ ] [ ] GuC sets new VF state to |
* [ ] [ ]------- VF_READY_PAUSED |
* [ ] [ ] | |
* [ ] [ ] <----- |
* [ ] success [ ] |
* [ ] <---------------------------[ ] |
* [ ] | |
* [ ] PF loads resources from the | |
* [ ]------- saved image supplied | |
* [ ] | | |
* [ ] <----- | |
* [ ] | |
* [ ] GUC_PF_OPCODE_VF_RESTORE | |
* [ ]---------------------------> [ ] |
* [ ] [ ] GuC loads contexts and CTB |
* [ ] [ ]------- state from image |
* [ ] [ ] | |
* [ ] [ ] <----- |
* [ ] [ ] |
* [ ] [ ] GuC sets new VF state to |
* [ ] [ ]------- VF_RESFIX_PAUSED |
* [ ] [ ] | |
* [ ] success [ ] <----- |
* [ ] <---------------------------[ ] |
* [ ] | |
* [ ] GUC_PF_TRIGGER_VF_RESUME | |
* [ ]---------------------------> [ ] |
* [ ] [ ] GuC sets new VF state to |
* [ ] [ ]------- VF_RESFIX_BLOCKED |
* [ ] [ ] | |
* [ ] [ ] <----- |
* [ ] [ ] |
* [ ] [ ] GUC_INTR_SW_INT_0 |
* [ ] success [ ]---------------------------> [ ]
* [ ] <---------------------------[ ] [ ]
* | | VF2GUC_QUERY_SINGLE_KLV [ ]
* | [ ] <---------------------------[ ]
* | [ ] [ ]
* | [ ] new VF provisioning [ ]
* | [ ]---------------------------> [ ]
* | | [ ]
* | | VF driver applies post [ ]
* | | migration fixups -------[ ]
* | | | [ ]
* | | -----> [ ]
* | | [ ]
* | | VF2GUC_NOTIFY_RESFIX_DONE [ ]
* | [ ] <---------------------------[ ]
* | [ ] [ ]
* | [ ] GuC sets new VF state to [ ]
* | [ ]------- VF_RUNNING [ ]
* | [ ] | [ ]
* | [ ] <----- [ ]
* | [ ] success [ ]
* | [ ]---------------------------> [ ]
* | | |
* | | |
*/
static void migration_worker_func(struct work_struct *w);
/**
* xe_sriov_vf_init_early - Initialize SR-IOV VF specific data.
* @xe: the &xe_device to initialize
*/
void xe_sriov_vf_init_early(struct xe_device *xe)
{
INIT_WORK(&xe->sriov.vf.migration.worker, migration_worker_func);
}
/**
* vf_post_migration_requery_guc - Re-query GuC for current VF provisioning.
* @xe: the &xe_device struct instance
*
* After migration, we need to re-query all VF configuration to make sure
* they match previous provisioning. Note that most of VF provisioning
* shall be the same, except GGTT range, since GGTT is not virtualized per-VF.
*
* Returns: 0 if the operation completed successfully, or a negative error
* code otherwise.
*/
static int vf_post_migration_requery_guc(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
int err, ret = 0;
for_each_gt(gt, xe, id) {
err = xe_gt_sriov_vf_query_config(gt);
ret = ret ?: err;
}
return ret;
}
/*
* vf_post_migration_imminent - Check if post-restore recovery is coming.
* @xe: the &xe_device struct instance
*
* Return: True if migration recovery worker will soon be running. Any worker currently
* executing does not affect the result.
*/
static bool vf_post_migration_imminent(struct xe_device *xe)
{
return xe->sriov.vf.migration.gt_flags != 0 ||
work_pending(&xe->sriov.vf.migration.worker);
}
/*
* Notify all GuCs about resource fixups apply finished.
*/
static void vf_post_migration_notify_resfix_done(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
for_each_gt(gt, xe, id) {
if (vf_post_migration_imminent(xe))
goto skip;
xe_gt_sriov_vf_notify_resfix_done(gt);
}
return;
skip:
drm_dbg(&xe->drm, "another recovery imminent, skipping notifications\n");
}
static void vf_post_migration_recovery(struct xe_device *xe)
{
int err;
drm_dbg(&xe->drm, "migration recovery in progress\n");
xe_pm_runtime_get(xe);
err = vf_post_migration_requery_guc(xe);
if (vf_post_migration_imminent(xe))
goto defer;
if (unlikely(err))
goto fail;
/* FIXME: add the recovery steps */
vf_post_migration_notify_resfix_done(xe);
xe_pm_runtime_put(xe);
drm_notice(&xe->drm, "migration recovery ended\n");
return;
defer:
xe_pm_runtime_put(xe);
drm_dbg(&xe->drm, "migration recovery deferred\n");
return;
fail:
xe_pm_runtime_put(xe);
drm_err(&xe->drm, "migration recovery failed (%pe)\n", ERR_PTR(err));
xe_device_declare_wedged(xe);
}
static void migration_worker_func(struct work_struct *w)
{
struct xe_device *xe = container_of(w, struct xe_device,
sriov.vf.migration.worker);
vf_post_migration_recovery(xe);
}
static bool vf_ready_to_recovery_on_all_gts(struct xe_device *xe)
{
struct xe_gt *gt;
unsigned int id;
for_each_gt(gt, xe, id) {
if (!test_bit(id, &xe->sriov.vf.migration.gt_flags)) {
xe_gt_sriov_dbg_verbose(gt, "still not ready to recover\n");
return false;
}
}
return true;
}
/**
* xe_sriov_vf_start_migration_recovery - Start VF migration recovery.
* @xe: the &xe_device to start recovery on
*
* This function shall be called only by VF.
*/
void xe_sriov_vf_start_migration_recovery(struct xe_device *xe)
{
bool started;
xe_assert(xe, IS_SRIOV_VF(xe));
if (!vf_ready_to_recovery_on_all_gts(xe))
return;
WRITE_ONCE(xe->sriov.vf.migration.gt_flags, 0);
/* Ensure other threads see that no flags are set now. */
smp_mb();
started = queue_work(xe->sriov.wq, &xe->sriov.vf.migration.worker);
drm_info(&xe->drm, "VF migration recovery %s\n", started ?
"scheduled" : "already in progress");
}

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023-2024 Intel Corporation
*/
#ifndef _XE_SRIOV_VF_H_
#define _XE_SRIOV_VF_H_
struct xe_device;
void xe_sriov_vf_init_early(struct xe_device *xe);
void xe_sriov_vf_start_migration_recovery(struct xe_device *xe);
#endif

View File

@ -211,6 +211,7 @@ DECLARE_EVENT_CLASS(xe_sched_job,
__string(dev, __dev_name_eq(job->q))
__field(u32, seqno)
__field(u32, lrc_seqno)
__field(u8, gt_id)
__field(u16, guc_id)
__field(u32, guc_state)
__field(u32, flags)
@ -223,6 +224,7 @@ DECLARE_EVENT_CLASS(xe_sched_job,
__assign_str(dev);
__entry->seqno = xe_sched_job_seqno(job);
__entry->lrc_seqno = xe_sched_job_lrc_seqno(job);
__entry->gt_id = job->q->gt->info.id;
__entry->guc_id = job->q->guc->id;
__entry->guc_state =
atomic_read(&job->q->guc->state);
@ -232,9 +234,9 @@ DECLARE_EVENT_CLASS(xe_sched_job,
__entry->batch_addr = (u64)job->ptrs[0].batch_addr;
),
TP_printk("dev=%s, fence=%p, seqno=%u, lrc_seqno=%u, guc_id=%d, batch_addr=0x%012llx, guc_state=0x%x, flags=0x%x, error=%d",
TP_printk("dev=%s, fence=%p, seqno=%u, lrc_seqno=%u, gt=%u, guc_id=%d, batch_addr=0x%012llx, guc_state=0x%x, flags=0x%x, error=%d",
__get_str(dev), __entry->fence, __entry->seqno,
__entry->lrc_seqno, __entry->guc_id,
__entry->lrc_seqno, __entry->gt_id, __entry->guc_id,
__entry->batch_addr, __entry->guc_state,
__entry->flags, __entry->error)
);
@ -282,6 +284,7 @@ DECLARE_EVENT_CLASS(xe_sched_msg,
__string(dev, __dev_name_eq(((struct xe_exec_queue *)msg->private_data)))
__field(u32, opcode)
__field(u16, guc_id)
__field(u8, gt_id)
),
TP_fast_assign(
@ -289,9 +292,11 @@ DECLARE_EVENT_CLASS(xe_sched_msg,
__entry->opcode = msg->opcode;
__entry->guc_id =
((struct xe_exec_queue *)msg->private_data)->guc->id;
__entry->gt_id =
((struct xe_exec_queue *)msg->private_data)->gt->info.id;
),
TP_printk("dev=%s, guc_id=%d, opcode=%u", __get_str(dev), __entry->guc_id,
TP_printk("dev=%s, gt=%u guc_id=%d, opcode=%u", __get_str(dev), __entry->gt_id, __entry->guc_id,
__entry->opcode)
);

View File

@ -48,6 +48,11 @@ DEFINE_EVENT(xe_bo, xe_bo_cpu_fault,
TP_ARGS(bo)
);
DEFINE_EVENT(xe_bo, xe_bo_validate,
TP_PROTO(struct xe_bo *bo),
TP_ARGS(bo)
);
TRACE_EVENT(xe_bo_move,
TP_PROTO(struct xe_bo *bo, uint32_t new_placement, uint32_t old_placement,
bool move_lacks_source),

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2024 Intel Corporation
*/
#ifndef __CHECKER__
#define CREATE_TRACE_POINTS
#include "xe_trace_lrc.h"
#endif

View File

@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright © 2024 Intel Corporation
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM xe
#if !defined(_XE_TRACE_LRC_H_) || defined(TRACE_HEADER_MULTI_READ)
#define _XE_TRACE_LRC_H_
#include <linux/tracepoint.h>
#include <linux/types.h>
#include "xe_gt_types.h"
#include "xe_lrc.h"
#include "xe_lrc_types.h"
#define __dev_name_lrc(lrc) dev_name(gt_to_xe((lrc)->fence_ctx.gt)->drm.dev)
TRACE_EVENT(xe_lrc_update_timestamp,
TP_PROTO(struct xe_lrc *lrc, uint32_t old),
TP_ARGS(lrc, old),
TP_STRUCT__entry(
__field(struct xe_lrc *, lrc)
__field(u32, old)
__field(u32, new)
__string(name, lrc->fence_ctx.name)
__string(device_id, __dev_name_lrc(lrc))
),
TP_fast_assign(
__entry->lrc = lrc;
__entry->old = old;
__entry->new = lrc->ctx_timestamp;
__assign_str(name);
__assign_str(device_id);
),
TP_printk("lrc=:%p lrc->name=%s old=%u new=%u device_id:%s",
__entry->lrc, __get_str(name),
__entry->old, __entry->new,
__get_str(device_id))
);
#endif
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/xe
#define TRACE_INCLUDE_FILE xe_trace_lrc
#include <trace/define_trace.h>

View File

@ -52,7 +52,7 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man,
struct xe_ttm_vram_mgr *mgr = to_xe_ttm_vram_mgr(man);
struct xe_ttm_vram_mgr_resource *vres;
struct drm_buddy *mm = &mgr->mm;
u64 size, remaining_size, min_page_size;
u64 size, min_page_size;
unsigned long lpfn;
int err;
@ -98,17 +98,6 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man,
goto error_fini;
}
if (WARN_ON(min_page_size > SZ_2G)) { /* FIXME: sg limit */
err = -EINVAL;
goto error_fini;
}
if (WARN_ON((size > SZ_2G &&
(vres->base.placement & TTM_PL_FLAG_CONTIGUOUS)))) {
err = -EINVAL;
goto error_fini;
}
if (WARN_ON(!IS_ALIGNED(size, min_page_size))) {
err = -EINVAL;
goto error_fini;
@ -116,12 +105,11 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man,
mutex_lock(&mgr->lock);
if (lpfn <= mgr->visible_size >> PAGE_SHIFT && size > mgr->visible_avail) {
mutex_unlock(&mgr->lock);
err = -ENOSPC;
goto error_fini;
goto error_unlock;
}
if (place->fpfn + (size >> PAGE_SHIFT) != place->lpfn &&
if (place->fpfn + (size >> PAGE_SHIFT) != lpfn &&
place->flags & TTM_PL_FLAG_CONTIGUOUS) {
size = roundup_pow_of_two(size);
min_page_size = size;
@ -129,25 +117,11 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man,
lpfn = max_t(unsigned long, place->fpfn + (size >> PAGE_SHIFT), lpfn);
}
remaining_size = size;
do {
/*
* Limit maximum size to 2GiB due to SG table limitations.
* FIXME: Should maybe be handled as part of sg construction.
*/
u64 alloc_size = min_t(u64, remaining_size, SZ_2G);
err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT,
(u64)lpfn << PAGE_SHIFT,
alloc_size,
min_page_size,
&vres->blocks,
vres->flags);
if (err)
goto error_free_blocks;
remaining_size -= alloc_size;
} while (remaining_size);
err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT,
(u64)lpfn << PAGE_SHIFT, size,
min_page_size, &vres->blocks, vres->flags);
if (err)
goto error_unlock;
if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
if (!drm_buddy_block_trim(mm, NULL, vres->base.size, &vres->blocks))
@ -194,9 +168,7 @@ static int xe_ttm_vram_mgr_new(struct ttm_resource_manager *man,
*res = &vres->base;
return 0;
error_free_blocks:
drm_buddy_free_list(mm, &vres->blocks, 0);
error_unlock:
mutex_unlock(&mgr->lock);
error_fini:
ttm_resource_fini(man, &vres->base);
@ -393,7 +365,8 @@ int xe_ttm_vram_mgr_alloc_sgt(struct xe_device *xe,
xe_res_first(res, offset, length, &cursor);
while (cursor.remaining) {
num_entries++;
xe_res_next(&cursor, cursor.size);
/* Limit maximum size to 2GiB due to SG table limitations. */
xe_res_next(&cursor, min_t(u64, cursor.size, SZ_2G));
}
r = sg_alloc_table(*sgt, num_entries, GFP_KERNEL);
@ -413,7 +386,7 @@ int xe_ttm_vram_mgr_alloc_sgt(struct xe_device *xe,
xe_res_first(res, offset, length, &cursor);
for_each_sgtable_sg((*sgt), sg, i) {
phys_addr_t phys = cursor.start + tile->mem.vram.io_start;
size_t size = cursor.size;
size_t size = min_t(u64, cursor.size, SZ_2G);
dma_addr_t addr;
addr = dma_map_resource(dev, phys, size, dir,
@ -426,7 +399,7 @@ int xe_ttm_vram_mgr_alloc_sgt(struct xe_device *xe,
sg_dma_address(sg) = addr;
sg_dma_len(sg) = size;
xe_res_next(&cursor, cursor.size);
xe_res_next(&cursor, size);
}
return 0;

View File

@ -732,13 +732,14 @@ static int xe_vma_ops_alloc(struct xe_vma_ops *vops, bool array_of_binds)
vops->pt_update_ops[i].ops =
kmalloc_array(vops->pt_update_ops[i].num_ops,
sizeof(*vops->pt_update_ops[i].ops),
GFP_KERNEL);
GFP_KERNEL | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
if (!vops->pt_update_ops[i].ops)
return array_of_binds ? -ENOBUFS : -ENOMEM;
}
return 0;
}
ALLOW_ERROR_INJECTION(xe_vma_ops_alloc, ERRNO);
static void xe_vma_ops_fini(struct xe_vma_ops *vops)
{
@ -1351,6 +1352,7 @@ static int xe_vm_create_scratch(struct xe_device *xe, struct xe_tile *tile,
return 0;
}
ALLOW_ERROR_INJECTION(xe_vm_create_scratch, ERRNO);
static void xe_vm_free_scratch(struct xe_vm *vm)
{
@ -1977,6 +1979,7 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_bo *bo,
return ops;
}
ALLOW_ERROR_INJECTION(vm_bind_ioctl_ops_create, ERRNO);
static struct xe_vma *new_vma(struct xe_vm *vm, struct drm_gpuva_op_map *op,
u16 pat_index, unsigned int flags)
@ -2356,13 +2359,15 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma,
bool validate)
{
struct xe_bo *bo = xe_vma_bo(vma);
struct xe_vm *vm = xe_vma_vm(vma);
int err = 0;
if (bo) {
if (!bo->vm)
err = drm_exec_lock_obj(exec, &bo->ttm.base);
if (!err && validate)
err = xe_bo_validate(bo, xe_vma_vm(vma), true);
err = xe_bo_validate(bo, vm,
!xe_vm_in_preempt_fence_mode(vm));
}
return err;
@ -2696,6 +2701,7 @@ static int vm_bind_ioctl_ops_execute(struct xe_vm *vm,
drm_exec_fini(&exec);
return err;
}
ALLOW_ERROR_INJECTION(vm_bind_ioctl_ops_execute, ERRNO);
#define SUPPORTED_FLAGS_STUB \
(DRM_XE_VM_BIND_FLAG_READONLY | \
@ -2732,7 +2738,8 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe,
*bind_ops = kvmalloc_array(args->num_binds,
sizeof(struct drm_xe_vm_bind_op),
GFP_KERNEL | __GFP_ACCOUNT);
GFP_KERNEL | __GFP_ACCOUNT |
__GFP_RETRY_MAYFAIL | __GFP_NOWARN);
if (!*bind_ops)
return args->num_binds > 1 ? -ENOBUFS : -ENOMEM;
@ -2972,14 +2979,16 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
if (args->num_binds) {
bos = kvcalloc(args->num_binds, sizeof(*bos),
GFP_KERNEL | __GFP_ACCOUNT);
GFP_KERNEL | __GFP_ACCOUNT |
__GFP_RETRY_MAYFAIL | __GFP_NOWARN);
if (!bos) {
err = -ENOMEM;
goto release_vm_lock;
}
ops = kvcalloc(args->num_binds, sizeof(*ops),
GFP_KERNEL | __GFP_ACCOUNT);
GFP_KERNEL | __GFP_ACCOUNT |
__GFP_RETRY_MAYFAIL | __GFP_NOWARN);
if (!ops) {
err = -ENOMEM;
goto release_vm_lock;
@ -3302,7 +3311,6 @@ void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap)
for (int i = 0; i < snap->num_snaps; i++) {
struct xe_bo *bo = snap->snap[i].bo;
struct iosys_map src;
int err;
if (IS_ERR(snap->snap[i].data))
@ -3315,16 +3323,8 @@ void xe_vm_snapshot_capture_delayed(struct xe_vm_snapshot *snap)
}
if (bo) {
xe_bo_lock(bo, false);
err = ttm_bo_vmap(&bo->ttm, &src);
if (!err) {
xe_map_memcpy_from(xe_bo_device(bo),
snap->snap[i].data,
&src, snap->snap[i].bo_ofs,
snap->snap[i].len);
ttm_bo_vunmap(&bo->ttm, &src);
}
xe_bo_unlock(bo);
err = xe_bo_read(bo, snap->snap[i].bo_ofs,
snap->snap[i].data, snap->snap[i].len);
} else {
void __user *userptr = (void __user *)(size_t)snap->snap[i].bo_ofs;

View File

@ -64,8 +64,8 @@
* update page level 2 PDE[1] to page level 3b phys address (GPU)
*
* bind BO2 0x1ff000-0x201000
* update page level 3a PTE[511] to BO2 phys addres (GPU)
* update page level 3b PTE[0] to BO2 phys addres + 0x1000 (GPU)
* update page level 3a PTE[511] to BO2 phys address (GPU)
* update page level 3b PTE[0] to BO2 phys address + 0x1000 (GPU)
*
* GPU bypass
* ~~~~~~~~~~
@ -192,7 +192,7 @@
*
* If a VM is in fault mode (TODO: link to fault mode), new bind operations that
* create mappings are by default deferred to the page fault handler (first
* use). This behavior can be overriden by setting the flag
* use). This behavior can be overridden by setting the flag
* DRM_XE_VM_BIND_FLAG_IMMEDIATE which indicates to creating the mapping
* immediately.
*
@ -209,7 +209,7 @@
*
* Since this a core kernel managed memory the kernel can move this memory
* whenever it wants. We register an invalidation MMU notifier to alert XE when
* a user poiter is about to move. The invalidation notifier needs to block
* a user pointer is about to move. The invalidation notifier needs to block
* until all pending users (jobs or compute mode engines) of the userptr are
* idle to ensure no faults. This done by waiting on all of VM's dma-resv slots.
*
@ -252,7 +252,7 @@
* Rebind worker
* -------------
*
* The rebind worker is very similar to an exec. It is resposible for rebinding
* The rebind worker is very similar to an exec. It is responsible for rebinding
* evicted BOs or userptrs, waiting on those operations, installing new preempt
* fences, and finally resuming executing of engines in the VM.
*
@ -317,11 +317,11 @@
* are not allowed, only long running workloads and ULLS are enabled on a faulting
* VM.
*
* Defered VM binds
* Deferred VM binds
* ----------------
*
* By default, on a faulting VM binds just allocate the VMA and the actual
* updating of the page tables is defered to the page fault handler. This
* updating of the page tables is deferred to the page fault handler. This
* behavior can be overridden by setting the flag DRM_XE_VM_BIND_FLAG_IMMEDIATE in
* the VM bind which will then do the bind immediately.
*
@ -500,18 +500,18 @@
* Slot waiting
* ------------
*
* 1. The exection of all jobs from kernel ops shall wait on all slots
* 1. The execution of all jobs from kernel ops shall wait on all slots
* (DMA_RESV_USAGE_PREEMPT_FENCE) of either an external BO or VM (depends on if
* kernel op is operating on external or private BO)
*
* 2. In non-compute mode, the exection of all jobs from rebinds in execs shall
* 2. In non-compute mode, the execution of all jobs from rebinds in execs shall
* wait on the DMA_RESV_USAGE_KERNEL slot of either an external BO or VM
* (depends on if the rebind is operatiing on an external or private BO)
*
* 3. In non-compute mode, the exection of all jobs from execs shall wait on the
* 3. In non-compute mode, the execution of all jobs from execs shall wait on the
* last rebind job
*
* 4. In compute mode, the exection of all jobs from rebinds in the rebind
* 4. In compute mode, the execution of all jobs from rebinds in the rebind
* worker shall wait on the DMA_RESV_USAGE_KERNEL slot of either an external BO
* or VM (depends on if rebind is operating on external or private BO)
*

View File

@ -0,0 +1,233 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright © 2024 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/errno.h>
#include <linux/intel_vsec.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/types.h>
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_drv.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
#include "xe_pm.h"
#include "xe_vsec.h"
#include "regs/xe_pmt.h"
/* PMT GUID value for BMG devices. NOTE: this is NOT a PCI id */
#define BMG_DEVICE_ID 0xE2F8
static struct intel_vsec_header bmg_telemetry = {
.length = 0x10,
.id = VSEC_ID_TELEMETRY,
.num_entries = 2,
.entry_size = 4,
.tbir = 0,
.offset = BMG_DISCOVERY_OFFSET,
};
static struct intel_vsec_header bmg_punit_crashlog = {
.length = 0x10,
.id = VSEC_ID_CRASHLOG,
.num_entries = 1,
.entry_size = 4,
.tbir = 0,
.offset = BMG_DISCOVERY_OFFSET + 0x60,
};
static struct intel_vsec_header bmg_oobmsm_crashlog = {
.length = 0x10,
.id = VSEC_ID_CRASHLOG,
.num_entries = 1,
.entry_size = 4,
.tbir = 0,
.offset = BMG_DISCOVERY_OFFSET + 0x78,
};
static struct intel_vsec_header *bmg_capabilities[] = {
&bmg_telemetry,
&bmg_punit_crashlog,
&bmg_oobmsm_crashlog,
NULL
};
enum xe_vsec {
XE_VSEC_UNKNOWN = 0,
XE_VSEC_BMG,
};
static struct intel_vsec_platform_info xe_vsec_info[] = {
[XE_VSEC_BMG] = {
.caps = VSEC_CAP_TELEMETRY | VSEC_CAP_CRASHLOG,
.headers = bmg_capabilities,
},
{ }
};
/*
* The GUID will have the following bits to decode:
* [0:3] - {Telemetry space iteration number (0,1,..)}
* [4:7] - Segment (SEGMENT_INDEPENDENT-0, Client-1, Server-2)
* [8:11] - SOC_SKU
* [12:27] Device ID changes for each down bin SKUs
* [28:29] - Capability Type (Crashlog-0, Telemetry Aggregator-1, Watcher-2)
* [30:31] - Record-ID (0-PUNIT, 1-OOBMSM_0, 2-OOBMSM_1)
*/
#define GUID_TELEM_ITERATION GENMASK(3, 0)
#define GUID_SEGMENT GENMASK(7, 4)
#define GUID_SOC_SKU GENMASK(11, 8)
#define GUID_DEVICE_ID GENMASK(27, 12)
#define GUID_CAP_TYPE GENMASK(29, 28)
#define GUID_RECORD_ID GENMASK(31, 30)
#define PUNIT_TELEMETRY_OFFSET 0x0200
#define PUNIT_WATCHER_OFFSET 0x14A0
#define OOBMSM_0_WATCHER_OFFSET 0x18D8
#define OOBMSM_1_TELEMETRY_OFFSET 0x1000
enum record_id {
PUNIT,
OOBMSM_0,
OOBMSM_1,
};
enum capability {
CRASHLOG,
TELEMETRY,
WATCHER,
};
static int xe_guid_decode(u32 guid, int *index, u32 *offset)
{
u32 record_id = FIELD_GET(GUID_RECORD_ID, guid);
u32 cap_type = FIELD_GET(GUID_CAP_TYPE, guid);
u32 device_id = FIELD_GET(GUID_DEVICE_ID, guid);
if (device_id != BMG_DEVICE_ID)
return -ENODEV;
if (cap_type > WATCHER)
return -EINVAL;
*offset = 0;
if (cap_type == CRASHLOG) {
*index = record_id == PUNIT ? 2 : 4;
return 0;
}
switch (record_id) {
case PUNIT:
*index = 0;
if (cap_type == TELEMETRY)
*offset = PUNIT_TELEMETRY_OFFSET;
else
*offset = PUNIT_WATCHER_OFFSET;
break;
case OOBMSM_0:
*index = 1;
if (cap_type == WATCHER)
*offset = OOBMSM_0_WATCHER_OFFSET;
break;
case OOBMSM_1:
*index = 1;
if (cap_type == TELEMETRY)
*offset = OOBMSM_1_TELEMETRY_OFFSET;
break;
default:
return -EINVAL;
}
return 0;
}
static int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset,
u32 count)
{
struct xe_device *xe = pdev_to_xe_device(pdev);
void __iomem *telem_addr = xe->mmio.regs + BMG_TELEMETRY_OFFSET;
u32 mem_region;
u32 offset;
int ret;
ret = xe_guid_decode(guid, &mem_region, &offset);
if (ret)
return ret;
telem_addr += offset + user_offset;
guard(mutex)(&xe->pmt.lock);
/* indicate that we are not at an appropriate power level */
if (!xe_pm_runtime_get_if_active(xe))
return -ENODATA;
/* set SoC re-mapper index register based on GUID memory region */
xe_mmio_rmw32(xe_root_tile_mmio(xe), SG_REMAP_INDEX1, SG_REMAP_BITS,
REG_FIELD_PREP(SG_REMAP_BITS, mem_region));
memcpy_fromio(data, telem_addr, count);
xe_pm_runtime_put(xe);
return count;
}
static struct pmt_callbacks xe_pmt_cb = {
.read_telem = xe_pmt_telem_read,
};
static const int vsec_platforms[] = {
[XE_BATTLEMAGE] = XE_VSEC_BMG,
};
static enum xe_vsec get_platform_info(struct xe_device *xe)
{
if (xe->info.platform > XE_BATTLEMAGE)
return XE_VSEC_UNKNOWN;
return vsec_platforms[xe->info.platform];
}
/**
* xe_vsec_init - Initialize resources and add intel_vsec auxiliary
* interface
* @xe: valid xe instance
*/
void xe_vsec_init(struct xe_device *xe)
{
struct intel_vsec_platform_info *info;
struct device *dev = xe->drm.dev;
struct pci_dev *pdev = to_pci_dev(dev);
enum xe_vsec platform;
platform = get_platform_info(xe);
if (platform == XE_VSEC_UNKNOWN)
return;
info = &xe_vsec_info[platform];
if (!info->headers)
return;
switch (platform) {
case XE_VSEC_BMG:
info->priv_data = &xe_pmt_cb;
break;
default:
break;
}
/*
* Register a VSEC. Cleanup is handled using device managed
* resources.
*/
intel_vsec_register(pdev, info);
}
MODULE_IMPORT_NS("INTEL_VSEC");

View File

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright © 2024 Intel Corporation */
#ifndef _XE_VSEC_H_
#define _XE_VSEC_H_
struct xe_device;
void xe_vsec_init(struct xe_device *xe);
#endif

View File

@ -607,6 +607,12 @@ static const struct xe_rtp_entry_sr engine_was[] = {
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(ROW_CHICKEN4, DISABLE_TDL_PUSH))
},
{ XE_RTP_NAME("16024792527"),
XE_RTP_RULES(GRAPHICS_VERSION(3000), GRAPHICS_STEP(A0, B0),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(FIELD_SET(SAMPLER_MODE, SMP_WAIT_FETCH_MERGING_COUNTER,
SMP_FORCE_128B_OVERFETCH))
},
{}
};

View File

@ -1,3 +1,4 @@
1607983814 GRAPHICS_VERSION_RANGE(1200, 1210)
22012773006 GRAPHICS_VERSION_RANGE(1200, 1250)
14014475959 GRAPHICS_VERSION_RANGE(1270, 1271), GRAPHICS_STEP(A0, B0)
PLATFORM(DG2)

View File

@ -0,0 +1,235 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2022 Intel Corporation
*/
#ifndef _XE_PCIIDS_H_
#define _XE_PCIIDS_H_
/*
* Lists below can be turned into initializers for a struct pci_device_id
* by defining INTEL_VGA_DEVICE:
*
* #define INTEL_VGA_DEVICE(id, info) { \
* 0x8086, id, \
* ~0, ~0, \
* 0x030000, 0xff0000, \
* (unsigned long) info }
*
* And then calling like:
*
* XE_TGL_12_GT1_IDS(INTEL_VGA_DEVICE, ## __VA_ARGS__)
*
* To turn them into something else, just provide a different macro passed as
* first argument.
*/
/* TGL */
#define XE_TGL_GT1_IDS(MACRO__, ...) \
MACRO__(0x9A60, ## __VA_ARGS__), \
MACRO__(0x9A68, ## __VA_ARGS__), \
MACRO__(0x9A70, ## __VA_ARGS__)
#define XE_TGL_GT2_IDS(MACRO__, ...) \
MACRO__(0x9A40, ## __VA_ARGS__), \
MACRO__(0x9A49, ## __VA_ARGS__), \
MACRO__(0x9A59, ## __VA_ARGS__), \
MACRO__(0x9A78, ## __VA_ARGS__), \
MACRO__(0x9AC0, ## __VA_ARGS__), \
MACRO__(0x9AC9, ## __VA_ARGS__), \
MACRO__(0x9AD9, ## __VA_ARGS__), \
MACRO__(0x9AF8, ## __VA_ARGS__)
#define XE_TGL_IDS(MACRO__, ...) \
XE_TGL_GT1_IDS(MACRO__, ## __VA_ARGS__),\
XE_TGL_GT2_IDS(MACRO__, ## __VA_ARGS__)
/* RKL */
#define XE_RKL_IDS(MACRO__, ...) \
MACRO__(0x4C80, ## __VA_ARGS__), \
MACRO__(0x4C8A, ## __VA_ARGS__), \
MACRO__(0x4C8B, ## __VA_ARGS__), \
MACRO__(0x4C8C, ## __VA_ARGS__), \
MACRO__(0x4C90, ## __VA_ARGS__), \
MACRO__(0x4C9A, ## __VA_ARGS__)
/* DG1 */
#define XE_DG1_IDS(MACRO__, ...) \
MACRO__(0x4905, ## __VA_ARGS__), \
MACRO__(0x4906, ## __VA_ARGS__), \
MACRO__(0x4907, ## __VA_ARGS__), \
MACRO__(0x4908, ## __VA_ARGS__), \
MACRO__(0x4909, ## __VA_ARGS__)
/* ADL-S */
#define XE_ADLS_IDS(MACRO__, ...) \
MACRO__(0x4680, ## __VA_ARGS__), \
MACRO__(0x4682, ## __VA_ARGS__), \
MACRO__(0x4688, ## __VA_ARGS__), \
MACRO__(0x468A, ## __VA_ARGS__), \
MACRO__(0x468B, ## __VA_ARGS__), \
MACRO__(0x4690, ## __VA_ARGS__), \
MACRO__(0x4692, ## __VA_ARGS__), \
MACRO__(0x4693, ## __VA_ARGS__)
/* ADL-P */
#define XE_ADLP_IDS(MACRO__, ...) \
MACRO__(0x46A0, ## __VA_ARGS__), \
MACRO__(0x46A1, ## __VA_ARGS__), \
MACRO__(0x46A2, ## __VA_ARGS__), \
MACRO__(0x46A3, ## __VA_ARGS__), \
MACRO__(0x46A6, ## __VA_ARGS__), \
MACRO__(0x46A8, ## __VA_ARGS__), \
MACRO__(0x46AA, ## __VA_ARGS__), \
MACRO__(0x462A, ## __VA_ARGS__), \
MACRO__(0x4626, ## __VA_ARGS__), \
MACRO__(0x4628, ## __VA_ARGS__), \
MACRO__(0x46B0, ## __VA_ARGS__), \
MACRO__(0x46B1, ## __VA_ARGS__), \
MACRO__(0x46B2, ## __VA_ARGS__), \
MACRO__(0x46B3, ## __VA_ARGS__), \
MACRO__(0x46C0, ## __VA_ARGS__), \
MACRO__(0x46C1, ## __VA_ARGS__), \
MACRO__(0x46C2, ## __VA_ARGS__), \
MACRO__(0x46C3, ## __VA_ARGS__)
/* ADL-N */
#define XE_ADLN_IDS(MACRO__, ...) \
MACRO__(0x46D0, ## __VA_ARGS__), \
MACRO__(0x46D1, ## __VA_ARGS__), \
MACRO__(0x46D2, ## __VA_ARGS__), \
MACRO__(0x46D3, ## __VA_ARGS__), \
MACRO__(0x46D4, ## __VA_ARGS__)
/* RPL-S */
#define XE_RPLS_IDS(MACRO__, ...) \
MACRO__(0xA780, ## __VA_ARGS__), \
MACRO__(0xA781, ## __VA_ARGS__), \
MACRO__(0xA782, ## __VA_ARGS__), \
MACRO__(0xA783, ## __VA_ARGS__), \
MACRO__(0xA788, ## __VA_ARGS__), \
MACRO__(0xA789, ## __VA_ARGS__), \
MACRO__(0xA78A, ## __VA_ARGS__), \
MACRO__(0xA78B, ## __VA_ARGS__)
/* RPL-U */
#define XE_RPLU_IDS(MACRO__, ...) \
MACRO__(0xA721, ## __VA_ARGS__), \
MACRO__(0xA7A1, ## __VA_ARGS__), \
MACRO__(0xA7A9, ## __VA_ARGS__), \
MACRO__(0xA7AC, ## __VA_ARGS__), \
MACRO__(0xA7AD, ## __VA_ARGS__)
/* RPL-P */
#define XE_RPLP_IDS(MACRO__, ...) \
MACRO__(0xA720, ## __VA_ARGS__), \
MACRO__(0xA7A0, ## __VA_ARGS__), \
MACRO__(0xA7A8, ## __VA_ARGS__), \
MACRO__(0xA7AA, ## __VA_ARGS__), \
MACRO__(0xA7AB, ## __VA_ARGS__)
/* DG2 */
#define XE_DG2_G10_IDS(MACRO__, ...) \
MACRO__(0x5690, ## __VA_ARGS__), \
MACRO__(0x5691, ## __VA_ARGS__), \
MACRO__(0x5692, ## __VA_ARGS__), \
MACRO__(0x56A0, ## __VA_ARGS__), \
MACRO__(0x56A1, ## __VA_ARGS__), \
MACRO__(0x56A2, ## __VA_ARGS__), \
MACRO__(0x56BE, ## __VA_ARGS__), \
MACRO__(0x56BF, ## __VA_ARGS__)
#define XE_DG2_G11_IDS(MACRO__, ...) \
MACRO__(0x5693, ## __VA_ARGS__), \
MACRO__(0x5694, ## __VA_ARGS__), \
MACRO__(0x5695, ## __VA_ARGS__), \
MACRO__(0x56A5, ## __VA_ARGS__), \
MACRO__(0x56A6, ## __VA_ARGS__), \
MACRO__(0x56B0, ## __VA_ARGS__), \
MACRO__(0x56B1, ## __VA_ARGS__), \
MACRO__(0x56BA, ## __VA_ARGS__), \
MACRO__(0x56BB, ## __VA_ARGS__), \
MACRO__(0x56BC, ## __VA_ARGS__), \
MACRO__(0x56BD, ## __VA_ARGS__)
#define XE_DG2_G12_IDS(MACRO__, ...) \
MACRO__(0x5696, ## __VA_ARGS__), \
MACRO__(0x5697, ## __VA_ARGS__), \
MACRO__(0x56A3, ## __VA_ARGS__), \
MACRO__(0x56A4, ## __VA_ARGS__), \
MACRO__(0x56B2, ## __VA_ARGS__), \
MACRO__(0x56B3, ## __VA_ARGS__)
#define XE_DG2_IDS(MACRO__, ...) \
XE_DG2_G10_IDS(MACRO__, ## __VA_ARGS__),\
XE_DG2_G11_IDS(MACRO__, ## __VA_ARGS__),\
XE_DG2_G12_IDS(MACRO__, ## __VA_ARGS__)
#define XE_ATS_M150_IDS(MACRO__, ...) \
MACRO__(0x56C0, ## __VA_ARGS__), \
MACRO__(0x56C2, ## __VA_ARGS__)
#define XE_ATS_M75_IDS(MACRO__, ...) \
MACRO__(0x56C1, ## __VA_ARGS__)
#define XE_ATS_M_IDS(MACRO__, ...) \
XE_ATS_M150_IDS(MACRO__, ## __VA_ARGS__),\
XE_ATS_M75_IDS(MACRO__, ## __VA_ARGS__)
/* ARL */
#define XE_ARL_IDS(MACRO__, ...) \
MACRO__(0x7D41, ## __VA_ARGS__), \
MACRO__(0x7D51, ## __VA_ARGS__), \
MACRO__(0x7D67, ## __VA_ARGS__), \
MACRO__(0x7DD1, ## __VA_ARGS__), \
MACRO__(0xB640, ## __VA_ARGS__)
/* MTL */
#define XE_MTL_IDS(MACRO__, ...) \
MACRO__(0x7D40, ## __VA_ARGS__), \
MACRO__(0x7D45, ## __VA_ARGS__), \
MACRO__(0x7D55, ## __VA_ARGS__), \
MACRO__(0x7D60, ## __VA_ARGS__), \
MACRO__(0x7DD5, ## __VA_ARGS__)
/* PVC */
#define XE_PVC_IDS(MACRO__, ...) \
MACRO__(0x0B69, ## __VA_ARGS__), \
MACRO__(0x0B6E, ## __VA_ARGS__), \
MACRO__(0x0BD4, ## __VA_ARGS__), \
MACRO__(0x0BD5, ## __VA_ARGS__), \
MACRO__(0x0BD6, ## __VA_ARGS__), \
MACRO__(0x0BD7, ## __VA_ARGS__), \
MACRO__(0x0BD8, ## __VA_ARGS__), \
MACRO__(0x0BD9, ## __VA_ARGS__), \
MACRO__(0x0BDA, ## __VA_ARGS__), \
MACRO__(0x0BDB, ## __VA_ARGS__), \
MACRO__(0x0BE0, ## __VA_ARGS__), \
MACRO__(0x0BE1, ## __VA_ARGS__), \
MACRO__(0x0BE5, ## __VA_ARGS__)
#define XE_LNL_IDS(MACRO__, ...) \
MACRO__(0x6420, ## __VA_ARGS__), \
MACRO__(0x64A0, ## __VA_ARGS__), \
MACRO__(0x64B0, ## __VA_ARGS__)
#define XE_BMG_IDS(MACRO__, ...) \
MACRO__(0xE202, ## __VA_ARGS__), \
MACRO__(0xE20B, ## __VA_ARGS__), \
MACRO__(0xE20C, ## __VA_ARGS__), \
MACRO__(0xE20D, ## __VA_ARGS__), \
MACRO__(0xE212, ## __VA_ARGS__)
#define XE_PTL_IDS(MACRO__, ...) \
MACRO__(0xB080, ## __VA_ARGS__), \
MACRO__(0xB081, ## __VA_ARGS__), \
MACRO__(0xB082, ## __VA_ARGS__), \
MACRO__(0xB090, ## __VA_ARGS__), \
MACRO__(0xB091, ## __VA_ARGS__), \
MACRO__(0xB092, ## __VA_ARGS__), \
MACRO__(0xB0A0, ## __VA_ARGS__), \
MACRO__(0xB0A1, ## __VA_ARGS__), \
MACRO__(0xB0A2, ## __VA_ARGS__), \
MACRO__(0xB0B0, ## __VA_ARGS__)
#endif

View File

@ -421,6 +421,8 @@ void ttm_bo_unpin(struct ttm_buffer_object *bo);
int ttm_bo_evict_first(struct ttm_device *bdev,
struct ttm_resource_manager *man,
struct ttm_operation_ctx *ctx);
int ttm_bo_access(struct ttm_buffer_object *bo, unsigned long offset,
void *buf, int len, int write);
vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo,
struct vm_fault *vmf);
vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,

View File

@ -1486,6 +1486,7 @@ struct drm_xe_oa_unit {
__u64 capabilities;
#define DRM_XE_OA_CAPS_BASE (1 << 0)
#define DRM_XE_OA_CAPS_SYNCS (1 << 1)
#define DRM_XE_OA_CAPS_OA_BUFFER_SIZE (1 << 2)
/** @oa_timestamp_freq: OA timestamp freq */
__u64 oa_timestamp_freq;
@ -1651,6 +1652,14 @@ enum drm_xe_oa_property_id {
* to the VM bind case.
*/
DRM_XE_OA_PROPERTY_SYNCS,
/**
* @DRM_XE_OA_PROPERTY_OA_BUFFER_SIZE: Size of OA buffer to be
* allocated by the driver in bytes. Supported sizes are powers of
* 2 from 128 KiB to 128 MiB. When not specified, a 16 MiB OA
* buffer is allocated by default.
*/
DRM_XE_OA_PROPERTY_OA_BUFFER_SIZE,
};
/**