mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-15 14:25:03 -05:00
Merge tag 'drm-xe-next-2025-12-19' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next
[airlied: fix guc submit double definition] UAPI Changes: - Multi-Queue support (Niranjana) - Add DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE (Brost) - Add NO_COMPRESSION BO flag and query capability (Sanjay) - Add gt_id to struct drm_xe_oa_unit (Ashutosh) - Expose MERT OA unit (Ashutosh) - Sysfs Survivability refactor (Riana) Cross-subsystem Changes: - VFIO: Add device specific vfio_pci driver variant for Intel graphics (Winiarski) Driver Changes: - MAINTAINERS update (Lucas -> Matt) - Add helper to query compression enable status (Xin) - Xe_VM fixes and updates (Shuicheng, Himal) - Documentation fixes (Winiarski, Swaraj, Niranjana) - Kunit fix (Roper) - Fix potential leaks, uaf, null derref, and oversized allocations (Shuicheng, Sanjay, Mika, Tapani) - Other minor fixes like kbuild duplication and sysfs_emit (Shuicheng, Madhur) - Handle msix vector0 interrupt (Venkata) - Scope-based forcewake and runtime PM (Roper, Raag) - GuC/HuC related fixes and refactors (Lucas, Zhanjun, Brost, Julia, Wajdeczko) - Fix conversion from clock ticks to milliseconds (Harish) - SRIOV PF PF: Add support for MERT (Lukasz) - Enable SR-IOV VF migration and other SRIOV updates (Winiarski, Satya, Brost, Wajdeczko, Piotr, Tomasz, Daniele) - Optimize runtime suspend/resume and other PM improvements (Raag) - Some W/a additions and updates (Bala, Harish, Roper) - Use for_each_tlb_inval() to calculate invalidation fences (Roper) - Fix VFIO link error (Arnd) - Fix ix drm_gpusvm_init() arguments (Arnd) - Other OA refactor (Ashutosh) - Refactor PAT and expose debugfs (Xin) - Enable Indirect Ring State for xe3p_xpc (Niranjana) - MEI interrupt fix (Junxiao) - Add stats for mode switching on hw_engine_group (Francois) - DMA-Buf related changes (Thomas) - Multi Queue feature support (Niranjana) - Enable I2C controller for Crescent Island (Raag) - Enable NVM for Crescent Island (Sasha) - Increase TDF timeout (Jagmeet) - Restore engine registers before restarting schedulers after GT reset (Jan) - Page Reclamation Support for Xe3p Platforms (Brian, Brost, Oak) - Fix performance when pagefaults and 3d/display share resources (Brost) - More OA MERT work (Ashutosh) - Fix return values (Dan) - Some log level and messages improvements (Brost) Signed-off-by: Dave Airlie <airlied@redhat.com> From: Rodrigo Vivi <rodrigo.vivi@intel.com> Link: https://patch.msgid.link/aUXUhEgzs6hDLQuu@intel.com
This commit is contained in:
1
.mailmap
1
.mailmap
@@ -481,6 +481,7 @@ Lorenzo Pieralisi <lpieralisi@kernel.org> <lorenzo.pieralisi@arm.com>
|
||||
Lorenzo Stoakes <lorenzo.stoakes@oracle.com> <lstoakes@gmail.com>
|
||||
Luca Ceresoli <luca.ceresoli@bootlin.com> <luca@lucaceresoli.net>
|
||||
Luca Weiss <luca@lucaweiss.eu> <luca@z3ntu.xyz>
|
||||
Lucas De Marchi <demarchi@kernel.org> <lucas.demarchi@intel.com>
|
||||
Lukasz Luba <lukasz.luba@arm.com> <l.luba@partner.samsung.com>
|
||||
Luo Jie <quic_luoj@quicinc.com> <luoj@codeaurora.org>
|
||||
Lance Yang <lance.yang@linux.dev> <ioworker0@gmail.com>
|
||||
|
||||
@@ -119,7 +119,7 @@ Description:
|
||||
The GT preemption timeout (PT) in [us] to be applied to all functions.
|
||||
See sriov_admin/{pf,vf<N>}/profile/preempt_timeout_us for more details.
|
||||
|
||||
sched_priority: (RW/RO) string
|
||||
sched_priority: (WO) string
|
||||
The GT scheduling priority to be applied for all functions.
|
||||
See sriov_admin/{pf,vf<N>}/profile/sched_priority for more details.
|
||||
|
||||
|
||||
@@ -7,6 +7,20 @@ Execution Queue
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
|
||||
:doc: Execution Queue
|
||||
|
||||
Multi Queue Group
|
||||
=================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
|
||||
:doc: Multi Queue Group
|
||||
|
||||
.. _multi-queue-group-guc-interface:
|
||||
|
||||
Multi Queue Group GuC interface
|
||||
===============================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_guc_submit.c
|
||||
:doc: Multi Queue Group GuC interface
|
||||
|
||||
Internal API
|
||||
============
|
||||
|
||||
|
||||
@@ -12640,7 +12640,7 @@ F: include/drm/intel/
|
||||
F: include/uapi/drm/i915_drm.h
|
||||
|
||||
INTEL DRM XE DRIVER (Lunar Lake and newer)
|
||||
M: Lucas De Marchi <lucas.demarchi@intel.com>
|
||||
M: Matthew Brost <matthew.brost@intel.com>
|
||||
M: Thomas Hellström <thomas.hellstrom@linux.intel.com>
|
||||
M: Rodrigo Vivi <rodrigo.vivi@intel.com>
|
||||
L: intel-xe@lists.freedesktop.org
|
||||
|
||||
@@ -1288,6 +1288,9 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
|
||||
DMA_BIDIRECTIONAL;
|
||||
|
||||
retry:
|
||||
if (time_after(jiffies, timeout))
|
||||
return -EBUSY;
|
||||
|
||||
hmm_range.notifier_seq = mmu_interval_read_begin(notifier);
|
||||
if (drm_gpusvm_pages_valid_unlocked(gpusvm, svm_pages))
|
||||
goto set_seqno;
|
||||
|
||||
@@ -95,6 +95,7 @@ xe-y += xe_bb.o \
|
||||
xe_oa.o \
|
||||
xe_observation.o \
|
||||
xe_pagefault.o \
|
||||
xe_page_reclaim.o \
|
||||
xe_pat.o \
|
||||
xe_pci.o \
|
||||
xe_pcode.o \
|
||||
@@ -173,6 +174,7 @@ xe-$(CONFIG_PCI_IOV) += \
|
||||
xe_lmtt.o \
|
||||
xe_lmtt_2l.o \
|
||||
xe_lmtt_ml.o \
|
||||
xe_mert.o \
|
||||
xe_pci_sriov.o \
|
||||
xe_sriov_packet.o \
|
||||
xe_sriov_pf.o \
|
||||
|
||||
@@ -139,6 +139,10 @@ enum xe_guc_action {
|
||||
XE_GUC_ACTION_DEREGISTER_G2G = 0x4508,
|
||||
XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE = 0x4600,
|
||||
XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC = 0x4601,
|
||||
XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_QUEUE = 0x4602,
|
||||
XE_GUC_ACTION_MULTI_QUEUE_CONTEXT_CGP_SYNC = 0x4603,
|
||||
XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE = 0x4604,
|
||||
XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR = 0x4605,
|
||||
XE_GUC_ACTION_CLIENT_SOFT_RESET = 0x5507,
|
||||
XE_GUC_ACTION_SET_ENG_UTIL_BUFF = 0x550A,
|
||||
XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER = 0x550C,
|
||||
@@ -151,6 +155,8 @@ enum xe_guc_action {
|
||||
XE_GUC_ACTION_TLB_INVALIDATION = 0x7000,
|
||||
XE_GUC_ACTION_TLB_INVALIDATION_DONE = 0x7001,
|
||||
XE_GUC_ACTION_TLB_INVALIDATION_ALL = 0x7002,
|
||||
XE_GUC_ACTION_PAGE_RECLAMATION = 0x7003,
|
||||
XE_GUC_ACTION_PAGE_RECLAMATION_DONE = 0x7004,
|
||||
XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION = 0x8002,
|
||||
XE_GUC_ACTION_NOTIFY_FLUSH_LOG_BUFFER_TO_FILE = 0x8003,
|
||||
XE_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED = 0x8004,
|
||||
|
||||
@@ -502,13 +502,17 @@
|
||||
#define VF2GUC_VF_RESET_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
|
||||
|
||||
/**
|
||||
* DOC: VF2GUC_NOTIFY_RESFIX_DONE
|
||||
* DOC: VF2GUC_RESFIX_DONE
|
||||
*
|
||||
* This action is used by VF to notify the GuC that the VF KMD has completed
|
||||
* post-migration recovery steps.
|
||||
* This action is used by VF to inform the GuC that the VF KMD has completed
|
||||
* post-migration recovery steps. From GuC VF compatibility 1.27.0 onwards, it
|
||||
* shall only be sent after posting RESFIX_START and that both @MARKER fields
|
||||
* must match.
|
||||
*
|
||||
* This message must be sent as `MMIO HXG Message`_.
|
||||
*
|
||||
* Updated since GuC VF compatibility 1.27.0.
|
||||
*
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
* | | Bits | Description |
|
||||
* +===+=======+==============================================================+
|
||||
@@ -516,9 +520,11 @@
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 27:16 | DATA0 = MBZ |
|
||||
* | | 27:16 | DATA0 = MARKER = MBZ (only prior 1.27.0) |
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE` = 0x5508 |
|
||||
* | | 27:16 | DATA0 = MARKER - can't be zero (1.27.0+) |
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_RESFIX_DONE` = 0x5508 |
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
*
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
@@ -531,13 +537,13 @@
|
||||
* | | 27:0 | DATA0 = MBZ |
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
*/
|
||||
#define GUC_ACTION_VF2GUC_NOTIFY_RESFIX_DONE 0x5508u
|
||||
#define GUC_ACTION_VF2GUC_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_RESFIX_DONE_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
|
||||
#define VF2GUC_RESFIX_DONE_REQUEST_MSG_0_MARKER 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
|
||||
#define VF2GUC_RESFIX_DONE_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
|
||||
#define VF2GUC_RESFIX_DONE_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
|
||||
|
||||
/**
|
||||
* DOC: VF2GUC_QUERY_SINGLE_KLV
|
||||
@@ -656,4 +662,45 @@
|
||||
#define PF2GUC_SAVE_RESTORE_VF_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
|
||||
#define PF2GUC_SAVE_RESTORE_VF_RESPONSE_MSG_0_USED GUC_HXG_RESPONSE_MSG_0_DATA0
|
||||
|
||||
/**
|
||||
* DOC: VF2GUC_RESFIX_START
|
||||
*
|
||||
* This action is used by VF to inform the GuC that the VF KMD will be starting
|
||||
* post-migration recovery fixups. The @MARKER sent with this action must match
|
||||
* with the MARKER posted in the VF2GUC_RESFIX_DONE message.
|
||||
*
|
||||
* This message must be sent as `MMIO HXG Message`_.
|
||||
*
|
||||
* Available since GuC VF compatibility 1.27.0.
|
||||
*
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
* | | Bits | Description |
|
||||
* +===+=======+==============================================================+
|
||||
* | 0 | 31 | ORIGIN = GUC_HXG_ORIGIN_HOST_ |
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 30:28 | TYPE = GUC_HXG_TYPE_REQUEST_ |
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 27:16 | DATA0 = MARKER - can't be zero |
|
||||
* | +-------+--------------------------------------------------------------+
|
||||
* | | 15:0 | ACTION = _`GUC_ACTION_VF2GUC_RESFIX_START` = 0x550F |
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
*
|
||||
* +---+-------+--------------------------------------------------------------+
|
||||
* | | 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_RESFIX_START 0x550Fu
|
||||
|
||||
#define VF2GUC_RESFIX_START_REQUEST_MSG_LEN GUC_HXG_REQUEST_MSG_MIN_LEN
|
||||
#define VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER GUC_HXG_REQUEST_MSG_0_DATA0
|
||||
|
||||
#define VF2GUC_RESFIX_START_RESPONSE_MSG_LEN GUC_HXG_RESPONSE_MSG_MIN_LEN
|
||||
#define VF2GUC_RESFIX_START_RESPONSE_MSG_0_MBZ GUC_HXG_RESPONSE_MSG_0_DATA0
|
||||
|
||||
#endif
|
||||
|
||||
@@ -352,6 +352,12 @@ enum {
|
||||
* :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
|
||||
*
|
||||
* _`GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT` : 0x8A0D
|
||||
* Given that multi-LRC contexts are incompatible with SRIOV scheduler
|
||||
* groups and cause the latter to be turned off when registered with the
|
||||
* GuC, this config allows the PF to set a threshold for multi-LRC context
|
||||
* registrations by VFs to monitor their behavior.
|
||||
*/
|
||||
|
||||
#define GUC_KLV_VF_CFG_GGTT_START_KEY 0x0001
|
||||
@@ -410,6 +416,9 @@ enum {
|
||||
#define GUC_SCHED_PRIORITY_NORMAL 1u
|
||||
#define GUC_SCHED_PRIORITY_HIGH 2u
|
||||
|
||||
#define GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT_KEY 0x8a0d
|
||||
#define GUC_KLV_VF_CFG_THRESHOLD_MULTI_LRC_COUNT_LEN 1u
|
||||
|
||||
/*
|
||||
* Workaround keys:
|
||||
*/
|
||||
|
||||
171
drivers/gpu/drm/xe/abi/guc_lfd_abi.h
Normal file
171
drivers/gpu/drm/xe/abi/guc_lfd_abi.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _ABI_GUC_LFD_ABI_H_
|
||||
#define _ABI_GUC_LFD_ABI_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "guc_lic_abi.h"
|
||||
|
||||
/* The current major version of GuC-Log-File format. */
|
||||
#define GUC_LFD_FORMAT_VERSION_MAJOR 0x0001
|
||||
/* The current minor version of GuC-Log-File format. */
|
||||
#define GUC_LFD_FORMAT_VERSION_MINOR 0x0000
|
||||
|
||||
/** enum guc_lfd_type - Log format descriptor type */
|
||||
enum guc_lfd_type {
|
||||
/**
|
||||
* @GUC_LFD_TYPE_FW_REQUIRED_RANGE_START: Start of range for
|
||||
* required LFDs from GuC
|
||||
* @GUC_LFD_TYPE_FW_VERSION: GuC Firmware Version structure.
|
||||
* @GUC_LFD_TYPE_GUC_DEVICE_ID: GuC microcontroller device ID.
|
||||
* @GUC_LFD_TYPE_TSC_FREQUENCY: Frequency of GuC timestamps.
|
||||
* @GUC_LFD_TYPE_GMD_ID: HW GMD ID.
|
||||
* @GUC_LFD_TYPE_BUILD_PLATFORM_ID: GuC build platform ID.
|
||||
* @GUC_LFD_TYPE_FW_REQUIRED_RANGE_END: End of range for
|
||||
* required LFDs from GuC
|
||||
*/
|
||||
GUC_LFD_TYPE_FW_REQUIRED_RANGE_START = 0x1,
|
||||
GUC_LFD_TYPE_FW_VERSION = 0x1,
|
||||
GUC_LFD_TYPE_GUC_DEVICE_ID = 0x2,
|
||||
GUC_LFD_TYPE_TSC_FREQUENCY = 0x3,
|
||||
GUC_LFD_TYPE_GMD_ID = 0x4,
|
||||
GUC_LFD_TYPE_BUILD_PLATFORM_ID = 0x5,
|
||||
GUC_LFD_TYPE_FW_REQUIRED_RANGE_END = 0x1FFF,
|
||||
|
||||
/**
|
||||
* @GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START: Start of range for
|
||||
* optional LFDs from GuC
|
||||
* @GUC_LFD_TYPE_LOG_EVENTS_BUFFER: Log-event-entries buffer.
|
||||
* @GUC_LFD_TYPE_FW_CRASH_DUMP: GuC generated crash-dump blob.
|
||||
* @GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END: End of range for
|
||||
* optional LFDs from GuC
|
||||
*/
|
||||
GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START = 0x2000,
|
||||
GUC_LFD_TYPE_LOG_EVENTS_BUFFER = 0x2000,
|
||||
GUC_LFD_TYPE_FW_CRASH_DUMP = 0x2001,
|
||||
GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END = 0x3FFF,
|
||||
|
||||
/**
|
||||
* @GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START: Start of range for
|
||||
* required KMD LFDs
|
||||
* @GUC_LFD_TYPE_OS_ID: An identifier for the OS.
|
||||
* @GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END: End of this range for
|
||||
* required KMD LFDs
|
||||
*/
|
||||
GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START = 0x4000,
|
||||
GUC_LFD_TYPE_OS_ID = 0x4000,
|
||||
GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END = 0x5FFF,
|
||||
|
||||
/**
|
||||
* @GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START: Start of range for
|
||||
* optional KMD LFDs
|
||||
* @GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT: Binary representation of
|
||||
* GuC log-events schema.
|
||||
* @GUC_LFD_TYPE_HOST_COMMENT: ASCII string containing comments
|
||||
* from the host/KMD.
|
||||
* @GUC_LFD_TYPE_TIMESTAMP_ANCHOR: A timestamp anchor, to convert
|
||||
* between host and GuC timestamp.
|
||||
* @GUC_LFD_TYPE_TIMESTAMP_ANCHOR_CONFIG: Timestamp anchor
|
||||
* configuration, definition of timestamp frequency and bit width.
|
||||
* @GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END: End of this range for
|
||||
* optional KMD LFDs
|
||||
*/
|
||||
GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START = 0x6000,
|
||||
GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT = 0x6000,
|
||||
GUC_LFD_TYPE_HOST_COMMENT = 0x6001,
|
||||
GUC_LFD_TYPE_TIMESTAMP_ANCHOR = 0x6002,
|
||||
GUC_LFD_TYPE_TIMESTAMP_ANCHOR_CONFIG = 0x6003,
|
||||
GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END = 0x7FFF,
|
||||
|
||||
/*
|
||||
* @GUC_LFD_TYPE_RESERVED_RANGE_START: Start of reserved range
|
||||
* @GUC_LFD_TYPE_RESERVED_RANGE_END: End of reserved range
|
||||
*/
|
||||
GUC_LFD_TYPE_RESERVED_RANGE_START = 0x8000,
|
||||
GUC_LFD_TYPE_RESERVED_RANGE_END = 0xFFFF,
|
||||
};
|
||||
|
||||
/** enum guc_lfd_os_type - OS Type LFD-ID */
|
||||
enum guc_lfd_os_type {
|
||||
/** @GUC_LFD_OS_TYPE_OSID_WIN: Windows OS */
|
||||
GUC_LFD_OS_TYPE_OSID_WIN = 0x1,
|
||||
/** @GUC_LFD_OS_TYPE_OSID_LIN: Linux OS */
|
||||
GUC_LFD_OS_TYPE_OSID_LIN = 0x2,
|
||||
/** @GUC_LFD_OS_TYPE_OSID_VMW: VMWare OS */
|
||||
GUC_LFD_OS_TYPE_OSID_VMW = 0x3,
|
||||
/** @GUC_LFD_OS_TYPE_OSID_OTHER: Other */
|
||||
GUC_LFD_OS_TYPE_OSID_OTHER = 0x4,
|
||||
};
|
||||
|
||||
/** struct guc_lfd_data - A generic header structure for all LFD blocks */
|
||||
struct guc_lfd_data {
|
||||
/** @header: A 32 bits dword, contains multiple bit fields */
|
||||
u32 header;
|
||||
/* LFD type. See guc_lfd_type */
|
||||
#define GUC_LFD_DATA_HEADER_MASK_TYPE GENMASK(31, 16)
|
||||
#define GUC_LFD_DATA_HEADER_MASK_MAGIC GENMASK(15, 0)
|
||||
|
||||
/** @data_count: Number of dwords the `data` field contains. */
|
||||
u32 data_count;
|
||||
/** @data: Data defined by GUC_LFD_DATA_HEADER_MASK_TYPE */
|
||||
u32 data[] __counted_by(data_count);
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct guc_lfd_data_log_events_buf - GuC Log Events Buffer.
|
||||
* This is optional fw LFD data
|
||||
*/
|
||||
struct guc_lfd_data_log_events_buf {
|
||||
/**
|
||||
* @log_events_format_version: version of GuC log format of buffer
|
||||
*/
|
||||
u32 log_events_format_version;
|
||||
/**
|
||||
* @log_event: The log event data.
|
||||
* Size in dwords is LFD block size - 1.
|
||||
*/
|
||||
u32 log_event[];
|
||||
} __packed;
|
||||
|
||||
/** struct guc_lfd_data_os_info - OS Version Information. */
|
||||
struct guc_lfd_data_os_info {
|
||||
/**
|
||||
* @os_id: enum values to identify the OS brand.
|
||||
* See guc_lfd_os_type for the range of types
|
||||
*/
|
||||
u32 os_id;
|
||||
/**
|
||||
* @build_version: ASCII string containing OS build version
|
||||
* information based on os_id. String is padded with null
|
||||
* characters to ensure its DWORD aligned.
|
||||
* Size in dwords is LFD block size - 1.
|
||||
*/
|
||||
char build_version[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct guc_logfile_header - Header of GuC Log Streaming-LFD-File Format.
|
||||
* This structure encapsulates the layout of the guc-log-file format
|
||||
*/
|
||||
struct guc_lfd_file_header {
|
||||
/**
|
||||
* @magic: A magic number set by producer of a GuC log file to
|
||||
* identify that file is a valid guc-log-file containing a stream
|
||||
* of LFDs.
|
||||
*/
|
||||
u64 magic;
|
||||
/** @version: Version of this file format layout */
|
||||
u32 version;
|
||||
#define GUC_LFD_FILE_HEADER_VERSION_MASK_MAJOR GENMASK(31, 16)
|
||||
#define GUC_LFD_FILE_HEADER_VERSION_MASK_MINOR GENMASK(15, 0)
|
||||
|
||||
/** @stream: A stream of one or more guc_lfd_data LFD blocks
|
||||
*/
|
||||
u32 stream[];
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
77
drivers/gpu/drm/xe/abi/guc_lic_abi.h
Normal file
77
drivers/gpu/drm/xe/abi/guc_lic_abi.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _ABI_GUC_LIC_ABI_H_
|
||||
#define _ABI_GUC_LIC_ABI_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* enum guc_lic_type - Log Init Config KLV IDs.
|
||||
*/
|
||||
enum guc_lic_type {
|
||||
/**
|
||||
* @GUC_LIC_TYPE_GUC_SW_VERSION: GuC firmware version. Value
|
||||
* is a 32 bit number represented by guc_sw_version.
|
||||
*/
|
||||
GUC_LIC_TYPE_GUC_SW_VERSION = 0x1,
|
||||
/**
|
||||
* @GUC_LIC_TYPE_GUC_DEVICE_ID: GuC device id. Value is a 32
|
||||
* bit.
|
||||
*/
|
||||
GUC_LIC_TYPE_GUC_DEVICE_ID = 0x2,
|
||||
/**
|
||||
* @GUC_LIC_TYPE_TSC_FREQUENCY: GuC timestamp counter
|
||||
* frequency. Value is a 32 bit number representing frequency in
|
||||
* kHz. This timestamp is utilized in log entries, timer and
|
||||
* for engine utilization tracking.
|
||||
*/
|
||||
GUC_LIC_TYPE_TSC_FREQUENCY = 0x3,
|
||||
/**
|
||||
* @GUC_LIC_TYPE_GMD_ID: HW GMD ID. Value is a 32 bit number
|
||||
* representing graphics, media and display HW architecture IDs.
|
||||
*/
|
||||
GUC_LIC_TYPE_GMD_ID = 0x4,
|
||||
/**
|
||||
* @GUC_LIC_TYPE_BUILD_PLATFORM_ID: GuC build platform ID.
|
||||
* Value is 32 bits.
|
||||
*/
|
||||
GUC_LIC_TYPE_BUILD_PLATFORM_ID = 0x5,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct guc_lic - GuC LIC (Log-Init-Config) structure.
|
||||
*
|
||||
* This is populated by the GUC at log init time and is located in the log
|
||||
* buffer memory allocation.
|
||||
*/
|
||||
struct guc_lic {
|
||||
/**
|
||||
* @magic: A magic number set by GuC to identify that this
|
||||
* structure contains valid information: magic = GUC_LIC_MAGIC.
|
||||
*/
|
||||
u32 magic;
|
||||
#define GUC_LIC_MAGIC 0x8086900D
|
||||
/**
|
||||
* @version: The version of the this structure.
|
||||
* Major and minor version number are represented as bit fields.
|
||||
*/
|
||||
u32 version;
|
||||
#define GUC_LIC_VERSION_MASK_MAJOR GENMASK(31, 16)
|
||||
#define GUC_LIC_VERSION_MASK_MINOR GENMASK(15, 0)
|
||||
|
||||
#define GUC_LIC_VERSION_MAJOR 1u
|
||||
#define GUC_LIC_VERSION_MINOR 0u
|
||||
|
||||
/** @data_count: Number of dwords the `data` array contains. */
|
||||
u32 data_count;
|
||||
/**
|
||||
* @data: Array of dwords representing a list of LIC KLVs of
|
||||
* type guc_klv_generic with keys represented by guc_lic_type
|
||||
*/
|
||||
u32 data[] __counted_by(data_count);
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
@@ -8,11 +8,45 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* DOC: GuC Log buffer Layout
|
||||
*
|
||||
* The in-memory log buffer layout is as follows::
|
||||
*
|
||||
* +===============================+ 0000h
|
||||
* | Crash dump state header | ^
|
||||
* +-------------------------------+ 32B |
|
||||
* | Debug state header | |
|
||||
* +-------------------------------+ 64B 4KB
|
||||
* | Capture state header | |
|
||||
* +-------------------------------+ 96B |
|
||||
* | | v
|
||||
* +===============================+ <--- EVENT_DATA_OFFSET
|
||||
* | Event logs(raw data) | ^
|
||||
* | | |
|
||||
* | | EVENT_DATA_BUFFER_SIZE
|
||||
* | | |
|
||||
* | | v
|
||||
* +===============================+ <--- CRASH_DUMP_OFFSET
|
||||
* | Crash Dump(raw data) | ^
|
||||
* | | |
|
||||
* | | CRASH_DUMP_BUFFER_SIZE
|
||||
* | | |
|
||||
* | | v
|
||||
* +===============================+ <--- STATE_CAPTURE_OFFSET
|
||||
* | Error state capture(raw data) | ^
|
||||
* | | |
|
||||
* | | STATE_CAPTURE_BUFFER_SIZE
|
||||
* | | |
|
||||
* | | v
|
||||
* +===============================+ Total: GUC_LOG_SIZE
|
||||
*/
|
||||
|
||||
/* GuC logging buffer types */
|
||||
enum guc_log_buffer_type {
|
||||
GUC_LOG_BUFFER_CRASH_DUMP,
|
||||
GUC_LOG_BUFFER_DEBUG,
|
||||
GUC_LOG_BUFFER_CAPTURE,
|
||||
enum guc_log_type {
|
||||
GUC_LOG_TYPE_EVENT_DATA,
|
||||
GUC_LOG_TYPE_CRASH_DUMP,
|
||||
GUC_LOG_TYPE_STATE_CAPTURE,
|
||||
};
|
||||
|
||||
#define GUC_LOG_BUFFER_TYPE_MAX 3
|
||||
|
||||
@@ -210,10 +210,11 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
|
||||
/* TODO: Consider sharing framebuffer mapping?
|
||||
* embed i915_vma inside intel_framebuffer
|
||||
*/
|
||||
xe_pm_runtime_get_noresume(xe);
|
||||
ret = mutex_lock_interruptible(&ggtt->lock);
|
||||
guard(xe_pm_runtime_noresume)(xe);
|
||||
ACQUIRE(mutex_intr, lock)(&ggtt->lock);
|
||||
ret = ACQUIRE_ERR(mutex_intr, &lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
align = XE_PAGE_SIZE;
|
||||
if (xe_bo_is_vram(bo) && ggtt->flags & XE_GGTT_FLAGS_64K)
|
||||
@@ -223,15 +224,13 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
|
||||
vma->node = bo->ggtt_node[tile0->id];
|
||||
} else if (view->type == I915_GTT_VIEW_NORMAL) {
|
||||
vma->node = xe_ggtt_node_init(ggtt);
|
||||
if (IS_ERR(vma->node)) {
|
||||
ret = PTR_ERR(vma->node);
|
||||
goto out_unlock;
|
||||
}
|
||||
if (IS_ERR(vma->node))
|
||||
return PTR_ERR(vma->node);
|
||||
|
||||
ret = xe_ggtt_node_insert_locked(vma->node, xe_bo_size(bo), align, 0);
|
||||
if (ret) {
|
||||
xe_ggtt_node_fini(vma->node);
|
||||
goto out_unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
xe_ggtt_map_bo(ggtt, vma->node, bo, xe->pat.idx[XE_CACHE_NONE]);
|
||||
@@ -245,13 +244,13 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
|
||||
vma->node = xe_ggtt_node_init(ggtt);
|
||||
if (IS_ERR(vma->node)) {
|
||||
ret = PTR_ERR(vma->node);
|
||||
goto out_unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = xe_ggtt_node_insert_locked(vma->node, size, align, 0);
|
||||
if (ret) {
|
||||
xe_ggtt_node_fini(vma->node);
|
||||
goto out_unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ggtt_ofs = vma->node->base.start;
|
||||
@@ -265,10 +264,6 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
|
||||
rot_info->plane[i].dst_stride);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ggtt->lock);
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,6 @@ static bool intel_hdcp_gsc_check_status(struct drm_device *drm)
|
||||
struct xe_tile *tile = xe_device_get_root_tile(xe);
|
||||
struct xe_gt *gt = tile->media_gt;
|
||||
struct xe_gsc *gsc = >->uc.gsc;
|
||||
bool ret = true;
|
||||
unsigned int fw_ref;
|
||||
|
||||
if (!gsc || !xe_uc_fw_is_enabled(&gsc->fw)) {
|
||||
drm_dbg_kms(&xe->drm,
|
||||
@@ -47,22 +45,15 @@ static bool intel_hdcp_gsc_check_status(struct drm_device *drm)
|
||||
return false;
|
||||
}
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
|
||||
if (!fw_ref) {
|
||||
guard(xe_pm_runtime)(xe);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
|
||||
if (!fw_ref.domains) {
|
||||
drm_dbg_kms(&xe->drm,
|
||||
"failed to get forcewake to check proxy status\n");
|
||||
ret = false;
|
||||
goto out;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!xe_gsc_proxy_init_done(gsc))
|
||||
ret = false;
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
return ret;
|
||||
return xe_gsc_proxy_init_done(gsc);
|
||||
}
|
||||
|
||||
/*This function helps allocate memory for the command that we will send to gsc cs */
|
||||
@@ -168,17 +159,15 @@ static ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_contex
|
||||
u32 addr_out_off, addr_in_wr_off = 0;
|
||||
int ret, tries = 0;
|
||||
|
||||
if (msg_in_len > max_msg_size || msg_out_len > max_msg_size) {
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
if (msg_in_len > max_msg_size || msg_out_len > max_msg_size)
|
||||
return -ENOSPC;
|
||||
|
||||
msg_size_in = msg_in_len + HDCP_GSC_HEADER_SIZE;
|
||||
msg_size_out = msg_out_len + HDCP_GSC_HEADER_SIZE;
|
||||
addr_out_off = PAGE_SIZE;
|
||||
|
||||
host_session_id = xe_gsc_create_host_session_id();
|
||||
xe_pm_runtime_get_noresume(xe);
|
||||
guard(xe_pm_runtime_noresume)(xe);
|
||||
addr_in_wr_off = xe_gsc_emit_header(xe, &gsc_context->hdcp_bo->vmap,
|
||||
addr_in_wr_off, HECI_MEADDRESS_HDCP,
|
||||
host_session_id, msg_in_len);
|
||||
@@ -203,14 +192,12 @@ static ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_contex
|
||||
} while (++tries < 20);
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
xe_map_memcpy_from(xe, msg_out, &gsc_context->hdcp_bo->vmap,
|
||||
addr_out_off + HDCP_GSC_HEADER_SIZE,
|
||||
msg_out_len);
|
||||
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
|
||||
#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|((len)-2))
|
||||
|
||||
#define PIPE_CONTROL0_QUEUE_DRAIN_MODE BIT(12)
|
||||
#define PIPE_CONTROL0_L3_READ_ONLY_CACHE_INVALIDATE BIT(10) /* gen12 */
|
||||
#define PIPE_CONTROL0_HDC_PIPELINE_FLUSH BIT(9) /* gen12 */
|
||||
|
||||
|
||||
@@ -227,6 +227,9 @@
|
||||
|
||||
#define MIRROR_FUSE1 XE_REG(0x911c)
|
||||
|
||||
#define FUSE2 XE_REG(0x9120)
|
||||
#define PRODUCTION_HW REG_BIT(2)
|
||||
|
||||
#define MIRROR_L3BANK_ENABLE XE_REG(0x9130)
|
||||
#define XE3_L3BANK_ENABLE REG_GENMASK(31, 0)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define XELPG_GGTT_PTE_PAT0 BIT_ULL(52)
|
||||
#define XELPG_GGTT_PTE_PAT1 BIT_ULL(53)
|
||||
|
||||
#define XE_PTE_ADDR_MASK GENMASK_ULL(51, 12)
|
||||
#define GGTT_PTE_VFID GENMASK_ULL(11, 2)
|
||||
|
||||
#define GUC_GGTT_TOP 0xFEE00000
|
||||
|
||||
@@ -90,6 +90,9 @@
|
||||
#define GUC_SEND_INTERRUPT XE_REG(0xc4c8)
|
||||
#define GUC_SEND_TRIGGER REG_BIT(0)
|
||||
|
||||
#define GUC_INTR_CHICKEN XE_REG(0xc50c)
|
||||
#define DISABLE_SIGNALING_ENGINES REG_BIT(1)
|
||||
|
||||
#define GUC_BCS_RCS_IER XE_REG(0xc550)
|
||||
#define GUC_VCS2_VCS1_IER XE_REG(0xc554)
|
||||
#define GUC_WD_VECS_IER XE_REG(0xc558)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define GU_MISC_IRQ REG_BIT(29)
|
||||
#define ERROR_IRQ(x) REG_BIT(26 + (x))
|
||||
#define DISPLAY_IRQ REG_BIT(16)
|
||||
#define SOC_H2DMEMINT_IRQ REG_BIT(13)
|
||||
#define I2C_IRQ REG_BIT(12)
|
||||
#define GT_DW_IRQ(x) REG_BIT(x)
|
||||
|
||||
|
||||
21
drivers/gpu/drm/xe/regs/xe_mert_regs.h
Normal file
21
drivers/gpu/drm/xe/regs/xe_mert_regs.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2025 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _XE_MERT_REGS_H_
|
||||
#define _XE_MERT_REGS_H_
|
||||
|
||||
#include "regs/xe_reg_defs.h"
|
||||
|
||||
#define MERT_LMEM_CFG XE_REG(0x1448b0)
|
||||
|
||||
#define MERT_TLB_CT_INTR_ERR_ID_PORT XE_REG(0x145190)
|
||||
#define MERT_TLB_CT_VFID_MASK REG_GENMASK(16, 9)
|
||||
#define MERT_TLB_CT_ERROR_MASK REG_GENMASK(5, 0)
|
||||
#define MERT_TLB_CT_LMTT_FAULT 0x05
|
||||
|
||||
#define MERT_TLB_INV_DESC_A XE_REG(0x14cf7c)
|
||||
#define MERT_TLB_INV_DESC_A_VALID REG_BIT(0)
|
||||
|
||||
#endif /* _XE_MERT_REGS_H_ */
|
||||
@@ -100,4 +100,21 @@
|
||||
#define OAM_COMPRESSION_T3_CONTROL XE_REG(0x1c2e00)
|
||||
#define OAM_LAT_MEASURE_ENABLE REG_BIT(4)
|
||||
|
||||
/* Actual address is MEDIA_GT_GSI_OFFSET + the base addr below */
|
||||
#define XE_OAM_SAG_BASE 0x13000
|
||||
#define XE_OAM_SCMI_0_BASE 0x14000
|
||||
#define XE_OAM_SCMI_1_BASE 0x14800
|
||||
#define XE_OAM_SAG_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SAG_BASE)
|
||||
#define XE_OAM_SCMI_0_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SCMI_0_BASE)
|
||||
#define XE_OAM_SCMI_1_BASE_ADJ (MEDIA_GT_GSI_OFFSET + XE_OAM_SCMI_1_BASE)
|
||||
|
||||
#define OAMERT_CONTROL XE_REG(0x1453a0)
|
||||
#define OAMERT_DEBUG XE_REG(0x1453a4)
|
||||
#define OAMERT_STATUS XE_REG(0x1453a8)
|
||||
#define OAMERT_HEAD_POINTER XE_REG(0x1453ac)
|
||||
#define OAMERT_TAIL_POINTER XE_REG(0x1453b0)
|
||||
#define OAMERT_BUFFER XE_REG(0x1453b4)
|
||||
#define OAMERT_CONTEXT_CONTROL XE_REG(0x1453c8)
|
||||
#define OAMERT_MMIO_TRG XE_REG(0x1453cc)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -78,6 +78,24 @@ static void pick_arg_example(struct kunit *test)
|
||||
#undef buz
|
||||
}
|
||||
|
||||
static void if_args_example(struct kunit *test)
|
||||
{
|
||||
enum { Z = 1, Q };
|
||||
|
||||
#define foo X, Y
|
||||
#define bar IF_ARGS(Z, Q, foo)
|
||||
#define buz IF_ARGS(Z, Q, DROP_FIRST_ARG(FIRST_ARG(foo)))
|
||||
|
||||
KUNIT_EXPECT_EQ(test, bar, Z);
|
||||
KUNIT_EXPECT_EQ(test, buz, Q);
|
||||
KUNIT_EXPECT_STREQ(test, __stringify(bar), "Z");
|
||||
KUNIT_EXPECT_STREQ(test, __stringify(buz), "Q");
|
||||
|
||||
#undef foo
|
||||
#undef bar
|
||||
#undef buz
|
||||
}
|
||||
|
||||
static void sep_comma_example(struct kunit *test)
|
||||
{
|
||||
#define foo(f) f(X) f(Y) f(Z) f(Q)
|
||||
@@ -198,6 +216,40 @@ static void last_arg_test(struct kunit *test)
|
||||
KUNIT_EXPECT_STREQ(test, __stringify(LAST_ARG(MAX_ARGS)), "-12");
|
||||
}
|
||||
|
||||
static void if_args_test(struct kunit *test)
|
||||
{
|
||||
bool with_args = true;
|
||||
bool no_args = false;
|
||||
enum { X = 100 };
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, IF_ARGS(true, false, FOO_ARGS));
|
||||
KUNIT_EXPECT_FALSE(test, IF_ARGS(true, false, NO_ARGS));
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, CONCATENATE(IF_ARGS(with, no, FOO_ARGS), _args));
|
||||
KUNIT_EXPECT_FALSE(test, CONCATENATE(IF_ARGS(with, no, NO_ARGS), _args));
|
||||
|
||||
KUNIT_EXPECT_STREQ(test, __stringify(IF_ARGS(yes, no, FOO_ARGS)), "yes");
|
||||
KUNIT_EXPECT_STREQ(test, __stringify(IF_ARGS(yes, no, NO_ARGS)), "no");
|
||||
|
||||
KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, FOO_ARGS), -1, FOO_ARGS), 4);
|
||||
KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, FOO_ARGS), -1, NO_ARGS), -1);
|
||||
KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, NO_ARGS), -1, FOO_ARGS), 0);
|
||||
KUNIT_EXPECT_EQ(test, IF_ARGS(CALL_ARGS(COUNT_ARGS, NO_ARGS), -1, NO_ARGS), -1);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
CALL_ARGS(FIRST_ARG,
|
||||
CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, FOO_ARGS), _ARGS)), X);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
CALL_ARGS(FIRST_ARG,
|
||||
CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, NO_ARGS), _ARGS)), -1);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
CALL_ARGS(COUNT_ARGS,
|
||||
CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, FOO_ARGS), _ARGS)), 4);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
CALL_ARGS(COUNT_ARGS,
|
||||
CALL_ARGS(CONCATENATE, IF_ARGS(FOO, MAX, NO_ARGS), _ARGS)), 12);
|
||||
}
|
||||
|
||||
static struct kunit_case args_tests[] = {
|
||||
KUNIT_CASE(count_args_test),
|
||||
KUNIT_CASE(call_args_example),
|
||||
@@ -209,6 +261,8 @@ static struct kunit_case args_tests[] = {
|
||||
KUNIT_CASE(last_arg_example),
|
||||
KUNIT_CASE(last_arg_test),
|
||||
KUNIT_CASE(pick_arg_example),
|
||||
KUNIT_CASE(if_args_example),
|
||||
KUNIT_CASE(if_args_test),
|
||||
KUNIT_CASE(sep_comma_example),
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -185,8 +185,7 @@ static int ccs_test_run_device(struct xe_device *xe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_tile(tile, xe, id) {
|
||||
/* For igfx run only for primary tile */
|
||||
if (!IS_DGFX(xe) && id > 0)
|
||||
@@ -194,8 +193,6 @@ static int ccs_test_run_device(struct xe_device *xe)
|
||||
ccs_test_run_tile(xe, tile, test);
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -356,13 +353,10 @@ static int evict_test_run_device(struct xe_device *xe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_tile(tile, xe, id)
|
||||
evict_test_run_tile(xe, tile, test);
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ static int dma_buf_run_device(struct xe_device *xe)
|
||||
const struct dma_buf_test_params *params;
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for (params = test_params; params->mem_mask; ++params) {
|
||||
struct dma_buf_test_params p = *params;
|
||||
|
||||
@@ -274,7 +274,6 @@ static int dma_buf_run_device(struct xe_device *xe)
|
||||
test->priv = &p;
|
||||
xe_test_dmabuf_import_same_driver(xe);
|
||||
}
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
/* A non-zero return would halt iteration over driver devices */
|
||||
return 0;
|
||||
|
||||
@@ -344,8 +344,7 @@ static int migrate_test_run_device(struct xe_device *xe)
|
||||
struct xe_tile *tile;
|
||||
int id;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_tile(tile, xe, id) {
|
||||
struct xe_migrate *m = tile->migrate;
|
||||
struct drm_exec *exec = XE_VALIDATION_OPT_OUT;
|
||||
@@ -356,8 +355,6 @@ static int migrate_test_run_device(struct xe_device *xe)
|
||||
xe_vm_unlock(m->q->vm);
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -759,13 +756,10 @@ static int validate_ccs_test_run_device(struct xe_device *xe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_tile(tile, xe, id)
|
||||
validate_ccs_test_run_tile(xe, tile, test);
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,14 +43,12 @@ static void read_l3cc_table(struct xe_gt *gt,
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
u32 l3cc, l3cc_expected;
|
||||
unsigned int fw_ref, i;
|
||||
unsigned int i;
|
||||
u32 reg_val;
|
||||
|
||||
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)) {
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
|
||||
KUNIT_FAIL_AND_ABORT(test, "Forcewake Failed.\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < info->num_mocs_regs; i++) {
|
||||
if (!(i & 1)) {
|
||||
@@ -74,7 +72,6 @@ static void read_l3cc_table(struct xe_gt *gt,
|
||||
KUNIT_EXPECT_EQ_MSG(test, l3cc_expected, l3cc,
|
||||
"l3cc idx=%u has incorrect val.\n", i);
|
||||
}
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
static void read_mocs_table(struct xe_gt *gt,
|
||||
@@ -82,14 +79,14 @@ static void read_mocs_table(struct xe_gt *gt,
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
u32 mocs, mocs_expected;
|
||||
unsigned int fw_ref, i;
|
||||
unsigned int i;
|
||||
u32 reg_val;
|
||||
|
||||
KUNIT_EXPECT_TRUE_MSG(test, info->unused_entries_index,
|
||||
"Unused entries index should have been defined\n");
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
KUNIT_ASSERT_NE_MSG(test, fw_ref, 0, "Forcewake Failed.\n");
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
KUNIT_ASSERT_NE_MSG(test, fw_ref.domains, 0, "Forcewake Failed.\n");
|
||||
|
||||
for (i = 0; i < info->num_mocs_regs; i++) {
|
||||
if (regs_are_mcr(gt))
|
||||
@@ -106,8 +103,6 @@ static void read_mocs_table(struct xe_gt *gt,
|
||||
KUNIT_EXPECT_EQ_MSG(test, mocs_expected, mocs,
|
||||
"mocs reg 0x%x has incorrect val.\n", i);
|
||||
}
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
static int mocs_kernel_test_run_device(struct xe_device *xe)
|
||||
@@ -120,8 +115,7 @@ static int mocs_kernel_test_run_device(struct xe_device *xe)
|
||||
unsigned int flags;
|
||||
int id;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_gt(gt, xe, id) {
|
||||
flags = live_mocs_init(&mocs, gt);
|
||||
if (flags & HAS_GLOBAL_MOCS)
|
||||
@@ -130,8 +124,6 @@ static int mocs_kernel_test_run_device(struct xe_device *xe)
|
||||
read_l3cc_table(gt, &mocs.table);
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -155,8 +147,7 @@ static int mocs_reset_test_run_device(struct xe_device *xe)
|
||||
int id;
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_gt(gt, xe, id) {
|
||||
flags = live_mocs_init(&mocs, gt);
|
||||
kunit_info(test, "mocs_reset_test before reset\n");
|
||||
@@ -174,8 +165,6 @@ static int mocs_reset_test_run_device(struct xe_device *xe)
|
||||
read_l3cc_table(gt, &mocs.table);
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -121,6 +121,33 @@
|
||||
#define PICK_ARG11(args...) PICK_ARG10(DROP_FIRST_ARG(args))
|
||||
#define PICK_ARG12(args...) PICK_ARG11(DROP_FIRST_ARG(args))
|
||||
|
||||
/**
|
||||
* IF_ARGS() - Make selection based on optional argument list.
|
||||
* @then: token to return if arguments are present
|
||||
* @else: token to return if arguments are empty
|
||||
* @...: arguments to check (optional)
|
||||
*
|
||||
* This macro allows to select a token based on the presence of the argument list.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define foo X, Y
|
||||
* #define bar IF_ARGS(Z, Q, foo)
|
||||
* #define buz IF_ARGS(Z, Q, DROP_FIRST_ARG(FIRST_ARG(foo)))
|
||||
*
|
||||
* With above definitions bar expands to Z while buz expands to Q.
|
||||
*/
|
||||
#if defined(CONFIG_CC_IS_CLANG) || GCC_VERSION >= 100100
|
||||
#define IF_ARGS(then, else, ...) FIRST_ARG(__VA_OPT__(then,) else)
|
||||
#else
|
||||
#define IF_ARGS(then, else, ...) _IF_ARGS(then, else, CALL_ARGS(FIRST_ARG, __VA_ARGS__))
|
||||
#define _IF_ARGS(then, else, ...) __IF_ARGS(then, else, CALL_ARGS(COUNT_ARGS, __VA_ARGS__))
|
||||
#define __IF_ARGS(then, else, n) ___IF_ARGS(then, else, CALL_ARGS(CONCATENATE, ___IF_ARG, n))
|
||||
#define ___IF_ARGS(then, else, if) CALL_ARGS(if, then, else)
|
||||
#define ___IF_ARG1(then, else) then
|
||||
#define ___IF_ARG0(then, else) else
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ARGS_SEP_COMMA - Definition of a comma character.
|
||||
*
|
||||
|
||||
@@ -516,8 +516,7 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
|
||||
* non-coherent and require a CPU:WC mapping.
|
||||
*/
|
||||
if ((!bo->cpu_caching && bo->flags & XE_BO_FLAG_SCANOUT) ||
|
||||
(xe->info.graphics_verx100 >= 1270 &&
|
||||
bo->flags & XE_BO_FLAG_PAGETABLE))
|
||||
(!xe->info.has_cached_pt && bo->flags & XE_BO_FLAG_PAGETABLE))
|
||||
caching = ttm_write_combined;
|
||||
}
|
||||
|
||||
@@ -2026,13 +2025,9 @@ static int xe_bo_vm_access(struct vm_area_struct *vma, unsigned long addr,
|
||||
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;
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return ttm_bo_vm_access(vma, addr, buf, len, write);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3176,7 +3171,8 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
if (XE_IOCTL_DBG(xe, args->flags &
|
||||
~(DRM_XE_GEM_CREATE_FLAG_DEFER_BACKING |
|
||||
DRM_XE_GEM_CREATE_FLAG_SCANOUT |
|
||||
DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM)))
|
||||
DRM_XE_GEM_CREATE_FLAG_NEEDS_VISIBLE_VRAM |
|
||||
DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION)))
|
||||
return -EINVAL;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, args->handle))
|
||||
@@ -3198,6 +3194,12 @@ int xe_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
if (args->flags & DRM_XE_GEM_CREATE_FLAG_SCANOUT)
|
||||
bo_flags |= XE_BO_FLAG_SCANOUT;
|
||||
|
||||
if (args->flags & DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION) {
|
||||
if (XE_IOCTL_DBG(xe, GRAPHICS_VER(xe) < 20))
|
||||
return -EOPNOTSUPP;
|
||||
bo_flags |= XE_BO_FLAG_NO_COMPRESSION;
|
||||
}
|
||||
|
||||
bo_flags |= args->placement << (ffs(XE_BO_FLAG_SYSTEM) - 1);
|
||||
|
||||
/* CCS formats need physical placement at a 64K alignment in VRAM. */
|
||||
@@ -3519,8 +3521,12 @@ bool xe_bo_needs_ccs_pages(struct xe_bo *bo)
|
||||
* Compression implies coh_none, therefore we know for sure that WB
|
||||
* memory can't currently use compression, which is likely one of the
|
||||
* common cases.
|
||||
* Additionally, userspace may explicitly request no compression via the
|
||||
* DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION flag, which should also disable
|
||||
* CCS usage.
|
||||
*/
|
||||
if (bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB)
|
||||
if (bo->cpu_caching == DRM_XE_GEM_CPU_CACHING_WB ||
|
||||
bo->flags & XE_BO_FLAG_NO_COMPRESSION)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#define XE_BO_FLAG_GGTT3 BIT(23)
|
||||
#define XE_BO_FLAG_CPU_ADDR_MIRROR BIT(24)
|
||||
#define XE_BO_FLAG_FORCE_USER_VRAM BIT(25)
|
||||
#define XE_BO_FLAG_NO_COMPRESSION BIT(26)
|
||||
|
||||
/* this one is trigger internally only */
|
||||
#define XE_BO_FLAG_INTERNAL_TEST BIT(30)
|
||||
|
||||
@@ -68,7 +68,7 @@ static int info(struct seq_file *m, void *data)
|
||||
struct xe_gt *gt;
|
||||
u8 id;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
|
||||
drm_printf(&p, "graphics_verx100 %d\n", xe->info.graphics_verx100);
|
||||
drm_printf(&p, "media_verx100 %d\n", xe->info.media_verx100);
|
||||
@@ -93,9 +93,10 @@ static int info(struct seq_file *m, void *data)
|
||||
xe_force_wake_ref(gt_to_fw(gt), XE_FW_GT));
|
||||
drm_printf(&p, "gt%d engine_mask 0x%llx\n", id,
|
||||
gt->info.engine_mask);
|
||||
drm_printf(&p, "gt%d multi_queue_engine_class_mask 0x%x\n", id,
|
||||
gt->info.multi_queue_engine_class_mask);
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -110,9 +111,8 @@ static int sriov_info(struct seq_file *m, void *data)
|
||||
|
||||
static int workarounds(struct xe_device *xe, struct drm_printer *p)
|
||||
{
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
xe_wa_device_dump(xe, p);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data)
|
||||
|
||||
xe = node_to_xe(m->private);
|
||||
p = drm_seq_file_printer(m);
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
mmio = xe_root_tile_mmio(xe);
|
||||
static const struct {
|
||||
u32 offset;
|
||||
@@ -151,7 +151,6 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data)
|
||||
for (int i = 0; i < ARRAY_SIZE(residencies); i++)
|
||||
read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p);
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -163,7 +162,7 @@ static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data)
|
||||
|
||||
xe = node_to_xe(m->private);
|
||||
p = drm_seq_file_printer(m);
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
mmio = xe_root_tile_mmio(xe);
|
||||
|
||||
static const struct {
|
||||
@@ -178,7 +177,6 @@ static int dgfx_pcie_link_residencies_show(struct seq_file *m, void *data)
|
||||
for (int i = 0; i < ARRAY_SIZE(residencies); i++)
|
||||
read_residency_counter(xe, mmio, residencies[i].offset, residencies[i].name, &p);
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -277,16 +275,14 @@ static ssize_t wedged_mode_set(struct file *f, const char __user *ubuf,
|
||||
|
||||
xe->wedged.mode = wedged_mode;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
for_each_gt(gt, xe, id) {
|
||||
ret = xe_guc_ads_scheduler_policy_toggle_reset(>->uc.guc.ads);
|
||||
if (ret) {
|
||||
xe_gt_err(gt, "Failed to update GuC ADS scheduler policy. GuC may still cause engine reset even with wedged_mode=2\n");
|
||||
xe_pm_runtime_put(xe);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -297,6 +293,39 @@ static const struct file_operations wedged_mode_fops = {
|
||||
.write = wedged_mode_set,
|
||||
};
|
||||
|
||||
static ssize_t page_reclaim_hw_assist_show(struct file *f, char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct xe_device *xe = file_inode(f)->i_private;
|
||||
char buf[8];
|
||||
int len;
|
||||
|
||||
len = scnprintf(buf, sizeof(buf), "%d\n", xe->info.has_page_reclaim_hw_assist);
|
||||
return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t page_reclaim_hw_assist_set(struct file *f, const char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct xe_device *xe = file_inode(f)->i_private;
|
||||
bool val;
|
||||
ssize_t ret;
|
||||
|
||||
ret = kstrtobool_from_user(ubuf, size, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xe->info.has_page_reclaim_hw_assist = val;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static const struct file_operations page_reclaim_hw_assist_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = page_reclaim_hw_assist_show,
|
||||
.write = page_reclaim_hw_assist_set,
|
||||
};
|
||||
|
||||
static ssize_t atomic_svm_timeslice_ms_show(struct file *f, char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
@@ -332,6 +361,74 @@ static const struct file_operations atomic_svm_timeslice_ms_fops = {
|
||||
.write = atomic_svm_timeslice_ms_set,
|
||||
};
|
||||
|
||||
static ssize_t min_run_period_lr_ms_show(struct file *f, char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct xe_device *xe = file_inode(f)->i_private;
|
||||
char buf[32];
|
||||
int len = 0;
|
||||
|
||||
len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_lr_ms);
|
||||
|
||||
return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t min_run_period_lr_ms_set(struct file *f, const char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct xe_device *xe = file_inode(f)->i_private;
|
||||
u32 min_run_period_lr_ms;
|
||||
ssize_t ret;
|
||||
|
||||
ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_lr_ms);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xe->min_run_period_lr_ms = min_run_period_lr_ms;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static const struct file_operations min_run_period_lr_ms_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = min_run_period_lr_ms_show,
|
||||
.write = min_run_period_lr_ms_set,
|
||||
};
|
||||
|
||||
static ssize_t min_run_period_pf_ms_show(struct file *f, char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct xe_device *xe = file_inode(f)->i_private;
|
||||
char buf[32];
|
||||
int len = 0;
|
||||
|
||||
len = scnprintf(buf, sizeof(buf), "%d\n", xe->min_run_period_pf_ms);
|
||||
|
||||
return simple_read_from_buffer(ubuf, size, pos, buf, len);
|
||||
}
|
||||
|
||||
static ssize_t min_run_period_pf_ms_set(struct file *f, const char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
struct xe_device *xe = file_inode(f)->i_private;
|
||||
u32 min_run_period_pf_ms;
|
||||
ssize_t ret;
|
||||
|
||||
ret = kstrtouint_from_user(ubuf, size, 0, &min_run_period_pf_ms);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xe->min_run_period_pf_ms = min_run_period_pf_ms;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static const struct file_operations min_run_period_pf_ms_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = min_run_period_pf_ms_show,
|
||||
.write = min_run_period_pf_ms_set,
|
||||
};
|
||||
|
||||
static ssize_t disable_late_binding_show(struct file *f, char __user *ubuf,
|
||||
size_t size, loff_t *pos)
|
||||
{
|
||||
@@ -375,7 +472,6 @@ void xe_debugfs_register(struct xe_device *xe)
|
||||
struct ttm_resource_manager *man;
|
||||
struct xe_tile *tile;
|
||||
struct xe_gt *gt;
|
||||
u32 mem_type;
|
||||
u8 tile_id;
|
||||
u8 id;
|
||||
|
||||
@@ -400,19 +496,22 @@ void xe_debugfs_register(struct xe_device *xe)
|
||||
debugfs_create_file("atomic_svm_timeslice_ms", 0600, root, xe,
|
||||
&atomic_svm_timeslice_ms_fops);
|
||||
|
||||
debugfs_create_file("min_run_period_lr_ms", 0600, root, xe,
|
||||
&min_run_period_lr_ms_fops);
|
||||
|
||||
debugfs_create_file("min_run_period_pf_ms", 0600, root, xe,
|
||||
&min_run_period_pf_ms_fops);
|
||||
|
||||
debugfs_create_file("disable_late_binding", 0600, root, xe,
|
||||
&disable_late_binding_fops);
|
||||
|
||||
for (mem_type = XE_PL_VRAM0; mem_type <= XE_PL_VRAM1; ++mem_type) {
|
||||
man = ttm_manager_type(bdev, mem_type);
|
||||
|
||||
if (man) {
|
||||
char name[16];
|
||||
|
||||
snprintf(name, sizeof(name), "vram%d_mm", mem_type - XE_PL_VRAM0);
|
||||
ttm_resource_manager_create_debugfs(man, root, name);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Don't expose page reclaim configuration file if not supported by the
|
||||
* hardware initially.
|
||||
*/
|
||||
if (xe->info.has_page_reclaim_hw_assist)
|
||||
debugfs_create_file("page_reclaim_hw_assist", 0600, root, xe,
|
||||
&page_reclaim_hw_assist_fops);
|
||||
|
||||
man = ttm_manager_type(bdev, XE_PL_TT);
|
||||
ttm_resource_manager_create_debugfs(man, root, "gtt_mm");
|
||||
|
||||
@@ -276,7 +276,6 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
|
||||
struct xe_devcoredump_snapshot *ss = container_of(work, typeof(*ss), work);
|
||||
struct xe_devcoredump *coredump = container_of(ss, typeof(*coredump), snapshot);
|
||||
struct xe_device *xe = coredump_to_xe(coredump);
|
||||
unsigned int fw_ref;
|
||||
|
||||
/*
|
||||
* NB: Despite passing a GFP_ flags parameter here, more allocations are done
|
||||
@@ -287,15 +286,15 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
|
||||
xe_devcoredump_read, xe_devcoredump_free,
|
||||
XE_COREDUMP_TIMEOUT_JIFFIES);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
|
||||
/* keep going if fw fails as we still want to save the memory and SW data */
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(ss->gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL))
|
||||
xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
|
||||
xe_vm_snapshot_capture_delayed(ss->vm);
|
||||
xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
|
||||
xe_force_wake_put(gt_to_fw(ss->gt), fw_ref);
|
||||
xe_with_force_wake(fw_ref, gt_to_fw(ss->gt), XE_FORCEWAKE_ALL) {
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
|
||||
xe_gt_info(ss->gt, "failed to get forcewake for coredump capture\n");
|
||||
xe_vm_snapshot_capture_delayed(ss->vm);
|
||||
xe_guc_exec_queue_snapshot_capture_delayed(ss->ge);
|
||||
}
|
||||
|
||||
ss->read.chunk_position = 0;
|
||||
|
||||
@@ -306,7 +305,7 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
|
||||
ss->read.buffer = kvmalloc(XE_DEVCOREDUMP_CHUNK_MAX,
|
||||
GFP_USER);
|
||||
if (!ss->read.buffer)
|
||||
goto put_pm;
|
||||
return;
|
||||
|
||||
__xe_devcoredump_read(ss->read.buffer,
|
||||
XE_DEVCOREDUMP_CHUNK_MAX,
|
||||
@@ -314,15 +313,12 @@ static void xe_devcoredump_deferred_snap_work(struct work_struct *work)
|
||||
} else {
|
||||
ss->read.buffer = kvmalloc(ss->read.size, GFP_USER);
|
||||
if (!ss->read.buffer)
|
||||
goto put_pm;
|
||||
return;
|
||||
|
||||
__xe_devcoredump_read(ss->read.buffer, ss->read.size, 0,
|
||||
coredump);
|
||||
xe_devcoredump_snapshot_free(ss);
|
||||
}
|
||||
|
||||
put_pm:
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||
@@ -332,7 +328,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
|
||||
struct xe_guc *guc = exec_queue_to_guc(q);
|
||||
const char *process_name = "no process";
|
||||
unsigned int fw_ref;
|
||||
bool cookie;
|
||||
|
||||
ss->snapshot_time = ktime_get_real();
|
||||
@@ -348,10 +343,10 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||
ss->gt = q->gt;
|
||||
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
|
||||
|
||||
cookie = dma_fence_begin_signalling();
|
||||
|
||||
/* keep going if fw fails as we still want to save the memory and SW data */
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
|
||||
|
||||
cookie = dma_fence_begin_signalling();
|
||||
|
||||
ss->guc.log = xe_guc_log_snapshot_capture(&guc->log, true);
|
||||
ss->guc.ct = xe_guc_ct_snapshot_capture(&guc->ct);
|
||||
@@ -364,7 +359,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||
|
||||
queue_work(system_unbound_wq, &ss->work);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(q->gt), fw_ref);
|
||||
dma_fence_end_signalling(cookie);
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
|
||||
struct xe_exec_queue *q;
|
||||
unsigned long idx;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
|
||||
/*
|
||||
* No need for exec_queue.lock here as there is no contention for it
|
||||
@@ -177,15 +177,18 @@ static void xe_file_close(struct drm_device *dev, struct drm_file *file)
|
||||
xa_for_each(&xef->exec_queue.xa, idx, q) {
|
||||
if (q->vm && q->hwe->hw_engine_group)
|
||||
xe_hw_engine_group_del_exec_queue(q->hwe->hw_engine_group, q);
|
||||
xe_exec_queue_kill(q);
|
||||
|
||||
if (xe_exec_queue_is_multi_queue_primary(q))
|
||||
xe_exec_queue_group_kill_put(q->multi_queue.group);
|
||||
else
|
||||
xe_exec_queue_kill(q);
|
||||
|
||||
xe_exec_queue_put(q);
|
||||
}
|
||||
xa_for_each(&xef->vm.xa, idx, vm)
|
||||
xe_vm_close_and_put(vm);
|
||||
|
||||
xe_file_put(xef);
|
||||
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
static const struct drm_ioctl_desc xe_ioctls[] = {
|
||||
@@ -209,6 +212,8 @@ static const struct drm_ioctl_desc xe_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(XE_MADVISE, xe_vm_madvise_ioctl, DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(XE_VM_QUERY_MEM_RANGE_ATTRS, xe_vm_query_vmas_attrs_ioctl,
|
||||
DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(XE_EXEC_QUEUE_SET_PROPERTY, xe_exec_queue_set_property_ioctl,
|
||||
DRM_RENDER_ALLOW),
|
||||
};
|
||||
|
||||
static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
@@ -220,10 +225,10 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
if (xe_device_wedged(xe))
|
||||
return -ECANCELED;
|
||||
|
||||
ret = xe_pm_runtime_get_ioctl(xe);
|
||||
ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
|
||||
ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
|
||||
if (ret >= 0)
|
||||
ret = drm_ioctl(file, cmd, arg);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -238,10 +243,10 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
|
||||
if (xe_device_wedged(xe))
|
||||
return -ECANCELED;
|
||||
|
||||
ret = xe_pm_runtime_get_ioctl(xe);
|
||||
ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
|
||||
ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm);
|
||||
if (ret >= 0)
|
||||
ret = drm_compat_ioctl(file, cmd, arg);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -455,6 +460,7 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
|
||||
xe->info.revid = pdev->revision;
|
||||
xe->info.force_execlist = xe_modparam.force_execlist;
|
||||
xe->atomic_svm_timeslice_ms = 5;
|
||||
xe->min_run_period_lr_ms = 5;
|
||||
|
||||
err = xe_irq_init(xe);
|
||||
if (err)
|
||||
@@ -775,7 +781,6 @@ ALLOW_ERROR_INJECTION(xe_device_probe_early, ERRNO); /* See xe_pci_probe() */
|
||||
static int probe_has_flat_ccs(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int fw_ref;
|
||||
u32 reg;
|
||||
|
||||
/* Always enabled/disabled, no runtime check to do */
|
||||
@@ -786,8 +791,8 @@ static int probe_has_flat_ccs(struct xe_device *xe)
|
||||
if (!gt)
|
||||
return 0;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
reg = xe_gt_mcr_unicast_read_any(gt, XE2_FLAT_CCS_BASE_RANGE_LOWER);
|
||||
@@ -797,11 +802,64 @@ static int probe_has_flat_ccs(struct xe_device *xe)
|
||||
drm_dbg(&xe->drm,
|
||||
"Flat CCS has been disabled in bios, May lead to performance impact");
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect if the driver is being run on pre-production hardware. We don't
|
||||
* keep workarounds for pre-production hardware long term, so print an
|
||||
* error and add taint if we're being loaded on a pre-production platform
|
||||
* for which the pre-prod workarounds have already been removed.
|
||||
*
|
||||
* The general policy is that we'll remove any workarounds that only apply to
|
||||
* pre-production hardware around the time force_probe restrictions are lifted
|
||||
* for a platform of the next major IP generation (for example, Xe2 pre-prod
|
||||
* workarounds should be removed around the time the first Xe3 platforms have
|
||||
* force_probe lifted).
|
||||
*/
|
||||
static void detect_preproduction_hw(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
int id;
|
||||
|
||||
/*
|
||||
* SR-IOV VFs don't have access to the FUSE2 register, so we can't
|
||||
* check pre-production status there. But the host OS will notice
|
||||
* and report the pre-production status, which should be enough to
|
||||
* help us catch mistaken use of pre-production hardware.
|
||||
*/
|
||||
if (IS_SRIOV_VF(xe))
|
||||
return;
|
||||
|
||||
/*
|
||||
* The "SW_CAP" fuse contains a bit indicating whether the device is a
|
||||
* production or pre-production device. This fuse is reflected through
|
||||
* the GT "FUSE2" register, even though the contents of the fuse are
|
||||
* not GT-specific. Every GT's reflection of this fuse should show the
|
||||
* same value, so we'll just use the first available GT for lookup.
|
||||
*/
|
||||
for_each_gt(gt, xe, id)
|
||||
break;
|
||||
|
||||
if (!gt)
|
||||
return;
|
||||
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT)) {
|
||||
xe_gt_err(gt, "Forcewake failure; cannot determine production/pre-production hw status.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (xe_mmio_read32(>->mmio, FUSE2) & PRODUCTION_HW)
|
||||
return;
|
||||
|
||||
xe_info(xe, "Pre-production hardware detected.\n");
|
||||
if (!xe->info.has_pre_prod_wa) {
|
||||
xe_err(xe, "Pre-production workarounds for this platform have already been removed.\n");
|
||||
add_taint(TAINT_MACHINE_CHECK, LOCKDEP_STILL_OK);
|
||||
}
|
||||
}
|
||||
|
||||
int xe_device_probe(struct xe_device *xe)
|
||||
{
|
||||
struct xe_tile *tile;
|
||||
@@ -972,6 +1030,8 @@ int xe_device_probe(struct xe_device *xe)
|
||||
if (err)
|
||||
goto err_unregister_display;
|
||||
|
||||
detect_preproduction_hw(xe);
|
||||
|
||||
return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
|
||||
|
||||
err_unregister_display:
|
||||
@@ -1034,7 +1094,6 @@ void xe_device_wmb(struct xe_device *xe)
|
||||
*/
|
||||
static void tdf_request_sync(struct xe_device *xe)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
struct xe_gt *gt;
|
||||
u8 id;
|
||||
|
||||
@@ -1042,8 +1101,8 @@ static void tdf_request_sync(struct xe_device *xe)
|
||||
if (xe_gt_is_media_type(gt))
|
||||
continue;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
xe_mmio_write32(>->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST);
|
||||
@@ -1058,15 +1117,12 @@ static void tdf_request_sync(struct xe_device *xe)
|
||||
if (xe_mmio_wait32(>->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST, 0,
|
||||
300, NULL, false))
|
||||
xe_gt_err_once(gt, "TD flush timeout\n");
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
}
|
||||
|
||||
void xe_device_l2_flush(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int fw_ref;
|
||||
|
||||
gt = xe_root_mmio_gt(xe);
|
||||
if (!gt)
|
||||
@@ -1075,8 +1131,8 @@ void xe_device_l2_flush(struct xe_device *xe)
|
||||
if (!XE_GT_WA(gt, 16023588340))
|
||||
return;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
spin_lock(>->global_invl_lock);
|
||||
@@ -1086,8 +1142,6 @@ void xe_device_l2_flush(struct xe_device *xe)
|
||||
xe_gt_err_once(gt, "Global invalidation timeout\n");
|
||||
|
||||
spin_unlock(>->global_invl_lock);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -172,6 +172,11 @@ static inline bool xe_device_has_lmtt(struct xe_device *xe)
|
||||
return IS_DGFX(xe);
|
||||
}
|
||||
|
||||
static inline bool xe_device_has_mert(struct xe_device *xe)
|
||||
{
|
||||
return xe->info.has_mert;
|
||||
}
|
||||
|
||||
u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size);
|
||||
|
||||
void xe_device_snapshot_print(struct xe_device *xe, struct drm_printer *p);
|
||||
|
||||
@@ -57,9 +57,8 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
drm_dbg(&xe->drm, "vram_d3cold_threshold: %u\n", vram_d3cold_threshold);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
ret = xe_pm_set_vram_threshold(xe, vram_d3cold_threshold);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret ?: count;
|
||||
}
|
||||
@@ -84,33 +83,31 @@ lb_fan_control_version_show(struct device *dev, struct device_attribute *attr, c
|
||||
u16 major = 0, minor = 0, hotfix = 0, build = 0;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
|
||||
&cap, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (REG_FIELD_GET(V1_FAN_PROVISIONED, cap)) {
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
|
||||
&ver_low, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
|
||||
&ver_high, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
|
||||
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
|
||||
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
|
||||
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
|
||||
}
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
|
||||
return sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(lb_fan_control_version);
|
||||
|
||||
@@ -123,33 +120,31 @@ lb_voltage_regulator_version_show(struct device *dev, struct device_attribute *a
|
||||
u16 major = 0, minor = 0, hotfix = 0, build = 0;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
|
||||
&cap, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (REG_FIELD_GET(VR_PARAMS_PROVISIONED, cap)) {
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
|
||||
&ver_low, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
|
||||
&ver_high, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
|
||||
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
|
||||
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
|
||||
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
|
||||
}
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
|
||||
return sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(lb_voltage_regulator_version);
|
||||
|
||||
@@ -233,9 +228,8 @@ auto_link_downgrade_capable_show(struct device *dev, struct device_attribute *at
|
||||
struct xe_device *xe = pdev_to_xe_device(pdev);
|
||||
u32 cap, val;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
val = xe_mmio_read32(xe_root_tile_mmio(xe), BMG_PCIE_CAP);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
cap = REG_FIELD_GET(LINK_DOWNGRADE, val);
|
||||
return sysfs_emit(buf, "%u\n", cap == DOWNGRADE_CAPABLE);
|
||||
@@ -251,11 +245,10 @@ auto_link_downgrade_status_show(struct device *dev, struct device_attribute *att
|
||||
u32 val = 0;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
ret = xe_pcode_read(xe_device_get_root_tile(xe),
|
||||
PCODE_MBOX(DGFX_PCODE_STATUS, DGFX_GET_INIT_STATUS, 0),
|
||||
&val, NULL);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret ?: sysfs_emit(buf, "%u\n", REG_FIELD_GET(DGFX_LINK_DOWNGRADE_STATUS, val));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "xe_late_bind_fw_types.h"
|
||||
#include "xe_lmtt_types.h"
|
||||
#include "xe_memirq_types.h"
|
||||
#include "xe_mert.h"
|
||||
#include "xe_oa_types.h"
|
||||
#include "xe_pagefault_types.h"
|
||||
#include "xe_platform_types.h"
|
||||
@@ -183,6 +184,13 @@ struct xe_tile {
|
||||
* Media GT shares a pool with its primary GT.
|
||||
*/
|
||||
struct xe_sa_manager *kernel_bb_pool;
|
||||
|
||||
/**
|
||||
* @mem.reclaim_pool: Pool for PRLs allocated.
|
||||
*
|
||||
* Only main GT has page reclaim list allocations.
|
||||
*/
|
||||
struct xe_sa_manager *reclaim_pool;
|
||||
} mem;
|
||||
|
||||
/** @sriov: tile level virtualization data */
|
||||
@@ -219,6 +227,9 @@ struct xe_tile {
|
||||
|
||||
/** @debugfs: debugfs directory associated with this tile */
|
||||
struct dentry *debugfs;
|
||||
|
||||
/** @mert: MERT-related data */
|
||||
struct xe_mert mert;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -285,6 +296,8 @@ struct xe_device {
|
||||
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_cached_pt: Supports caching pagetable */
|
||||
u8 has_cached_pt:1;
|
||||
/** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */
|
||||
u8 has_device_atomics_on_smem:1;
|
||||
/** @info.has_fan_control: Device supports fan control */
|
||||
@@ -297,6 +310,8 @@ struct xe_device {
|
||||
u8 has_heci_cscfi:1;
|
||||
/** @info.has_heci_gscfi: device has heci gscfi */
|
||||
u8 has_heci_gscfi:1;
|
||||
/** @info.has_i2c: Device has I2C controller */
|
||||
u8 has_i2c:1;
|
||||
/** @info.has_late_bind: Device has firmware late binding support */
|
||||
u8 has_late_bind:1;
|
||||
/** @info.has_llc: Device has a shared CPU+GPU last level cache */
|
||||
@@ -307,6 +322,12 @@ struct xe_device {
|
||||
u8 has_mbx_power_limits:1;
|
||||
/** @info.has_mem_copy_instr: Device supports MEM_COPY instruction */
|
||||
u8 has_mem_copy_instr:1;
|
||||
/** @info.has_mert: Device has standalone MERT */
|
||||
u8 has_mert:1;
|
||||
/** @info.has_page_reclaim_hw_assist: Device supports page reclamation feature */
|
||||
u8 has_page_reclaim_hw_assist:1;
|
||||
/** @info.has_pre_prod_wa: Pre-production workarounds still present in driver */
|
||||
u8 has_pre_prod_wa:1;
|
||||
/** @info.has_pxp: Device has PXP support */
|
||||
u8 has_pxp:1;
|
||||
/** @info.has_range_tlb_inval: Has range based TLB invalidations */
|
||||
@@ -605,6 +626,12 @@ struct xe_device {
|
||||
/** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
|
||||
u32 atomic_svm_timeslice_ms;
|
||||
|
||||
/** @min_run_period_lr_ms: LR VM (preempt fence mode) timeslice */
|
||||
u32 min_run_period_lr_ms;
|
||||
|
||||
/** @min_run_period_pf_ms: LR VM (page fault mode) timeslice */
|
||||
u32 min_run_period_pf_ms;
|
||||
|
||||
#ifdef TEST_VM_OPS_ERROR
|
||||
/**
|
||||
* @vm_inject_error_position: inject errors at different places in VM
|
||||
|
||||
@@ -285,32 +285,31 @@ static struct xe_hw_engine *any_engine(struct xe_device *xe)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool force_wake_get_any_engine(struct xe_device *xe,
|
||||
struct xe_hw_engine **phwe,
|
||||
unsigned int *pfw_ref)
|
||||
/*
|
||||
* Pick any engine and grab its forcewake. On error phwe will be NULL and
|
||||
* the returned forcewake reference will be invalid. Callers should check
|
||||
* phwe against NULL.
|
||||
*/
|
||||
static struct xe_force_wake_ref force_wake_get_any_engine(struct xe_device *xe,
|
||||
struct xe_hw_engine **phwe)
|
||||
{
|
||||
enum xe_force_wake_domains domain;
|
||||
unsigned int fw_ref;
|
||||
struct xe_force_wake_ref fw_ref = {};
|
||||
struct xe_hw_engine *hwe;
|
||||
struct xe_force_wake *fw;
|
||||
|
||||
*phwe = NULL;
|
||||
|
||||
hwe = any_engine(xe);
|
||||
if (!hwe)
|
||||
return false;
|
||||
return fw_ref; /* will be invalid */
|
||||
|
||||
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;
|
||||
}
|
||||
fw_ref = xe_force_wake_constructor(gt_to_fw(hwe->gt), domain);
|
||||
if (xe_force_wake_ref_has_domain(fw_ref.domains, domain))
|
||||
*phwe = hwe; /* valid forcewake */
|
||||
|
||||
*phwe = hwe;
|
||||
*pfw_ref = fw_ref;
|
||||
|
||||
return true;
|
||||
return fw_ref;
|
||||
}
|
||||
|
||||
static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
|
||||
@@ -322,7 +321,6 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
|
||||
struct xe_hw_engine *hwe;
|
||||
struct xe_exec_queue *q;
|
||||
u64 gpu_timestamp;
|
||||
unsigned int fw_ref;
|
||||
|
||||
/*
|
||||
* RING_TIMESTAMP registers are inaccessible in VF mode.
|
||||
@@ -339,29 +337,26 @@ static void show_run_ticks(struct drm_printer *p, struct drm_file *file)
|
||||
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;
|
||||
}
|
||||
scoped_guard(xe_pm_runtime, xe) {
|
||||
CLASS(xe_force_wake_release_only, fw_ref)(force_wake_get_any_engine(xe, &hwe));
|
||||
if (!hwe)
|
||||
return;
|
||||
|
||||
/* Accumulate all the exec queues from this client */
|
||||
mutex_lock(&xef->exec_queue.lock);
|
||||
xa_for_each(&xef->exec_queue.xa, i, q) {
|
||||
xe_exec_queue_get(q);
|
||||
/* Accumulate all the exec queues from this client */
|
||||
mutex_lock(&xef->exec_queue.lock);
|
||||
xa_for_each(&xef->exec_queue.xa, i, q) {
|
||||
xe_exec_queue_get(q);
|
||||
mutex_unlock(&xef->exec_queue.lock);
|
||||
|
||||
xe_exec_queue_update_run_ticks(q);
|
||||
|
||||
mutex_lock(&xef->exec_queue.lock);
|
||||
xe_exec_queue_put(q);
|
||||
}
|
||||
mutex_unlock(&xef->exec_queue.lock);
|
||||
|
||||
xe_exec_queue_update_run_ticks(q);
|
||||
|
||||
mutex_lock(&xef->exec_queue.lock);
|
||||
xe_exec_queue_put(q);
|
||||
gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
|
||||
}
|
||||
mutex_unlock(&xef->exec_queue.lock);
|
||||
|
||||
gpu_timestamp = xe_hw_engine_read_timestamp(hwe);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(hwe->gt), fw_ref);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
for (class = 0; class < XE_ENGINE_CLASS_MAX; class++) {
|
||||
const char *class_name;
|
||||
|
||||
@@ -121,7 +121,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||
u64 addresses[XE_HW_ENGINE_MAX_INSTANCE];
|
||||
struct drm_gpuvm_exec vm_exec = {.extra.fn = xe_exec_fn};
|
||||
struct drm_exec *exec = &vm_exec.exec;
|
||||
u32 i, num_syncs, num_ufence = 0;
|
||||
u32 i, num_syncs, num_in_sync = 0, num_ufence = 0;
|
||||
struct xe_validation_ctx ctx;
|
||||
struct xe_sched_job *job;
|
||||
struct xe_vm *vm;
|
||||
@@ -183,6 +183,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||
|
||||
if (xe_sync_is_ufence(&syncs[num_syncs]))
|
||||
num_ufence++;
|
||||
|
||||
if (!num_in_sync && xe_sync_needs_wait(&syncs[num_syncs]))
|
||||
num_in_sync++;
|
||||
}
|
||||
|
||||
if (XE_IOCTL_DBG(xe, num_ufence > 1)) {
|
||||
@@ -203,7 +206,9 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||
mode = xe_hw_engine_group_find_exec_mode(q);
|
||||
|
||||
if (mode == EXEC_MODE_DMA_FENCE) {
|
||||
err = xe_hw_engine_group_get_mode(group, mode, &previous_mode);
|
||||
err = xe_hw_engine_group_get_mode(group, mode, &previous_mode,
|
||||
syncs, num_in_sync ?
|
||||
num_syncs : 0);
|
||||
if (err)
|
||||
goto err_syncs;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <drm/drm_syncobj.h>
|
||||
#include <uapi/drm/xe_drm.h>
|
||||
|
||||
#include "xe_bo.h"
|
||||
#include "xe_dep_scheduler.h"
|
||||
#include "xe_device.h"
|
||||
#include "xe_gt.h"
|
||||
@@ -53,6 +54,54 @@
|
||||
* the ring operations the different engine classes support.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Multi Queue Group
|
||||
*
|
||||
* Multi Queue Group is another mode of execution supported by the compute
|
||||
* and blitter copy command streamers (CCS and BCS, respectively). It is
|
||||
* an enhancement of the existing hardware architecture and leverages the
|
||||
* same submission model. It enables support for efficient, parallel
|
||||
* execution of multiple queues within a single shared context. The multi
|
||||
* queue group functionality is only supported with GuC submission backend.
|
||||
* All the queues of a group must use the same address space (VM).
|
||||
*
|
||||
* The DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE execution queue property
|
||||
* supports creating a multi queue group and adding queues to a queue group.
|
||||
*
|
||||
* The XE_EXEC_QUEUE_CREATE ioctl call with above property with value field
|
||||
* set to DRM_XE_MULTI_GROUP_CREATE, will create a new multi queue group with
|
||||
* the queue being created as the primary queue (aka q0) of the group. To add
|
||||
* secondary queues to the group, they need to be created with the above
|
||||
* property with id of the primary queue as the value. The properties of
|
||||
* the primary queue (like priority, time slice) applies to the whole group.
|
||||
* So, these properties can't be set for secondary queues of a group.
|
||||
*
|
||||
* The hardware does not support removing a queue from a multi-queue group.
|
||||
* However, queues can be dynamically added to the group. A group can have
|
||||
* up to 64 queues. To support this, XeKMD holds references to LRCs of the
|
||||
* queues even after the queues are destroyed by the user until the whole
|
||||
* group is destroyed. The secondary queues hold a reference to the primary
|
||||
* queue thus preventing the group from being destroyed when user destroys
|
||||
* the primary queue. Once the primary queue is destroyed, secondary queues
|
||||
* can't be added to the queue group, but they can continue to submit the
|
||||
* jobs if the DRM_XE_MULTI_GROUP_KEEP_ACTIVE flag is set during the multi
|
||||
* queue group creation.
|
||||
*
|
||||
* The queues of a multi queue group can set their priority within the group
|
||||
* through the DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY property.
|
||||
* This multi queue priority can also be set dynamically through the
|
||||
* XE_EXEC_QUEUE_SET_PROPERTY ioctl. This is the only other property
|
||||
* supported by the secondary queues of a multi queue group, other than
|
||||
* DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE.
|
||||
*
|
||||
* When GuC reports an error on any of the queues of a multi queue group,
|
||||
* the queue cleanup mechanism is invoked for all the queues of the group
|
||||
* as hardware cannot make progress on the multi queue context.
|
||||
*
|
||||
* Refer :ref:`multi-queue-group-guc-interface` for multi queue group GuC
|
||||
* interface.
|
||||
*/
|
||||
|
||||
enum xe_exec_queue_sched_prop {
|
||||
XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
|
||||
XE_EXEC_QUEUE_TIMESLICE = 1,
|
||||
@@ -61,7 +110,35 @@ enum xe_exec_queue_sched_prop {
|
||||
};
|
||||
|
||||
static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u64 extensions, int ext_number);
|
||||
u64 extensions);
|
||||
|
||||
static void xe_exec_queue_group_cleanup(struct xe_exec_queue *q)
|
||||
{
|
||||
struct xe_exec_queue_group *group = q->multi_queue.group;
|
||||
struct xe_lrc *lrc;
|
||||
unsigned long idx;
|
||||
|
||||
if (xe_exec_queue_is_multi_queue_secondary(q)) {
|
||||
/*
|
||||
* Put pairs with get from xe_exec_queue_lookup() call
|
||||
* in xe_exec_queue_group_validate().
|
||||
*/
|
||||
xe_exec_queue_put(xe_exec_queue_multi_queue_primary(q));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
/* Primary queue cleanup */
|
||||
xa_for_each(&group->xa, idx, lrc)
|
||||
xe_lrc_put(lrc);
|
||||
|
||||
xa_destroy(&group->xa);
|
||||
mutex_destroy(&group->list_lock);
|
||||
xe_bo_unpin_map_no_vm(group->cgp_bo);
|
||||
kfree(group);
|
||||
}
|
||||
|
||||
static void __xe_exec_queue_free(struct xe_exec_queue *q)
|
||||
{
|
||||
@@ -73,12 +150,17 @@ static void __xe_exec_queue_free(struct xe_exec_queue *q)
|
||||
|
||||
if (xe_exec_queue_uses_pxp(q))
|
||||
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
|
||||
|
||||
if (xe_exec_queue_is_multi_queue(q))
|
||||
xe_exec_queue_group_cleanup(q);
|
||||
|
||||
if (q->vm)
|
||||
xe_vm_put(q->vm);
|
||||
|
||||
if (q->xef)
|
||||
xe_file_put(q->xef);
|
||||
|
||||
kvfree(q->replay_state);
|
||||
kfree(q);
|
||||
}
|
||||
|
||||
@@ -147,6 +229,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
|
||||
INIT_LIST_HEAD(&q->multi_gt_link);
|
||||
INIT_LIST_HEAD(&q->hw_engine_group_link);
|
||||
INIT_LIST_HEAD(&q->pxp.link);
|
||||
q->multi_queue.priority = XE_MULTI_QUEUE_PRIORITY_NORMAL;
|
||||
|
||||
q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us;
|
||||
q->sched_props.preempt_timeout_us =
|
||||
@@ -175,7 +258,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
|
||||
* may set q->usm, must come before xe_lrc_create(),
|
||||
* may overwrite q->sched_props, must come before q->ops->init()
|
||||
*/
|
||||
err = exec_queue_user_extensions(xe, q, extensions, 0);
|
||||
err = exec_queue_user_extensions(xe, q, extensions);
|
||||
if (err) {
|
||||
__xe_exec_queue_free(q);
|
||||
return ERR_PTR(err);
|
||||
@@ -225,8 +308,8 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags)
|
||||
struct xe_lrc *lrc;
|
||||
|
||||
xe_gt_sriov_vf_wait_valid_ggtt(q->gt);
|
||||
lrc = xe_lrc_create(q->hwe, q->vm, xe_lrc_ring_size(),
|
||||
q->msix_vec, flags);
|
||||
lrc = xe_lrc_create(q->hwe, q->vm, q->replay_state,
|
||||
xe_lrc_ring_size(), q->msix_vec, flags);
|
||||
if (IS_ERR(lrc)) {
|
||||
err = PTR_ERR(lrc);
|
||||
goto err_lrc;
|
||||
@@ -383,6 +466,26 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
|
||||
}
|
||||
ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
|
||||
|
||||
static void xe_exec_queue_group_kill(struct kref *ref)
|
||||
{
|
||||
struct xe_exec_queue_group *group = container_of(ref, struct xe_exec_queue_group,
|
||||
kill_refcount);
|
||||
xe_exec_queue_kill(group->primary);
|
||||
}
|
||||
|
||||
static inline void xe_exec_queue_group_kill_get(struct xe_exec_queue_group *group)
|
||||
{
|
||||
kref_get(&group->kill_refcount);
|
||||
}
|
||||
|
||||
void xe_exec_queue_group_kill_put(struct xe_exec_queue_group *group)
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
kref_put(&group->kill_refcount, xe_exec_queue_group_kill);
|
||||
}
|
||||
|
||||
void xe_exec_queue_destroy(struct kref *ref)
|
||||
{
|
||||
struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount);
|
||||
@@ -567,6 +670,217 @@ exec_queue_set_pxp_type(struct xe_device *xe, struct xe_exec_queue *q, u64 value
|
||||
return xe_pxp_exec_queue_set_type(xe->pxp, q, DRM_XE_PXP_TYPE_HWDRM);
|
||||
}
|
||||
|
||||
static int exec_queue_set_hang_replay_state(struct xe_device *xe,
|
||||
struct xe_exec_queue *q,
|
||||
u64 value)
|
||||
{
|
||||
size_t size = xe_gt_lrc_hang_replay_size(q->gt, q->class);
|
||||
u64 __user *address = u64_to_user_ptr(value);
|
||||
void *ptr;
|
||||
|
||||
ptr = vmemdup_user(address, size);
|
||||
if (XE_IOCTL_DBG(xe, IS_ERR(ptr)))
|
||||
return PTR_ERR(ptr);
|
||||
|
||||
q->replay_state = ptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xe_exec_queue_group_init(struct xe_device *xe, struct xe_exec_queue *q)
|
||||
{
|
||||
struct xe_tile *tile = gt_to_tile(q->gt);
|
||||
struct xe_exec_queue_group *group;
|
||||
struct xe_bo *bo;
|
||||
|
||||
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
||||
if (!group)
|
||||
return -ENOMEM;
|
||||
|
||||
bo = xe_bo_create_pin_map_novm(xe, tile, SZ_4K, ttm_bo_type_kernel,
|
||||
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
|
||||
XE_BO_FLAG_PINNED_LATE_RESTORE |
|
||||
XE_BO_FLAG_FORCE_USER_VRAM |
|
||||
XE_BO_FLAG_GGTT_INVALIDATE |
|
||||
XE_BO_FLAG_GGTT, false);
|
||||
if (IS_ERR(bo)) {
|
||||
drm_err(&xe->drm, "CGP bo allocation for queue group failed: %ld\n",
|
||||
PTR_ERR(bo));
|
||||
kfree(group);
|
||||
return PTR_ERR(bo);
|
||||
}
|
||||
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0, SZ_4K);
|
||||
|
||||
group->primary = q;
|
||||
group->cgp_bo = bo;
|
||||
INIT_LIST_HEAD(&group->list);
|
||||
kref_init(&group->kill_refcount);
|
||||
xa_init_flags(&group->xa, XA_FLAGS_ALLOC1);
|
||||
mutex_init(&group->list_lock);
|
||||
q->multi_queue.group = group;
|
||||
|
||||
/* group->list_lock is used in submission backend */
|
||||
if (IS_ENABLED(CONFIG_LOCKDEP)) {
|
||||
fs_reclaim_acquire(GFP_KERNEL);
|
||||
might_lock(&group->list_lock);
|
||||
fs_reclaim_release(GFP_KERNEL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool xe_exec_queue_supports_multi_queue(struct xe_exec_queue *q)
|
||||
{
|
||||
return q->gt->info.multi_queue_engine_class_mask & BIT(q->class);
|
||||
}
|
||||
|
||||
static int xe_exec_queue_group_validate(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u32 primary_id)
|
||||
{
|
||||
struct xe_exec_queue_group *group;
|
||||
struct xe_exec_queue *primary;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Get from below xe_exec_queue_lookup() pairs with put
|
||||
* in xe_exec_queue_group_cleanup().
|
||||
*/
|
||||
primary = xe_exec_queue_lookup(q->vm->xef, primary_id);
|
||||
if (XE_IOCTL_DBG(xe, !primary))
|
||||
return -ENOENT;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, !xe_exec_queue_is_multi_queue_primary(primary)) ||
|
||||
XE_IOCTL_DBG(xe, q->vm != primary->vm) ||
|
||||
XE_IOCTL_DBG(xe, q->logical_mask != primary->logical_mask)) {
|
||||
ret = -EINVAL;
|
||||
goto put_primary;
|
||||
}
|
||||
|
||||
group = primary->multi_queue.group;
|
||||
q->multi_queue.valid = true;
|
||||
q->multi_queue.group = group;
|
||||
|
||||
return 0;
|
||||
put_primary:
|
||||
xe_exec_queue_put(primary);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define XE_MAX_GROUP_SIZE 64
|
||||
static int xe_exec_queue_group_add(struct xe_device *xe, struct xe_exec_queue *q)
|
||||
{
|
||||
struct xe_exec_queue_group *group = q->multi_queue.group;
|
||||
u32 pos;
|
||||
int err;
|
||||
|
||||
xe_assert(xe, xe_exec_queue_is_multi_queue_secondary(q));
|
||||
|
||||
/* Primary queue holds a reference to LRCs of all secondary queues */
|
||||
err = xa_alloc(&group->xa, &pos, xe_lrc_get(q->lrc[0]),
|
||||
XA_LIMIT(1, XE_MAX_GROUP_SIZE - 1), GFP_KERNEL);
|
||||
if (XE_IOCTL_DBG(xe, err)) {
|
||||
xe_lrc_put(q->lrc[0]);
|
||||
|
||||
/* It is invalid if queue group limit is exceeded */
|
||||
if (err == -EBUSY)
|
||||
err = -EINVAL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
q->multi_queue.pos = pos;
|
||||
|
||||
if (group->primary->multi_queue.keep_active) {
|
||||
xe_exec_queue_group_kill_get(group);
|
||||
q->multi_queue.keep_active = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xe_exec_queue_group_delete(struct xe_device *xe, struct xe_exec_queue *q)
|
||||
{
|
||||
struct xe_exec_queue_group *group = q->multi_queue.group;
|
||||
struct xe_lrc *lrc;
|
||||
|
||||
xe_assert(xe, xe_exec_queue_is_multi_queue_secondary(q));
|
||||
|
||||
lrc = xa_erase(&group->xa, q->multi_queue.pos);
|
||||
xe_assert(xe, lrc);
|
||||
xe_lrc_put(lrc);
|
||||
|
||||
if (q->multi_queue.keep_active) {
|
||||
xe_exec_queue_group_kill_put(group);
|
||||
q->multi_queue.keep_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int exec_queue_set_multi_group(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u64 value)
|
||||
{
|
||||
if (XE_IOCTL_DBG(xe, !xe_exec_queue_supports_multi_queue(q)))
|
||||
return -ENODEV;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, !xe_device_uc_enabled(xe)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, !q->vm->xef))
|
||||
return -EINVAL;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, xe_exec_queue_is_parallel(q)))
|
||||
return -EINVAL;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, xe_exec_queue_is_multi_queue(q)))
|
||||
return -EINVAL;
|
||||
|
||||
if (value & DRM_XE_MULTI_GROUP_CREATE) {
|
||||
if (XE_IOCTL_DBG(xe, value & ~(DRM_XE_MULTI_GROUP_CREATE |
|
||||
DRM_XE_MULTI_GROUP_KEEP_ACTIVE)))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* KEEP_ACTIVE is not supported in preempt fence mode as in that mode,
|
||||
* VM_DESTROY ioctl expects all exec queues of that VM are already killed.
|
||||
*/
|
||||
if (XE_IOCTL_DBG(xe, (value & DRM_XE_MULTI_GROUP_KEEP_ACTIVE) &&
|
||||
xe_vm_in_preempt_fence_mode(q->vm)))
|
||||
return -EINVAL;
|
||||
|
||||
q->multi_queue.valid = true;
|
||||
q->multi_queue.is_primary = true;
|
||||
q->multi_queue.pos = 0;
|
||||
if (value & DRM_XE_MULTI_GROUP_KEEP_ACTIVE)
|
||||
q->multi_queue.keep_active = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* While adding secondary queues, the upper 32 bits must be 0 */
|
||||
if (XE_IOCTL_DBG(xe, value & (~0ull << 32)))
|
||||
return -EINVAL;
|
||||
|
||||
return xe_exec_queue_group_validate(xe, q, value);
|
||||
}
|
||||
|
||||
static int exec_queue_set_multi_queue_priority(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u64 value)
|
||||
{
|
||||
if (XE_IOCTL_DBG(xe, value > XE_MULTI_QUEUE_PRIORITY_HIGH))
|
||||
return -EINVAL;
|
||||
|
||||
/* For queue creation time (!q->xef) setting, just store the priority value */
|
||||
if (!q->xef) {
|
||||
q->multi_queue.priority = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!xe_exec_queue_is_multi_queue(q))
|
||||
return -EINVAL;
|
||||
|
||||
return q->ops->set_multi_queue_priority(q, value);
|
||||
}
|
||||
|
||||
typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe,
|
||||
struct xe_exec_queue *q,
|
||||
u64 value);
|
||||
@@ -575,11 +889,76 @@ static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = {
|
||||
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority,
|
||||
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice,
|
||||
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE] = exec_queue_set_pxp_type,
|
||||
[DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE] = exec_queue_set_hang_replay_state,
|
||||
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP] = exec_queue_set_multi_group,
|
||||
[DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY] =
|
||||
exec_queue_set_multi_queue_priority,
|
||||
};
|
||||
|
||||
int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct xe_device *xe = to_xe_device(dev);
|
||||
struct xe_file *xef = to_xe_file(file);
|
||||
struct drm_xe_exec_queue_set_property *args = data;
|
||||
struct xe_exec_queue *q;
|
||||
int ret;
|
||||
u32 idx;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
|
||||
return -EINVAL;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, args->property !=
|
||||
DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY))
|
||||
return -EINVAL;
|
||||
|
||||
q = xe_exec_queue_lookup(xef, args->exec_queue_id);
|
||||
if (XE_IOCTL_DBG(xe, !q))
|
||||
return -ENOENT;
|
||||
|
||||
idx = array_index_nospec(args->property,
|
||||
ARRAY_SIZE(exec_queue_set_property_funcs));
|
||||
ret = exec_queue_set_property_funcs[idx](xe, q, args->value);
|
||||
if (XE_IOCTL_DBG(xe, ret))
|
||||
goto err_post_lookup;
|
||||
|
||||
xe_exec_queue_put(q);
|
||||
return 0;
|
||||
|
||||
err_post_lookup:
|
||||
xe_exec_queue_put(q);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exec_queue_user_ext_check(struct xe_exec_queue *q, u64 properties)
|
||||
{
|
||||
u64 secondary_queue_valid_props = BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP) |
|
||||
BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY);
|
||||
|
||||
/*
|
||||
* Only MULTI_QUEUE_PRIORITY property is valid for secondary queues of a
|
||||
* multi-queue group.
|
||||
*/
|
||||
if (xe_exec_queue_is_multi_queue_secondary(q) &&
|
||||
properties & ~secondary_queue_valid_props)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_queue_user_ext_check_final(struct xe_exec_queue *q, u64 properties)
|
||||
{
|
||||
/* MULTI_QUEUE_PRIORITY only applies to multi-queue group queues */
|
||||
if ((properties & BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY)) &&
|
||||
!(properties & BIT_ULL(DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP)))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_queue_user_ext_set_property(struct xe_device *xe,
|
||||
struct xe_exec_queue *q,
|
||||
u64 extension)
|
||||
u64 extension, u64 *properties)
|
||||
{
|
||||
u64 __user *address = u64_to_user_ptr(extension);
|
||||
struct drm_xe_ext_set_property ext;
|
||||
@@ -595,27 +974,35 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe,
|
||||
XE_IOCTL_DBG(xe, ext.pad) ||
|
||||
XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY &&
|
||||
ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE &&
|
||||
ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE))
|
||||
ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE &&
|
||||
ext.property != DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE &&
|
||||
ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP &&
|
||||
ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY))
|
||||
return -EINVAL;
|
||||
|
||||
idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs));
|
||||
if (!exec_queue_set_property_funcs[idx])
|
||||
return -EINVAL;
|
||||
|
||||
*properties |= BIT_ULL(idx);
|
||||
err = exec_queue_user_ext_check(q, *properties);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
return err;
|
||||
|
||||
return exec_queue_set_property_funcs[idx](xe, q, ext.value);
|
||||
}
|
||||
|
||||
typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
|
||||
struct xe_exec_queue *q,
|
||||
u64 extension);
|
||||
u64 extension, u64 *properties);
|
||||
|
||||
static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
|
||||
[DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
|
||||
};
|
||||
|
||||
#define MAX_USER_EXTENSIONS 16
|
||||
static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u64 extensions, int ext_number)
|
||||
static int __exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u64 extensions, int ext_number, u64 *properties)
|
||||
{
|
||||
u64 __user *address = u64_to_user_ptr(extensions);
|
||||
struct drm_xe_user_extension ext;
|
||||
@@ -636,13 +1023,36 @@ static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue
|
||||
|
||||
idx = array_index_nospec(ext.name,
|
||||
ARRAY_SIZE(exec_queue_user_extension_funcs));
|
||||
err = exec_queue_user_extension_funcs[idx](xe, q, extensions);
|
||||
err = exec_queue_user_extension_funcs[idx](xe, q, extensions, properties);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
return err;
|
||||
|
||||
if (ext.next_extension)
|
||||
return exec_queue_user_extensions(xe, q, ext.next_extension,
|
||||
++ext_number);
|
||||
return __exec_queue_user_extensions(xe, q, ext.next_extension,
|
||||
++ext_number, properties);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
|
||||
u64 extensions)
|
||||
{
|
||||
u64 properties = 0;
|
||||
int err;
|
||||
|
||||
err = __exec_queue_user_extensions(xe, q, extensions, 0, &properties);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
return err;
|
||||
|
||||
err = exec_queue_user_ext_check_final(q, properties);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
return err;
|
||||
|
||||
if (xe_exec_queue_is_multi_queue_primary(q)) {
|
||||
err = xe_exec_queue_group_init(xe, q);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -798,12 +1208,18 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
|
||||
if (IS_ERR(q))
|
||||
return PTR_ERR(q);
|
||||
|
||||
if (xe_exec_queue_is_multi_queue_secondary(q)) {
|
||||
err = xe_exec_queue_group_add(xe, q);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
goto put_exec_queue;
|
||||
}
|
||||
|
||||
if (xe_vm_in_preempt_fence_mode(vm)) {
|
||||
q->lr.context = dma_fence_context_alloc(1);
|
||||
|
||||
err = xe_vm_add_compute_exec_queue(vm, q);
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
goto put_exec_queue;
|
||||
goto delete_queue_group;
|
||||
}
|
||||
|
||||
if (q->vm && q->hwe->hw_engine_group) {
|
||||
@@ -826,6 +1242,9 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
|
||||
|
||||
kill_exec_queue:
|
||||
xe_exec_queue_kill(q);
|
||||
delete_queue_group:
|
||||
if (xe_exec_queue_is_multi_queue_secondary(q))
|
||||
xe_exec_queue_group_delete(xe, q);
|
||||
put_exec_queue:
|
||||
xe_exec_queue_put(q);
|
||||
return err;
|
||||
@@ -981,6 +1400,11 @@ void xe_exec_queue_kill(struct xe_exec_queue *q)
|
||||
|
||||
q->ops->kill(q);
|
||||
xe_vm_remove_compute_exec_queue(q->vm, q);
|
||||
|
||||
if (!xe_exec_queue_is_multi_queue_primary(q) && q->multi_queue.keep_active) {
|
||||
xe_exec_queue_group_kill_put(q->multi_queue.group);
|
||||
q->multi_queue.keep_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
|
||||
@@ -1007,7 +1431,10 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
|
||||
if (q->vm && q->hwe->hw_engine_group)
|
||||
xe_hw_engine_group_del_exec_queue(q->hwe->hw_engine_group, q);
|
||||
|
||||
xe_exec_queue_kill(q);
|
||||
if (xe_exec_queue_is_multi_queue_primary(q))
|
||||
xe_exec_queue_group_kill_put(q->multi_queue.group);
|
||||
else
|
||||
xe_exec_queue_kill(q);
|
||||
|
||||
trace_xe_exec_queue_close(q);
|
||||
xe_exec_queue_put(q);
|
||||
|
||||
@@ -66,6 +66,55 @@ static inline bool xe_exec_queue_uses_pxp(struct xe_exec_queue *q)
|
||||
return q->pxp.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_exec_queue_is_multi_queue() - Whether an exec_queue is part of a queue group.
|
||||
* @q: The exec_queue
|
||||
*
|
||||
* Return: True if the exec_queue is part of a queue group, false otherwise.
|
||||
*/
|
||||
static inline bool xe_exec_queue_is_multi_queue(struct xe_exec_queue *q)
|
||||
{
|
||||
return q->multi_queue.valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_exec_queue_is_multi_queue_primary() - Whether an exec_queue is primary queue
|
||||
* of a multi queue group.
|
||||
* @q: The exec_queue
|
||||
*
|
||||
* Return: True if @q is primary queue of a queue group, false otherwise.
|
||||
*/
|
||||
static inline bool xe_exec_queue_is_multi_queue_primary(struct xe_exec_queue *q)
|
||||
{
|
||||
return q->multi_queue.is_primary;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_exec_queue_is_multi_queue_secondary() - Whether an exec_queue is secondary queue
|
||||
* of a multi queue group.
|
||||
* @q: The exec_queue
|
||||
*
|
||||
* Return: True if @q is secondary queue of a queue group, false otherwise.
|
||||
*/
|
||||
static inline bool xe_exec_queue_is_multi_queue_secondary(struct xe_exec_queue *q)
|
||||
{
|
||||
return xe_exec_queue_is_multi_queue(q) && !xe_exec_queue_is_multi_queue_primary(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_exec_queue_multi_queue_primary() - Get multi queue group's primary queue
|
||||
* @q: The exec_queue
|
||||
*
|
||||
* If @q belongs to a multi queue group, then the primary queue of the group will
|
||||
* be returned. Otherwise, @q will be returned.
|
||||
*/
|
||||
static inline struct xe_exec_queue *xe_exec_queue_multi_queue_primary(struct xe_exec_queue *q)
|
||||
{
|
||||
return xe_exec_queue_is_multi_queue(q) ? q->multi_queue.group->primary : q;
|
||||
}
|
||||
|
||||
void xe_exec_queue_group_kill_put(struct xe_exec_queue_group *group);
|
||||
|
||||
bool xe_exec_queue_is_lr(struct xe_exec_queue *q);
|
||||
|
||||
bool xe_exec_queue_is_idle(struct xe_exec_queue *q);
|
||||
@@ -78,6 +127,8 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
int xe_exec_queue_set_property_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
enum xe_exec_queue_priority xe_exec_queue_device_get_max_priority(struct xe_device *xe);
|
||||
|
||||
void xe_exec_queue_last_fence_put(struct xe_exec_queue *e, struct xe_vm *vm);
|
||||
@@ -111,4 +162,21 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch);
|
||||
|
||||
struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q);
|
||||
|
||||
/**
|
||||
* xe_exec_queue_idle_skip_suspend() - Can exec queue skip suspend
|
||||
* @q: The exec_queue
|
||||
*
|
||||
* If an exec queue is not parallel and is idle, the suspend steps can be
|
||||
* skipped in the submission backend immediatley signaling the suspend fence.
|
||||
* Parallel queues cannot skip this step due to limitations in the submission
|
||||
* backend.
|
||||
*
|
||||
* Return: True if exec queue is idle and can skip suspend steps, False
|
||||
* otherwise
|
||||
*/
|
||||
static inline bool xe_exec_queue_idle_skip_suspend(struct xe_exec_queue *q)
|
||||
{
|
||||
return !xe_exec_queue_is_parallel(q) && xe_exec_queue_is_idle(q);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,6 +32,44 @@ enum xe_exec_queue_priority {
|
||||
XE_EXEC_QUEUE_PRIORITY_COUNT
|
||||
};
|
||||
|
||||
/**
|
||||
* enum xe_multi_queue_priority - Multi Queue priority values
|
||||
*
|
||||
* The priority values of the queues within the multi queue group.
|
||||
*/
|
||||
enum xe_multi_queue_priority {
|
||||
/** @XE_MULTI_QUEUE_PRIORITY_LOW: Priority low */
|
||||
XE_MULTI_QUEUE_PRIORITY_LOW = 0,
|
||||
/** @XE_MULTI_QUEUE_PRIORITY_NORMAL: Priority normal */
|
||||
XE_MULTI_QUEUE_PRIORITY_NORMAL,
|
||||
/** @XE_MULTI_QUEUE_PRIORITY_HIGH: Priority high */
|
||||
XE_MULTI_QUEUE_PRIORITY_HIGH,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct xe_exec_queue_group - Execution multi queue group
|
||||
*
|
||||
* Contains multi queue group information.
|
||||
*/
|
||||
struct xe_exec_queue_group {
|
||||
/** @primary: Primary queue of this group */
|
||||
struct xe_exec_queue *primary;
|
||||
/** @cgp_bo: BO for the Context Group Page */
|
||||
struct xe_bo *cgp_bo;
|
||||
/** @xa: xarray to store LRCs */
|
||||
struct xarray xa;
|
||||
/** @list: List of all secondary queues in the group */
|
||||
struct list_head list;
|
||||
/** @list_lock: Secondary queue list lock */
|
||||
struct mutex list_lock;
|
||||
/** @kill_refcount: ref count to kill primary queue */
|
||||
struct kref kill_refcount;
|
||||
/** @sync_pending: CGP_SYNC_DONE g2h response pending */
|
||||
bool sync_pending;
|
||||
/** @banned: Group banned */
|
||||
bool banned;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct xe_exec_queue - Execution queue
|
||||
*
|
||||
@@ -111,6 +149,24 @@ struct xe_exec_queue {
|
||||
struct xe_guc_exec_queue *guc;
|
||||
};
|
||||
|
||||
/** @multi_queue: Multi queue information */
|
||||
struct {
|
||||
/** @multi_queue.group: Queue group information */
|
||||
struct xe_exec_queue_group *group;
|
||||
/** @multi_queue.link: Link into group's secondary queues list */
|
||||
struct list_head link;
|
||||
/** @multi_queue.priority: Queue priority within the multi-queue group */
|
||||
enum xe_multi_queue_priority priority;
|
||||
/** @multi_queue.pos: Position of queue within the multi-queue group */
|
||||
u8 pos;
|
||||
/** @multi_queue.valid: Queue belongs to a multi queue group */
|
||||
u8 valid:1;
|
||||
/** @multi_queue.is_primary: Is primary queue (Q0) of the group */
|
||||
u8 is_primary:1;
|
||||
/** @multi_queue.keep_active: Keep the group active after primary is destroyed */
|
||||
u8 keep_active:1;
|
||||
} multi_queue;
|
||||
|
||||
/** @sched_props: scheduling properties */
|
||||
struct {
|
||||
/** @sched_props.timeslice_us: timeslice period in micro-seconds */
|
||||
@@ -167,6 +223,9 @@ struct xe_exec_queue {
|
||||
/** @ufence_timeline_value: User fence timeline value */
|
||||
u64 ufence_timeline_value;
|
||||
|
||||
/** @replay_state: GPU hang replay state */
|
||||
void *replay_state;
|
||||
|
||||
/** @ops: submission backend exec queue operations */
|
||||
const struct xe_exec_queue_ops *ops;
|
||||
|
||||
@@ -213,6 +272,9 @@ struct xe_exec_queue_ops {
|
||||
int (*set_timeslice)(struct xe_exec_queue *q, u32 timeslice_us);
|
||||
/** @set_preempt_timeout: Set preemption timeout for exec queue */
|
||||
int (*set_preempt_timeout)(struct xe_exec_queue *q, u32 preempt_timeout_us);
|
||||
/** @set_multi_queue_priority: Set multi queue priority */
|
||||
int (*set_multi_queue_priority)(struct xe_exec_queue *q,
|
||||
enum xe_multi_queue_priority priority);
|
||||
/**
|
||||
* @suspend: Suspend exec queue from executing, allowed to be called
|
||||
* multiple times in a row before resume with the caveat that
|
||||
|
||||
@@ -269,7 +269,7 @@ struct xe_execlist_port *xe_execlist_port_create(struct xe_device *xe,
|
||||
|
||||
port->hwe = hwe;
|
||||
|
||||
port->lrc = xe_lrc_create(hwe, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0);
|
||||
port->lrc = xe_lrc_create(hwe, NULL, NULL, SZ_16K, XE_IRQ_DEFAULT_MSIX, 0);
|
||||
if (IS_ERR(port->lrc)) {
|
||||
err = PTR_ERR(port->lrc);
|
||||
goto err;
|
||||
|
||||
@@ -166,6 +166,13 @@ static int domain_sleep_wait(struct xe_gt *gt,
|
||||
* xe_force_wake_ref_has_domain() function. Caller must call
|
||||
* xe_force_wake_put() function to decrease incremented refcounts.
|
||||
*
|
||||
* When possible, scope-based forcewake (through CLASS(xe_force_wake, ...) or
|
||||
* xe_with_force_wake()) should be used instead of direct calls to this
|
||||
* function. Direct usage of get/put should only be used when the function
|
||||
* has goto-based flows that can interfere with scope-based cleanup, or when
|
||||
* the lifetime of the forcewake reference does not match a specific scope
|
||||
* (e.g., forcewake obtained in one function and released in a different one).
|
||||
*
|
||||
* Return: opaque reference to woken domains or zero if none of requested
|
||||
* domains were awake.
|
||||
*/
|
||||
|
||||
@@ -61,4 +61,44 @@ xe_force_wake_ref_has_domain(unsigned int fw_ref, enum xe_force_wake_domains dom
|
||||
return fw_ref & domain;
|
||||
}
|
||||
|
||||
struct xe_force_wake_ref {
|
||||
struct xe_force_wake *fw;
|
||||
unsigned int domains;
|
||||
};
|
||||
|
||||
static struct xe_force_wake_ref
|
||||
xe_force_wake_constructor(struct xe_force_wake *fw, unsigned int domains)
|
||||
{
|
||||
struct xe_force_wake_ref fw_ref = { .fw = fw };
|
||||
|
||||
fw_ref.domains = xe_force_wake_get(fw, domains);
|
||||
|
||||
return fw_ref;
|
||||
}
|
||||
|
||||
DEFINE_CLASS(xe_force_wake, struct xe_force_wake_ref,
|
||||
xe_force_wake_put(_T.fw, _T.domains),
|
||||
xe_force_wake_constructor(fw, domains),
|
||||
struct xe_force_wake *fw, unsigned int domains);
|
||||
|
||||
/*
|
||||
* Scoped helper for the forcewake class, using the same trick as scoped_guard()
|
||||
* to bind the lifetime to the next statement/block.
|
||||
*/
|
||||
#define __xe_with_force_wake(ref, fw, domains, done) \
|
||||
for (CLASS(xe_force_wake, ref)(fw, domains), *(done) = NULL; \
|
||||
!(done); (done) = (void *)1)
|
||||
|
||||
#define xe_with_force_wake(ref, fw, domains) \
|
||||
__xe_with_force_wake(ref, fw, domains, __UNIQUE_ID(done))
|
||||
|
||||
/*
|
||||
* Used when xe_force_wake_constructor() has already been called by another
|
||||
* function and the current function is responsible for releasing the forcewake
|
||||
* reference in all possible cases and error paths.
|
||||
*/
|
||||
DEFINE_CLASS(xe_force_wake_release_only, struct xe_force_wake_ref,
|
||||
if (_T.fw) xe_force_wake_put(_T.fw, _T.domains), fw_ref,
|
||||
struct xe_force_wake_ref fw_ref);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -396,9 +396,8 @@ static void ggtt_node_remove_work_func(struct work_struct *work)
|
||||
delayed_removal_work);
|
||||
struct xe_device *xe = tile_to_xe(node->ggtt->tile);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
ggtt_node_remove(node);
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -352,7 +352,6 @@ static void gsc_work(struct work_struct *work)
|
||||
struct xe_gsc *gsc = container_of(work, typeof(*gsc), work);
|
||||
struct xe_gt *gt = gsc_to_gt(gsc);
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
unsigned int fw_ref;
|
||||
u32 actions;
|
||||
int ret;
|
||||
|
||||
@@ -361,13 +360,12 @@ static void gsc_work(struct work_struct *work)
|
||||
gsc->work_actions = 0;
|
||||
spin_unlock_irq(&gsc->lock);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
|
||||
|
||||
if (actions & GSC_ACTION_ER_COMPLETE) {
|
||||
ret = gsc_er_complete(gt);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (gsc_er_complete(gt))
|
||||
return;
|
||||
}
|
||||
|
||||
if (actions & GSC_ACTION_FW_LOAD) {
|
||||
@@ -380,10 +378,6 @@ static void gsc_work(struct work_struct *work)
|
||||
|
||||
if (actions & GSC_ACTION_SW_PROXY)
|
||||
xe_gsc_proxy_request_handler(gsc);
|
||||
|
||||
out:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
void xe_gsc_hwe_irq_handler(struct xe_hw_engine *hwe, u16 intr_vec)
|
||||
@@ -615,7 +609,6 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p)
|
||||
{
|
||||
struct xe_gt *gt = gsc_to_gt(gsc);
|
||||
struct xe_mmio *mmio = >->mmio;
|
||||
unsigned int fw_ref;
|
||||
|
||||
xe_uc_fw_print(&gsc->fw, p);
|
||||
|
||||
@@ -624,8 +617,8 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p)
|
||||
if (!xe_uc_fw_is_enabled(&gsc->fw))
|
||||
return;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
drm_printf(p, "\nHECI1 FWSTS: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
||||
@@ -635,6 +628,4 @@ void xe_gsc_print_info(struct xe_gsc *gsc, struct drm_printer *p)
|
||||
xe_mmio_read32(mmio, HECI_FWSTS4(MTL_GSC_HECI1_BASE)),
|
||||
xe_mmio_read32(mmio, HECI_FWSTS5(MTL_GSC_HECI1_BASE)),
|
||||
xe_mmio_read32(mmio, HECI_FWSTS6(MTL_GSC_HECI1_BASE)));
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
@@ -37,9 +37,8 @@ static int gsc_info(struct seq_file *m, void *data)
|
||||
struct xe_device *xe = gsc_to_xe(gsc);
|
||||
struct drm_printer p = drm_seq_file_printer(m);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
xe_gsc_print_info(gsc, &p);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -440,22 +440,19 @@ static void xe_gsc_proxy_remove(void *arg)
|
||||
struct xe_gsc *gsc = arg;
|
||||
struct xe_gt *gt = gsc_to_gt(gsc);
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
unsigned int fw_ref = 0;
|
||||
|
||||
if (!gsc->proxy.component_added)
|
||||
return;
|
||||
|
||||
/* disable HECI2 IRQs */
|
||||
xe_pm_runtime_get(xe);
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
|
||||
if (!fw_ref)
|
||||
xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n");
|
||||
scoped_guard(xe_pm_runtime, xe) {
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GSC);
|
||||
if (!fw_ref.domains)
|
||||
xe_gt_err(gt, "failed to get forcewake to disable GSC interrupts\n");
|
||||
|
||||
/* try do disable irq even if forcewake failed */
|
||||
gsc_proxy_irq_toggle(gsc, false);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_pm_runtime_put(xe);
|
||||
/* try do disable irq even if forcewake failed */
|
||||
gsc_proxy_irq_toggle(gsc, false);
|
||||
}
|
||||
|
||||
xe_gsc_wait_for_worker_completion(gsc);
|
||||
|
||||
|
||||
@@ -103,14 +103,13 @@ void xe_gt_sanitize(struct xe_gt *gt)
|
||||
|
||||
static void xe_gt_enable_host_l2_vram(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
u32 reg;
|
||||
|
||||
if (!XE_GT_WA(gt, 16023588340))
|
||||
return;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
if (xe_gt_is_main_type(gt)) {
|
||||
@@ -120,12 +119,10 @@ static void xe_gt_enable_host_l2_vram(struct xe_gt *gt)
|
||||
}
|
||||
|
||||
xe_gt_mcr_multicast_write(gt, XEHPC_L3CLOS_MASK(3), 0xF);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
static void xe_gt_disable_host_l2_vram(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
u32 reg;
|
||||
|
||||
if (!XE_GT_WA(gt, 16023588340))
|
||||
@@ -134,15 +131,13 @@ static void xe_gt_disable_host_l2_vram(struct xe_gt *gt)
|
||||
if (xe_gt_is_media_type(gt))
|
||||
return;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
reg = xe_gt_mcr_unicast_read_any(gt, XE2_GAMREQSTRM_CTRL);
|
||||
reg &= ~CG_DIS_CNTLBUS;
|
||||
xe_gt_mcr_multicast_write(gt, XE2_GAMREQSTRM_CTRL, reg);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
static void gt_reset_worker(struct work_struct *w);
|
||||
@@ -389,7 +384,6 @@ int xe_gt_record_default_lrcs(struct xe_gt *gt)
|
||||
|
||||
int xe_gt_init_early(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
if (IS_SRIOV_PF(gt_to_xe(gt))) {
|
||||
@@ -436,13 +430,12 @@ int xe_gt_init_early(struct xe_gt *gt)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
xe_gt_mcr_init_early(gt);
|
||||
xe_pat_init(gt);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -460,16 +453,15 @@ static void dump_pat_on_error(struct xe_gt *gt)
|
||||
|
||||
static int gt_init_with_gt_forcewake(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
err = xe_uc_init(>->uc);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
xe_gt_topology_init(gt);
|
||||
xe_gt_mcr_init(gt);
|
||||
@@ -478,7 +470,7 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt)
|
||||
if (xe_gt_is_main_type(gt)) {
|
||||
err = xe_ggtt_init(gt_to_tile(gt)->mem.ggtt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
if (IS_SRIOV_PF(gt_to_xe(gt)))
|
||||
xe_lmtt_init(>_to_tile(gt)->sriov.pf.lmtt);
|
||||
}
|
||||
@@ -492,17 +484,17 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt)
|
||||
err = xe_hw_engines_init_early(gt);
|
||||
if (err) {
|
||||
dump_pat_on_error(gt);
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = xe_hw_engine_class_sysfs_init(gt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
/* Initialize CCS mode sysfs after early initialization of HW engines */
|
||||
err = xe_gt_ccs_mode_sysfs_init(gt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Stash hardware-reported version. Since this register does not exist
|
||||
@@ -510,25 +502,16 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt)
|
||||
*/
|
||||
gt->info.gmdid = xe_mmio_read32(>->mmio, GMD_ID);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
return 0;
|
||||
|
||||
err_force_wake:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gt_init_with_all_forcewake(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
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)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto err_force_wake;
|
||||
}
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
xe_gt_mcr_set_implicit_defaults(gt);
|
||||
xe_wa_process_gt(gt);
|
||||
@@ -537,20 +520,20 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
|
||||
|
||||
err = xe_gt_clock_init(gt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
xe_mocs_init(gt);
|
||||
err = xe_execlist_init(gt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
err = xe_hw_engines_init(gt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
err = xe_uc_init_post_hwconfig(>->uc);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
if (xe_gt_is_main_type(gt)) {
|
||||
/*
|
||||
@@ -561,10 +544,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
|
||||
|
||||
gt->usm.bb_pool = xe_sa_bo_manager_init(gt_to_tile(gt),
|
||||
IS_DGFX(xe) ? SZ_1M : SZ_512K, 16);
|
||||
if (IS_ERR(gt->usm.bb_pool)) {
|
||||
err = PTR_ERR(gt->usm.bb_pool);
|
||||
goto err_force_wake;
|
||||
}
|
||||
if (IS_ERR(gt->usm.bb_pool))
|
||||
return PTR_ERR(gt->usm.bb_pool);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,12 +554,12 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
|
||||
|
||||
err = xe_migrate_init(tile->migrate);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = xe_uc_load_hw(>->uc);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
/* Configure default CCS mode of 1 engine with all resources */
|
||||
if (xe_gt_ccs_mode_enabled(gt)) {
|
||||
@@ -592,14 +573,7 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
|
||||
if (IS_SRIOV_PF(gt_to_xe(gt)))
|
||||
xe_gt_sriov_pf_init_hw(gt);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return 0;
|
||||
|
||||
err_force_wake:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xe_gt_fini(void *arg)
|
||||
@@ -902,56 +876,42 @@ void xe_gt_reset_async(struct xe_gt *gt)
|
||||
|
||||
void xe_gt_suspend_prepare(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
xe_uc_suspend_prepare(>->uc);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
int xe_gt_suspend(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
xe_gt_dbg(gt, "suspending\n");
|
||||
xe_gt_sanitize(gt);
|
||||
|
||||
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_msg;
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
|
||||
xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
err = xe_uc_suspend(>->uc);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
if (err) {
|
||||
xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
xe_gt_idle_disable_pg(gt);
|
||||
|
||||
xe_gt_disable_host_l2_vram(gt);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_gt_dbg(gt, "suspended\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_msg:
|
||||
err = -ETIMEDOUT;
|
||||
err_force_wake:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_gt_err(gt, "suspend failed (%pe)\n", ERR_PTR(err));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void xe_gt_shutdown(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
do_gt_reset(gt);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -976,32 +936,72 @@ int xe_gt_sanitize_freq(struct xe_gt *gt)
|
||||
|
||||
int xe_gt_resume(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
xe_gt_dbg(gt, "resuming\n");
|
||||
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_msg;
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
|
||||
xe_gt_err(gt, "resume failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
err = do_gt_restart(gt);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
return err;
|
||||
|
||||
xe_gt_idle_enable_pg(gt);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_gt_dbg(gt, "resumed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_msg:
|
||||
err = -ETIMEDOUT;
|
||||
err_force_wake:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_gt_err(gt, "resume failed (%pe)\n", ERR_PTR(err));
|
||||
/**
|
||||
* xe_gt_runtime_suspend() - GT runtime suspend
|
||||
* @gt: the GT object
|
||||
*
|
||||
* Return: 0 on success, negative error code otherwise.
|
||||
*/
|
||||
int xe_gt_runtime_suspend(struct xe_gt *gt)
|
||||
{
|
||||
xe_gt_dbg(gt, "runtime suspending\n");
|
||||
|
||||
return err;
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
|
||||
xe_gt_err(gt, "runtime suspend failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
xe_uc_runtime_suspend(>->uc);
|
||||
xe_gt_disable_host_l2_vram(gt);
|
||||
|
||||
xe_gt_dbg(gt, "runtime suspended\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_gt_runtime_resume() - GT runtime resume
|
||||
* @gt: the GT object
|
||||
*
|
||||
* Return: 0 on success, negative error code otherwise.
|
||||
*/
|
||||
int xe_gt_runtime_resume(struct xe_gt *gt)
|
||||
{
|
||||
xe_gt_dbg(gt, "runtime resuming\n");
|
||||
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) {
|
||||
xe_gt_err(gt, "runtime resume failed (%pe)\n", ERR_PTR(-ETIMEDOUT));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
xe_gt_enable_host_l2_vram(gt);
|
||||
xe_uc_runtime_resume(>->uc);
|
||||
|
||||
xe_gt_dbg(gt, "runtime resumed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct xe_hw_engine *xe_gt_hw_engine(struct xe_gt *gt,
|
||||
|
||||
@@ -58,6 +58,8 @@ int xe_gt_suspend(struct xe_gt *gt);
|
||||
void xe_gt_shutdown(struct xe_gt *gt);
|
||||
int xe_gt_resume(struct xe_gt *gt);
|
||||
void xe_gt_reset_async(struct xe_gt *gt);
|
||||
int xe_gt_runtime_resume(struct xe_gt *gt);
|
||||
int xe_gt_runtime_suspend(struct xe_gt *gt);
|
||||
void xe_gt_sanitize(struct xe_gt *gt);
|
||||
int xe_gt_sanitize_freq(struct xe_gt *gt);
|
||||
|
||||
|
||||
@@ -105,35 +105,24 @@ int xe_gt_debugfs_show_with_rpm(struct seq_file *m, void *data)
|
||||
struct drm_info_node *node = m->private;
|
||||
struct xe_gt *gt = node_to_gt(node);
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
ret = xe_gt_debugfs_simple_show(m, data);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret;
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return xe_gt_debugfs_simple_show(m, data);
|
||||
}
|
||||
|
||||
static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
|
||||
{
|
||||
struct xe_hw_engine *hwe;
|
||||
enum xe_hw_engine_id id;
|
||||
unsigned int fw_ref;
|
||||
int ret = 0;
|
||||
|
||||
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)) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto fw_put;
|
||||
}
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for_each_hw_engine(hwe, gt, id)
|
||||
xe_hw_engine_print(hwe, p);
|
||||
|
||||
fw_put:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int steering(struct xe_gt *gt, struct drm_printer *p)
|
||||
@@ -220,6 +209,7 @@ static const struct drm_info_list vf_safe_debugfs_list[] = {
|
||||
{ "default_lrc_vcs", .show = xe_gt_debugfs_show_with_rpm, .data = vcs_default_lrc },
|
||||
{ "default_lrc_vecs", .show = xe_gt_debugfs_show_with_rpm, .data = vecs_default_lrc },
|
||||
{ "hwconfig", .show = xe_gt_debugfs_show_with_rpm, .data = hwconfig },
|
||||
{ "pat_sw_config", .show = xe_gt_debugfs_simple_show, .data = xe_pat_dump_sw_config },
|
||||
};
|
||||
|
||||
/* everything else should be added here */
|
||||
@@ -269,9 +259,8 @@ static void force_reset(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
xe_gt_reset_async(gt);
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
static ssize_t force_reset_write(struct file *file,
|
||||
@@ -297,9 +286,8 @@ static void force_reset_sync(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
xe_gt_reset(gt);
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
static ssize_t force_reset_sync_write(struct file *file,
|
||||
|
||||
@@ -70,9 +70,8 @@ static ssize_t act_freq_show(struct kobject *kobj,
|
||||
struct xe_guc_pc *pc = dev_to_pc(dev);
|
||||
u32 freq;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
freq = xe_guc_pc_get_act_freq(pc);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
|
||||
return sysfs_emit(buf, "%d\n", freq);
|
||||
}
|
||||
@@ -86,9 +85,8 @@ static ssize_t cur_freq_show(struct kobject *kobj,
|
||||
u32 freq;
|
||||
ssize_t ret;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
ret = xe_guc_pc_get_cur_freq(pc, &freq);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -113,9 +111,8 @@ static ssize_t rpe_freq_show(struct kobject *kobj,
|
||||
struct xe_guc_pc *pc = dev_to_pc(dev);
|
||||
u32 freq;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
freq = xe_guc_pc_get_rpe_freq(pc);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
|
||||
return sysfs_emit(buf, "%d\n", freq);
|
||||
}
|
||||
@@ -128,9 +125,8 @@ static ssize_t rpa_freq_show(struct kobject *kobj,
|
||||
struct xe_guc_pc *pc = dev_to_pc(dev);
|
||||
u32 freq;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
freq = xe_guc_pc_get_rpa_freq(pc);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
|
||||
return sysfs_emit(buf, "%d\n", freq);
|
||||
}
|
||||
@@ -154,9 +150,8 @@ static ssize_t min_freq_show(struct kobject *kobj,
|
||||
u32 freq;
|
||||
ssize_t ret;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
ret = xe_guc_pc_get_min_freq(pc, &freq);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -175,9 +170,8 @@ static ssize_t min_freq_store(struct kobject *kobj,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
ret = xe_guc_pc_set_min_freq(pc, freq);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -193,9 +187,8 @@ static ssize_t max_freq_show(struct kobject *kobj,
|
||||
u32 freq;
|
||||
ssize_t ret;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
ret = xe_guc_pc_get_max_freq(pc, &freq);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -214,9 +207,8 @@ static ssize_t max_freq_store(struct kobject *kobj,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
ret = xe_guc_pc_set_max_freq(pc, freq);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -243,9 +235,8 @@ static ssize_t power_profile_store(struct kobject *kobj,
|
||||
struct xe_guc_pc *pc = dev_to_pc(dev);
|
||||
int err;
|
||||
|
||||
xe_pm_runtime_get(dev_to_xe(dev));
|
||||
guard(xe_pm_runtime)(dev_to_xe(dev));
|
||||
err = xe_guc_pc_set_power_profile(pc, buff);
|
||||
xe_pm_runtime_put(dev_to_xe(dev));
|
||||
|
||||
return err ?: count;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,6 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt)
|
||||
struct xe_gt_idle *gtidle = >->gtidle;
|
||||
struct xe_mmio *mmio = >->mmio;
|
||||
u32 vcs_mask, vecs_mask;
|
||||
unsigned int fw_ref;
|
||||
int i, j;
|
||||
|
||||
if (IS_SRIOV_VF(xe))
|
||||
@@ -137,7 +136,7 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt)
|
||||
}
|
||||
}
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (xe->info.skip_guc_pc) {
|
||||
/*
|
||||
* GuC sets the hysteresis value when GuC PC is enabled
|
||||
@@ -154,13 +153,11 @@ void xe_gt_idle_enable_pg(struct xe_gt *gt)
|
||||
VDN_MFXVDENC_POWERGATE_ENABLE(2));
|
||||
|
||||
xe_mmio_write32(mmio, POWERGATE_ENABLE, gtidle->powergate_enable);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
void xe_gt_idle_disable_pg(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_gt_idle *gtidle = >->gtidle;
|
||||
unsigned int fw_ref;
|
||||
|
||||
if (IS_SRIOV_VF(gt_to_xe(gt)))
|
||||
return;
|
||||
@@ -168,9 +165,8 @@ void xe_gt_idle_disable_pg(struct xe_gt *gt)
|
||||
xe_device_assert_mem_access(gt_to_xe(gt));
|
||||
gtidle->powergate_enable = 0;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
xe_mmio_write32(>->mmio, POWERGATE_ENABLE, gtidle->powergate_enable);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +185,6 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p)
|
||||
enum xe_gt_idle_state state;
|
||||
u32 pg_enabled, pg_status = 0;
|
||||
u32 vcs_mask, vecs_mask;
|
||||
unsigned int fw_ref;
|
||||
int n;
|
||||
/*
|
||||
* Media Slices
|
||||
@@ -226,14 +221,12 @@ int xe_gt_idle_pg_print(struct xe_gt *gt, struct drm_printer *p)
|
||||
|
||||
/* Do not wake the GT to read powergating status */
|
||||
if (state != GT_IDLE_C6) {
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
pg_enabled = xe_mmio_read32(>->mmio, POWERGATE_ENABLE);
|
||||
pg_status = xe_mmio_read32(>->mmio, POWERGATE_DOMAIN_STATUS);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
if (gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK) {
|
||||
@@ -271,13 +264,9 @@ static ssize_t name_show(struct kobject *kobj,
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct xe_gt_idle *gtidle = dev_to_gtidle(dev);
|
||||
struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
|
||||
ssize_t ret;
|
||||
|
||||
xe_pm_runtime_get(pc_to_xe(pc));
|
||||
ret = sysfs_emit(buff, "%s\n", gtidle->name);
|
||||
xe_pm_runtime_put(pc_to_xe(pc));
|
||||
|
||||
return ret;
|
||||
guard(xe_pm_runtime)(pc_to_xe(pc));
|
||||
return sysfs_emit(buff, "%s\n", gtidle->name);
|
||||
}
|
||||
static struct kobj_attribute name_attr = __ATTR_RO(name);
|
||||
|
||||
@@ -289,9 +278,8 @@ static ssize_t idle_status_show(struct kobject *kobj,
|
||||
struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
|
||||
enum xe_gt_idle_state state;
|
||||
|
||||
xe_pm_runtime_get(pc_to_xe(pc));
|
||||
state = gtidle->idle_status(pc);
|
||||
xe_pm_runtime_put(pc_to_xe(pc));
|
||||
scoped_guard(xe_pm_runtime, pc_to_xe(pc))
|
||||
state = gtidle->idle_status(pc);
|
||||
|
||||
return sysfs_emit(buff, "%s\n", gt_idle_state_to_string(state));
|
||||
}
|
||||
@@ -319,9 +307,8 @@ static ssize_t idle_residency_ms_show(struct kobject *kobj,
|
||||
struct xe_guc_pc *pc = gtidle_to_pc(gtidle);
|
||||
u64 residency;
|
||||
|
||||
xe_pm_runtime_get(pc_to_xe(pc));
|
||||
residency = xe_gt_idle_residency_msec(gtidle);
|
||||
xe_pm_runtime_put(pc_to_xe(pc));
|
||||
scoped_guard(xe_pm_runtime, pc_to_xe(pc))
|
||||
residency = xe_gt_idle_residency_msec(gtidle);
|
||||
|
||||
return sysfs_emit(buff, "%llu\n", residency);
|
||||
}
|
||||
@@ -404,21 +391,17 @@ void xe_gt_idle_enable_c6(struct xe_gt *gt)
|
||||
|
||||
int xe_gt_idle_disable_c6(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
|
||||
xe_device_assert_mem_access(gt_to_xe(gt));
|
||||
|
||||
if (IS_SRIOV_VF(gt_to_xe(gt)))
|
||||
return 0;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
xe_mmio_write32(>->mmio, RC_CONTROL, 0);
|
||||
xe_mmio_write32(>->mmio, RC_STATE, 0);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -269,7 +269,8 @@ static u32 encode_config_ggtt(u32 *cfg, const struct xe_gt_sriov_config *config,
|
||||
}
|
||||
|
||||
/* Return: number of configuration dwords written */
|
||||
static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool details)
|
||||
static u32 encode_config(struct xe_gt *gt, u32 *cfg, const struct xe_gt_sriov_config *config,
|
||||
bool details)
|
||||
{
|
||||
u32 n = 0;
|
||||
|
||||
@@ -303,9 +304,11 @@ static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool
|
||||
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_PREEMPT_TIMEOUT);
|
||||
cfg[n++] = config->preempt_timeout;
|
||||
|
||||
#define encode_threshold_config(TAG, ...) ({ \
|
||||
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \
|
||||
cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \
|
||||
#define encode_threshold_config(TAG, NAME, VER...) ({ \
|
||||
if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), true, VER)) { \
|
||||
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_THRESHOLD_##TAG); \
|
||||
cfg[n++] = config->thresholds[MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG)]; \
|
||||
} \
|
||||
});
|
||||
|
||||
MAKE_XE_GUC_KLV_THRESHOLDS_SET(encode_threshold_config);
|
||||
@@ -328,7 +331,7 @@ static int pf_push_full_vf_config(struct xe_gt *gt, unsigned int vfid)
|
||||
return -ENOBUFS;
|
||||
|
||||
cfg = xe_guc_buf_cpu_ptr(buf);
|
||||
num_dwords = encode_config(cfg, config, true);
|
||||
num_dwords = encode_config(gt, cfg, config, true);
|
||||
xe_gt_assert(gt, num_dwords <= max_cfg_dwords);
|
||||
|
||||
if (xe_gt_is_media_type(gt)) {
|
||||
@@ -2518,7 +2521,7 @@ ssize_t xe_gt_sriov_pf_config_save(struct xe_gt *gt, unsigned int vfid, void *bu
|
||||
ret = -ENOBUFS;
|
||||
} else {
|
||||
config = pf_pick_vf_config(gt, vfid);
|
||||
ret = encode_config(buf, config, false) * sizeof(u32);
|
||||
ret = encode_config(gt, buf, config, false) * sizeof(u32);
|
||||
}
|
||||
}
|
||||
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
|
||||
@@ -2551,11 +2554,13 @@ static int pf_restore_vf_config_klv(struct xe_gt *gt, unsigned int vfid,
|
||||
return pf_provision_preempt_timeout(gt, vfid, value[0]);
|
||||
|
||||
/* auto-generate case statements */
|
||||
#define define_threshold_key_to_provision_case(TAG, ...) \
|
||||
#define define_threshold_key_to_provision_case(TAG, NAME, VER...) \
|
||||
case MAKE_GUC_KLV_VF_CFG_THRESHOLD_KEY(TAG): \
|
||||
BUILD_BUG_ON(MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG) != 1u); \
|
||||
if (len != MAKE_GUC_KLV_VF_CFG_THRESHOLD_LEN(TAG)) \
|
||||
return -EBADMSG; \
|
||||
if (IF_ARGS(!GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), false, VER)) \
|
||||
return -EKEYREJECTED; \
|
||||
return pf_provision_threshold(gt, vfid, \
|
||||
MAKE_XE_GUC_KLV_THRESHOLD_INDEX(TAG), \
|
||||
value[0]);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "xe_gt_sriov_pf_monitor.h"
|
||||
#include "xe_gt_sriov_pf_policy.h"
|
||||
#include "xe_gt_sriov_pf_service.h"
|
||||
#include "xe_guc.h"
|
||||
#include "xe_pm.h"
|
||||
#include "xe_sriov_pf.h"
|
||||
#include "xe_sriov_pf_provision.h"
|
||||
@@ -123,11 +124,10 @@ static int POLICY##_set(void *data, u64 val) \
|
||||
if (val > (TYPE)~0ull) \
|
||||
return -EOVERFLOW; \
|
||||
\
|
||||
xe_pm_runtime_get(xe); \
|
||||
guard(xe_pm_runtime)(xe); \
|
||||
err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \
|
||||
if (!err) \
|
||||
xe_sriov_pf_provision_set_custom_mode(xe); \
|
||||
xe_pm_runtime_put(xe); \
|
||||
\
|
||||
return err; \
|
||||
} \
|
||||
@@ -189,12 +189,11 @@ static int CONFIG##_set(void *data, u64 val) \
|
||||
if (val > (TYPE)~0ull) \
|
||||
return -EOVERFLOW; \
|
||||
\
|
||||
xe_pm_runtime_get(xe); \
|
||||
guard(xe_pm_runtime)(xe); \
|
||||
err = xe_sriov_pf_wait_ready(xe) ?: \
|
||||
xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
|
||||
if (!err) \
|
||||
xe_sriov_pf_provision_set_custom_mode(xe); \
|
||||
xe_pm_runtime_put(xe); \
|
||||
\
|
||||
return err; \
|
||||
} \
|
||||
@@ -249,11 +248,10 @@ static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index in
|
||||
if (val > (u32)~0ull)
|
||||
return -EOVERFLOW;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
|
||||
if (!err)
|
||||
xe_sriov_pf_provision_set_custom_mode(xe);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -304,9 +302,11 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
|
||||
&sched_priority_fops);
|
||||
|
||||
/* register all threshold attributes */
|
||||
#define register_threshold_attribute(TAG, NAME, ...) \
|
||||
debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
|
||||
&NAME##_fops);
|
||||
#define register_threshold_attribute(TAG, NAME, VER...) ({ \
|
||||
if (IF_ARGS(GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, VER), true, VER)) \
|
||||
debugfs_create_file_unsafe("threshold_" #NAME, 0644, parent, parent, \
|
||||
&NAME##_fops); \
|
||||
});
|
||||
MAKE_XE_GUC_KLV_THRESHOLDS_SET(register_threshold_attribute)
|
||||
#undef register_threshold_attribute
|
||||
}
|
||||
@@ -358,9 +358,8 @@ static ssize_t control_write(struct file *file, const char __user *buf, size_t c
|
||||
xe_gt_assert(gt, sizeof(cmd) > strlen(control_cmds[n].cmd));
|
||||
|
||||
if (sysfs_streq(cmd, control_cmds[n].cmd)) {
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
ret = control_cmds[n].fn ? (*control_cmds[n].fn)(gt, vfid) : 0;
|
||||
xe_pm_runtime_put(xe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1026,7 +1026,7 @@ static void action_ring_cleanup(void *arg)
|
||||
|
||||
static void pf_gt_migration_check_support(struct xe_gt *gt)
|
||||
{
|
||||
if (GUC_FIRMWARE_VER(>->uc.guc) < MAKE_GUC_VER(70, 54, 0))
|
||||
if (!GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 54))
|
||||
xe_sriov_pf_migration_disable(gt_to_xe(gt), "requires GuC version >= 70.54.0");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
@@ -41,6 +42,37 @@
|
||||
|
||||
#define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
|
||||
|
||||
#ifdef CONFIG_DRM_XE_DEBUG
|
||||
enum VF_MIGRATION_WAIT_POINTS {
|
||||
VF_MIGRATION_WAIT_RESFIX_START = BIT(0),
|
||||
VF_MIGRATION_WAIT_FIXUPS = BIT(1),
|
||||
VF_MIGRATION_WAIT_RESTART_JOBS = BIT(2),
|
||||
VF_MIGRATION_WAIT_RESFIX_DONE = BIT(3),
|
||||
};
|
||||
|
||||
#define VF_MIGRATION_WAIT_DELAY_IN_MS 1000
|
||||
static void vf_post_migration_inject_wait(struct xe_gt *gt,
|
||||
enum VF_MIGRATION_WAIT_POINTS wait)
|
||||
{
|
||||
while (gt->sriov.vf.migration.debug.resfix_stoppers & wait) {
|
||||
xe_gt_dbg(gt,
|
||||
"*TESTING* injecting %u ms delay due to resfix_stoppers=%#x, to continue clear %#x\n",
|
||||
VF_MIGRATION_WAIT_DELAY_IN_MS,
|
||||
gt->sriov.vf.migration.debug.resfix_stoppers, wait);
|
||||
|
||||
msleep(VF_MIGRATION_WAIT_DELAY_IN_MS);
|
||||
}
|
||||
}
|
||||
|
||||
#define VF_MIGRATION_INJECT_WAIT(gt, _POS) ({ \
|
||||
struct xe_gt *__gt = (gt); \
|
||||
vf_post_migration_inject_wait(__gt, VF_MIGRATION_WAIT_##_POS); \
|
||||
})
|
||||
|
||||
#else
|
||||
#define VF_MIGRATION_INJECT_WAIT(_gt, ...) typecheck(struct xe_gt *, (_gt))
|
||||
#endif
|
||||
|
||||
static int guc_action_vf_reset(struct xe_guc *guc)
|
||||
{
|
||||
u32 request[GUC_HXG_REQUEST_MSG_MIN_LEN] = {
|
||||
@@ -299,12 +331,13 @@ void xe_gt_sriov_vf_guc_versions(struct xe_gt *gt,
|
||||
*found = gt->sriov.vf.guc_version;
|
||||
}
|
||||
|
||||
static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
|
||||
static int guc_action_vf_resfix_start(struct xe_guc *guc, u16 marker)
|
||||
{
|
||||
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),
|
||||
FIELD_PREP(GUC_HXG_REQUEST_MSG_0_ACTION, GUC_ACTION_VF2GUC_RESFIX_START) |
|
||||
FIELD_PREP(VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER, marker),
|
||||
};
|
||||
int ret;
|
||||
|
||||
@@ -313,28 +346,43 @@ static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
|
||||
return ret > 0 ? -EPROTO : ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static int vf_notify_resfix_done(struct xe_gt *gt)
|
||||
static int vf_resfix_start(struct xe_gt *gt, u16 marker)
|
||||
{
|
||||
struct xe_guc *guc = >->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");
|
||||
VF_MIGRATION_INJECT_WAIT(gt, RESFIX_START);
|
||||
|
||||
return err;
|
||||
xe_gt_sriov_dbg_verbose(gt, "Sending resfix start marker %u\n", marker);
|
||||
|
||||
return guc_action_vf_resfix_start(guc, marker);
|
||||
}
|
||||
|
||||
static int guc_action_vf_resfix_done(struct xe_guc *guc, u16 marker)
|
||||
{
|
||||
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_RESFIX_DONE) |
|
||||
FIELD_PREP(VF2GUC_RESFIX_DONE_REQUEST_MSG_0_MARKER, marker),
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = xe_guc_mmio_send(guc, request, ARRAY_SIZE(request));
|
||||
|
||||
return ret > 0 ? -EPROTO : ret;
|
||||
}
|
||||
|
||||
static int vf_resfix_done(struct xe_gt *gt, u16 marker)
|
||||
{
|
||||
struct xe_guc *guc = >->uc.guc;
|
||||
|
||||
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
|
||||
|
||||
xe_gt_sriov_dbg_verbose(gt, "Sending resfix done marker %u\n", marker);
|
||||
|
||||
return guc_action_vf_resfix_done(guc, marker);
|
||||
}
|
||||
|
||||
static int guc_action_query_single_klv(struct xe_guc *guc, u32 key,
|
||||
@@ -1123,12 +1171,8 @@ static bool vf_post_migration_shutdown(struct xe_gt *gt)
|
||||
return true;
|
||||
}
|
||||
|
||||
spin_lock_irq(>->sriov.vf.migration.lock);
|
||||
gt->sriov.vf.migration.recovery_queued = false;
|
||||
spin_unlock_irq(>->sriov.vf.migration.lock);
|
||||
|
||||
xe_guc_ct_flush_and_stop(>->uc.guc.ct);
|
||||
xe_guc_submit_pause(>->uc.guc);
|
||||
xe_guc_submit_pause_vf(>->uc.guc);
|
||||
xe_tlb_inval_reset(>->tlb_inval);
|
||||
|
||||
return false;
|
||||
@@ -1144,6 +1188,8 @@ static int vf_post_migration_fixups(struct xe_gt *gt)
|
||||
void *buf = gt->sriov.vf.migration.scratch;
|
||||
int err;
|
||||
|
||||
VF_MIGRATION_INJECT_WAIT(gt, FIXUPS);
|
||||
|
||||
/* xe_gt_sriov_vf_query_config will fixup the GGTT addresses */
|
||||
err = xe_gt_sriov_vf_query_config(gt);
|
||||
if (err)
|
||||
@@ -1162,13 +1208,22 @@ static int vf_post_migration_fixups(struct xe_gt *gt)
|
||||
|
||||
static void vf_post_migration_rearm(struct xe_gt *gt)
|
||||
{
|
||||
VF_MIGRATION_INJECT_WAIT(gt, RESTART_JOBS);
|
||||
|
||||
/*
|
||||
* Make sure interrupts on the new HW are properly set. The GuC IRQ
|
||||
* must be working at this point, since the recovery did started,
|
||||
* but the rest was not enabled using the procedure from spec.
|
||||
*/
|
||||
xe_irq_resume(gt_to_xe(gt));
|
||||
|
||||
xe_guc_ct_restart(>->uc.guc.ct);
|
||||
xe_guc_submit_unpause_prepare(>->uc.guc);
|
||||
xe_guc_submit_unpause_prepare_vf(>->uc.guc);
|
||||
}
|
||||
|
||||
static void vf_post_migration_kickstart(struct xe_gt *gt)
|
||||
{
|
||||
xe_guc_submit_unpause(>->uc.guc);
|
||||
xe_guc_submit_unpause_vf(>->uc.guc);
|
||||
}
|
||||
|
||||
static void vf_post_migration_abort(struct xe_gt *gt)
|
||||
@@ -1183,37 +1238,49 @@ static void vf_post_migration_abort(struct xe_gt *gt)
|
||||
xe_guc_submit_pause_abort(>->uc.guc);
|
||||
}
|
||||
|
||||
static int vf_post_migration_notify_resfix_done(struct xe_gt *gt)
|
||||
static int vf_post_migration_resfix_done(struct xe_gt *gt, u16 marker)
|
||||
{
|
||||
bool skip_resfix = false;
|
||||
VF_MIGRATION_INJECT_WAIT(gt, RESFIX_DONE);
|
||||
|
||||
spin_lock_irq(>->sriov.vf.migration.lock);
|
||||
if (gt->sriov.vf.migration.recovery_queued) {
|
||||
skip_resfix = true;
|
||||
xe_gt_sriov_dbg(gt, "another recovery imminent, resfix skipped\n");
|
||||
} else {
|
||||
if (gt->sriov.vf.migration.recovery_queued)
|
||||
xe_gt_sriov_dbg(gt, "another recovery imminent\n");
|
||||
else
|
||||
WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false);
|
||||
}
|
||||
spin_unlock_irq(>->sriov.vf.migration.lock);
|
||||
|
||||
if (skip_resfix)
|
||||
return -EAGAIN;
|
||||
return vf_resfix_done(gt, marker);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure interrupts on the new HW are properly set. The GuC IRQ
|
||||
* must be working at this point, since the recovery did started,
|
||||
* but the rest was not enabled using the procedure from spec.
|
||||
*/
|
||||
xe_irq_resume(gt_to_xe(gt));
|
||||
static int vf_post_migration_resfix_start(struct xe_gt *gt, u16 marker)
|
||||
{
|
||||
int err;
|
||||
|
||||
return vf_notify_resfix_done(gt);
|
||||
err = vf_resfix_start(gt, marker);
|
||||
|
||||
guard(spinlock_irq) (>->sriov.vf.migration.lock);
|
||||
gt->sriov.vf.migration.recovery_queued = false;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static u16 vf_post_migration_next_resfix_marker(struct xe_gt *gt)
|
||||
{
|
||||
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
|
||||
|
||||
BUILD_BUG_ON(1 + ((typeof(gt->sriov.vf.migration.resfix_marker))~0) >
|
||||
FIELD_MAX(VF2GUC_RESFIX_START_REQUEST_MSG_0_MARKER));
|
||||
|
||||
/* add 1 to avoid zero-marker */
|
||||
return 1 + gt->sriov.vf.migration.resfix_marker++;
|
||||
}
|
||||
|
||||
static void vf_post_migration_recovery(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
int err;
|
||||
u16 marker;
|
||||
bool retry;
|
||||
int err;
|
||||
|
||||
xe_gt_sriov_dbg(gt, "migration recovery in progress\n");
|
||||
|
||||
@@ -1227,15 +1294,30 @@ static void vf_post_migration_recovery(struct xe_gt *gt)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
marker = vf_post_migration_next_resfix_marker(gt);
|
||||
|
||||
err = vf_post_migration_resfix_start(gt, marker);
|
||||
if (unlikely(err)) {
|
||||
xe_gt_sriov_err(gt, "Recovery failed at GuC RESFIX_START step (%pe)\n",
|
||||
ERR_PTR(err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = vf_post_migration_fixups(gt);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
vf_post_migration_rearm(gt);
|
||||
|
||||
err = vf_post_migration_notify_resfix_done(gt);
|
||||
if (err && err != -EAGAIN)
|
||||
err = vf_post_migration_resfix_done(gt, marker);
|
||||
if (err) {
|
||||
if (err == -EREMCHG)
|
||||
goto queue;
|
||||
|
||||
xe_gt_sriov_err(gt, "Recovery failed at GuC RESFIX_DONE step (%pe)\n",
|
||||
ERR_PTR(err));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vf_post_migration_kickstart(gt);
|
||||
|
||||
|
||||
@@ -69,4 +69,16 @@ void xe_gt_sriov_vf_debugfs_register(struct xe_gt *gt, struct dentry *root)
|
||||
vfdentry->d_inode->i_private = gt;
|
||||
|
||||
drm_debugfs_create_files(vf_info, ARRAY_SIZE(vf_info), vfdentry, minor);
|
||||
|
||||
/*
|
||||
* /sys/kernel/debug/dri/BDF/
|
||||
* ├── tile0
|
||||
* ├── gt0
|
||||
* ├── vf
|
||||
* ├── resfix_stoppers
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
|
||||
debugfs_create_x8("resfix_stoppers", 0600, vfdentry,
|
||||
>->sriov.vf.migration.debug.resfix_stoppers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,19 @@ struct xe_gt_sriov_vf_migration {
|
||||
wait_queue_head_t wq;
|
||||
/** @scratch: Scratch memory for VF recovery */
|
||||
void *scratch;
|
||||
/** @debug: Debug hooks for delaying migration */
|
||||
struct {
|
||||
/**
|
||||
* @debug.resfix_stoppers: Stop and wait at different stages
|
||||
* during post migration recovery
|
||||
*/
|
||||
u8 resfix_stoppers;
|
||||
} debug;
|
||||
/**
|
||||
* @resfix_marker: Marker sent on start and on end of post-migration
|
||||
* steps.
|
||||
*/
|
||||
u8 resfix_marker;
|
||||
/** @recovery_teardown: VF post migration recovery is being torn down */
|
||||
bool recovery_teardown;
|
||||
/** @recovery_queued: VF post migration recovery in queued */
|
||||
|
||||
@@ -66,6 +66,16 @@ static const char *const stat_description[__XE_GT_STATS_NUM_IDS] = {
|
||||
DEF_STAT_STR(SVM_4K_BIND_US, "svm_4K_bind_us"),
|
||||
DEF_STAT_STR(SVM_64K_BIND_US, "svm_64K_bind_us"),
|
||||
DEF_STAT_STR(SVM_2M_BIND_US, "svm_2M_bind_us"),
|
||||
DEF_STAT_STR(HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT,
|
||||
"hw_engine_group_suspend_lr_queue_count"),
|
||||
DEF_STAT_STR(HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT,
|
||||
"hw_engine_group_skip_lr_queue_count"),
|
||||
DEF_STAT_STR(HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT,
|
||||
"hw_engine_group_wait_dma_queue_count"),
|
||||
DEF_STAT_STR(HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
|
||||
"hw_engine_group_suspend_lr_queue_us"),
|
||||
DEF_STAT_STR(HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
|
||||
"hw_engine_group_wait_dma_queue_us"),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef _XE_GT_STATS_H_
|
||||
#define _XE_GT_STATS_H_
|
||||
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include "xe_gt_stats_types.h"
|
||||
|
||||
struct xe_gt;
|
||||
@@ -23,4 +25,34 @@ xe_gt_stats_incr(struct xe_gt *gt, const enum xe_gt_stats_id id,
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* xe_gt_stats_ktime_us_delta() - Get delta in microseconds between now and a
|
||||
* start time
|
||||
* @start: Start time
|
||||
*
|
||||
* Helper for GT stats to get delta in microseconds between now and a start
|
||||
* time, compiles out if GT stats are disabled.
|
||||
*
|
||||
* Return: Delta in microseconds between now and a start time
|
||||
*/
|
||||
static inline s64 xe_gt_stats_ktime_us_delta(ktime_t start)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_DEBUG_FS) ?
|
||||
ktime_us_delta(ktime_get(), start) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_gt_stats_ktime_get() - Get current ktime
|
||||
*
|
||||
* Helper for GT stats to get current ktime, compiles out if GT stats are
|
||||
* disabled.
|
||||
*
|
||||
* Return: Get current ktime
|
||||
*/
|
||||
static inline ktime_t xe_gt_stats_ktime_get(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_DEBUG_FS) ? ktime_get() : 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,6 +44,11 @@ enum xe_gt_stats_id {
|
||||
XE_GT_STATS_ID_SVM_4K_BIND_US,
|
||||
XE_GT_STATS_ID_SVM_64K_BIND_US,
|
||||
XE_GT_STATS_ID_SVM_2M_BIND_US,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
|
||||
/* must be the last entry */
|
||||
__XE_GT_STATS_NUM_IDS,
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
struct xe_reg reg;
|
||||
u32 val, mask;
|
||||
u32 mask;
|
||||
|
||||
if (xe_gt_is_media_type(gt))
|
||||
reg = MTL_MEDIA_PERF_LIMIT_REASONS;
|
||||
@@ -97,11 +97,8 @@ u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
|
||||
else
|
||||
mask = GT0_PERF_LIMIT_REASONS_MASK;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
val = xe_mmio_read32(>->mmio, reg) & mask;
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return val;
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return xe_mmio_read32(>->mmio, reg) & mask;
|
||||
}
|
||||
|
||||
static bool is_throttled_by(struct xe_gt *gt, u32 mask)
|
||||
|
||||
@@ -140,6 +140,11 @@ struct xe_gt {
|
||||
u64 engine_mask;
|
||||
/** @info.gmdid: raw GMD_ID value from hardware */
|
||||
u32 gmdid;
|
||||
/**
|
||||
* @multi_queue_engine_class_mask: Bitmask of engine classes with
|
||||
* multi queue support enabled.
|
||||
*/
|
||||
u16 multi_queue_engine_class_mask;
|
||||
/** @info.id: Unique ID of this GT within the PCI Device */
|
||||
u8 id;
|
||||
/** @info.has_indirect_ring_state: GT has indirect ring state support */
|
||||
|
||||
@@ -104,7 +104,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc)
|
||||
u32 offset = guc_bo_ggtt_addr(guc, guc->log.bo) >> PAGE_SHIFT;
|
||||
u32 flags;
|
||||
|
||||
#if (((CRASH_BUFFER_SIZE) % SZ_1M) == 0)
|
||||
#if (((XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE) % SZ_1M) == 0)
|
||||
#define LOG_UNIT SZ_1M
|
||||
#define LOG_FLAG GUC_LOG_LOG_ALLOC_UNITS
|
||||
#else
|
||||
@@ -112,7 +112,7 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc)
|
||||
#define LOG_FLAG 0
|
||||
#endif
|
||||
|
||||
#if (((CAPTURE_BUFFER_SIZE) % SZ_1M) == 0)
|
||||
#if (((XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE) % SZ_1M) == 0)
|
||||
#define CAPTURE_UNIT SZ_1M
|
||||
#define CAPTURE_FLAG GUC_LOG_CAPTURE_ALLOC_UNITS
|
||||
#else
|
||||
@@ -120,20 +120,21 @@ static u32 guc_ctl_log_params_flags(struct xe_guc *guc)
|
||||
#define CAPTURE_FLAG 0
|
||||
#endif
|
||||
|
||||
BUILD_BUG_ON(!CRASH_BUFFER_SIZE);
|
||||
BUILD_BUG_ON(!IS_ALIGNED(CRASH_BUFFER_SIZE, LOG_UNIT));
|
||||
BUILD_BUG_ON(!DEBUG_BUFFER_SIZE);
|
||||
BUILD_BUG_ON(!IS_ALIGNED(DEBUG_BUFFER_SIZE, LOG_UNIT));
|
||||
BUILD_BUG_ON(!CAPTURE_BUFFER_SIZE);
|
||||
BUILD_BUG_ON(!IS_ALIGNED(CAPTURE_BUFFER_SIZE, CAPTURE_UNIT));
|
||||
BUILD_BUG_ON(!XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE);
|
||||
BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE, LOG_UNIT));
|
||||
BUILD_BUG_ON(!XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE);
|
||||
BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE, LOG_UNIT));
|
||||
BUILD_BUG_ON(!XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE);
|
||||
BUILD_BUG_ON(!IS_ALIGNED(XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE, CAPTURE_UNIT));
|
||||
|
||||
flags = GUC_LOG_VALID |
|
||||
GUC_LOG_NOTIFY_ON_HALF_FULL |
|
||||
CAPTURE_FLAG |
|
||||
LOG_FLAG |
|
||||
FIELD_PREP(GUC_LOG_CRASH, CRASH_BUFFER_SIZE / LOG_UNIT - 1) |
|
||||
FIELD_PREP(GUC_LOG_DEBUG, DEBUG_BUFFER_SIZE / LOG_UNIT - 1) |
|
||||
FIELD_PREP(GUC_LOG_CAPTURE, CAPTURE_BUFFER_SIZE / CAPTURE_UNIT - 1) |
|
||||
FIELD_PREP(GUC_LOG_CRASH_DUMP, XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE / LOG_UNIT - 1) |
|
||||
FIELD_PREP(GUC_LOG_EVENT_DATA, XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE / LOG_UNIT - 1) |
|
||||
FIELD_PREP(GUC_LOG_STATE_CAPTURE, XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE /
|
||||
CAPTURE_UNIT - 1) |
|
||||
FIELD_PREP(GUC_LOG_BUF_ADDR, offset);
|
||||
|
||||
#undef LOG_UNIT
|
||||
@@ -660,11 +661,9 @@ static void guc_fini_hw(void *arg)
|
||||
{
|
||||
struct xe_guc *guc = arg;
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
unsigned int fw_ref;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
xe_uc_sanitize_reset(&guc_to_gt(guc)->uc);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL)
|
||||
xe_uc_sanitize_reset(&guc_to_gt(guc)->uc);
|
||||
|
||||
guc_g2g_fini(guc);
|
||||
}
|
||||
@@ -768,6 +767,10 @@ int xe_guc_init(struct xe_guc *guc)
|
||||
if (!xe_uc_fw_is_enabled(&guc->fw))
|
||||
return 0;
|
||||
|
||||
/* Disable page reclaim if GuC FW does not support */
|
||||
if (GUC_SUBMIT_VER(guc) < MAKE_GUC_VER(1, 14, 0))
|
||||
xe->info.has_page_reclaim_hw_assist = false;
|
||||
|
||||
if (IS_SRIOV_VF(xe)) {
|
||||
ret = xe_guc_ct_init(&guc->ct);
|
||||
if (ret)
|
||||
@@ -1485,6 +1488,12 @@ int xe_guc_mmio_send_recv(struct xe_guc *guc, const u32 *request,
|
||||
u32 hint = FIELD_GET(GUC_HXG_FAILURE_MSG_0_HINT, header);
|
||||
u32 error = FIELD_GET(GUC_HXG_FAILURE_MSG_0_ERROR, header);
|
||||
|
||||
if (unlikely(error == XE_GUC_RESPONSE_VF_MIGRATED)) {
|
||||
xe_gt_dbg(gt, "GuC mmio request %#x rejected due to MIGRATION (hint %#x)\n",
|
||||
request[0], hint);
|
||||
return -EREMCHG;
|
||||
}
|
||||
|
||||
xe_gt_err(gt, "GuC mmio request %#x: failure %#x hint %#x\n",
|
||||
request[0], error, hint);
|
||||
return -ENXIO;
|
||||
@@ -1618,18 +1627,51 @@ int xe_guc_start(struct xe_guc *guc)
|
||||
return xe_guc_submit_start(guc);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_runtime_suspend() - GuC runtime suspend
|
||||
* @guc: The GuC object
|
||||
*
|
||||
* Stop further runs of submission tasks on given GuC and runtime suspend
|
||||
* GuC CT.
|
||||
*/
|
||||
void xe_guc_runtime_suspend(struct xe_guc *guc)
|
||||
{
|
||||
xe_guc_submit_pause(guc);
|
||||
xe_guc_submit_disable(guc);
|
||||
xe_guc_ct_runtime_suspend(&guc->ct);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_runtime_resume() - GuC runtime resume
|
||||
* @guc: The GuC object
|
||||
*
|
||||
* Runtime resume GuC CT and allow further runs of submission tasks on
|
||||
* given GuC.
|
||||
*/
|
||||
void xe_guc_runtime_resume(struct xe_guc *guc)
|
||||
{
|
||||
/*
|
||||
* Runtime PM flows are not applicable for VFs, so it's safe to
|
||||
* directly enable IRQ.
|
||||
*/
|
||||
guc_enable_irq(guc);
|
||||
|
||||
xe_guc_ct_runtime_resume(&guc->ct);
|
||||
xe_guc_submit_enable(guc);
|
||||
xe_guc_submit_unpause(guc);
|
||||
}
|
||||
|
||||
void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p)
|
||||
{
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
unsigned int fw_ref;
|
||||
u32 status;
|
||||
int i;
|
||||
|
||||
xe_uc_fw_print(&guc->fw, p);
|
||||
|
||||
if (!IS_SRIOV_VF(gt_to_xe(gt))) {
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
status = xe_mmio_read32(>->mmio, GUC_STATUS);
|
||||
@@ -1649,8 +1691,6 @@ void xe_guc_print_info(struct xe_guc *guc, struct drm_printer *p)
|
||||
drm_printf(p, "\t%2d: \t0x%x\n",
|
||||
i, xe_mmio_read32(>->mmio, SOFT_SCRATCH(i)));
|
||||
}
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
drm_puts(p, "\n");
|
||||
|
||||
@@ -18,10 +18,16 @@
|
||||
*/
|
||||
#define MAKE_GUC_VER(maj, min, pat) (((maj) << 16) | ((min) << 8) | (pat))
|
||||
#define MAKE_GUC_VER_STRUCT(ver) MAKE_GUC_VER((ver).major, (ver).minor, (ver).patch)
|
||||
#define MAKE_GUC_VER_ARGS(ver...) \
|
||||
(BUILD_BUG_ON_ZERO(COUNT_ARGS(ver) < 2 || COUNT_ARGS(ver) > 3) + \
|
||||
MAKE_GUC_VER(PICK_ARG1(ver), PICK_ARG2(ver), IF_ARGS(PICK_ARG3(ver), 0, PICK_ARG3(ver))))
|
||||
|
||||
#define GUC_SUBMIT_VER(guc) \
|
||||
MAKE_GUC_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_COMPATIBILITY])
|
||||
#define GUC_FIRMWARE_VER(guc) \
|
||||
MAKE_GUC_VER_STRUCT((guc)->fw.versions.found[XE_UC_FW_VER_RELEASE])
|
||||
#define GUC_FIRMWARE_VER_AT_LEAST(guc, ver...) \
|
||||
xe_guc_fw_version_at_least((guc), MAKE_GUC_VER_ARGS(ver))
|
||||
|
||||
struct drm_printer;
|
||||
|
||||
@@ -35,6 +41,8 @@ int xe_guc_upload(struct xe_guc *guc);
|
||||
int xe_guc_min_load_for_hwconfig(struct xe_guc *guc);
|
||||
int xe_guc_enable_communication(struct xe_guc *guc);
|
||||
int xe_guc_opt_in_features_enable(struct xe_guc *guc);
|
||||
void xe_guc_runtime_suspend(struct xe_guc *guc);
|
||||
void xe_guc_runtime_resume(struct xe_guc *guc);
|
||||
int xe_guc_suspend(struct xe_guc *guc);
|
||||
void xe_guc_notify(struct xe_guc *guc);
|
||||
int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr);
|
||||
@@ -94,4 +102,19 @@ static inline struct drm_device *guc_to_drm(struct xe_guc *guc)
|
||||
return &guc_to_xe(guc)->drm;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_fw_version_at_least() - Check if GuC is at least of given version.
|
||||
* @guc: the &xe_guc
|
||||
* @ver: the version to check
|
||||
*
|
||||
* The @ver should be prepared using MAKE_GUC_VER(major, minor, patch).
|
||||
*
|
||||
* Return: true if loaded GuC firmware is at least of given version,
|
||||
* false otherwise.
|
||||
*/
|
||||
static inline bool xe_guc_fw_version_at_least(const struct xe_guc *guc, u32 ver)
|
||||
{
|
||||
return GUC_FIRMWARE_VER(guc) >= ver;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -317,7 +317,7 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
|
||||
offset = guc_ads_waklv_offset(ads);
|
||||
remain = guc_ads_waklv_size(ads);
|
||||
|
||||
if (XE_GT_WA(gt, 14019882105) || XE_GT_WA(gt, 16021333562))
|
||||
if (XE_GT_WA(gt, 16021333562))
|
||||
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
|
||||
GUC_WORKAROUND_KLV_BLOCK_INTERRUPTS_WHEN_MGSR_BLOCKED);
|
||||
if (XE_GT_WA(gt, 18024947630))
|
||||
@@ -347,10 +347,10 @@ static void guc_waklv_init(struct xe_guc_ads *ads)
|
||||
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
|
||||
GUC_WORKAROUND_KLV_ID_BACK_TO_BACK_RCS_ENGINE_RESET);
|
||||
|
||||
if (GUC_FIRMWARE_VER(>->uc.guc) >= MAKE_GUC_VER(70, 44, 0) && XE_GT_WA(gt, 16026508708))
|
||||
if (GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 44) && XE_GT_WA(gt, 16026508708))
|
||||
guc_waklv_enable(ads, NULL, 0, &offset, &remain,
|
||||
GUC_WA_KLV_RESET_BB_STACK_PTR_ON_VF_SWITCH);
|
||||
if (GUC_FIRMWARE_VER(>->uc.guc) >= MAKE_GUC_VER(70, 47, 0) && XE_GT_WA(gt, 16026007364)) {
|
||||
if (GUC_FIRMWARE_VER_AT_LEAST(>->uc.guc, 70, 47) && XE_GT_WA(gt, 16026007364)) {
|
||||
u32 data[] = {
|
||||
0x0,
|
||||
0xF,
|
||||
|
||||
@@ -30,7 +30,7 @@ static int guc_buf_cache_init(struct xe_guc_buf_cache *cache, u32 size)
|
||||
struct xe_gt *gt = cache_to_gt(cache);
|
||||
struct xe_sa_manager *sam;
|
||||
|
||||
sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32));
|
||||
sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32), 0);
|
||||
if (IS_ERR(sam))
|
||||
return PTR_ERR(sam);
|
||||
cache->sam = sam;
|
||||
|
||||
@@ -843,7 +843,7 @@ static void check_guc_capture_size(struct xe_guc *guc)
|
||||
{
|
||||
int capture_size = guc_capture_output_size_est(guc);
|
||||
int spare_size = capture_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER;
|
||||
u32 buffer_size = xe_guc_log_section_size_capture(&guc->log);
|
||||
u32 buffer_size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE;
|
||||
|
||||
/*
|
||||
* NOTE: capture_size is much smaller than the capture region
|
||||
@@ -949,7 +949,7 @@ guc_capture_init_node(struct xe_guc *guc, struct __guc_capture_parsed_output *no
|
||||
* ADS module also calls separately for PF vs VF.
|
||||
*
|
||||
* --> alloc B: GuC output capture buf (registered via guc_init_params(log_param))
|
||||
* Size = #define CAPTURE_BUFFER_SIZE (warns if on too-small)
|
||||
* Size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE (warns if on too-small)
|
||||
* Note2: 'x 3' to hold multiple capture groups
|
||||
*
|
||||
* GUC Runtime notify capture:
|
||||
@@ -1367,7 +1367,7 @@ static int __guc_capture_flushlog_complete(struct xe_guc *guc)
|
||||
{
|
||||
u32 action[] = {
|
||||
XE_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE,
|
||||
GUC_LOG_BUFFER_CAPTURE
|
||||
GUC_LOG_TYPE_STATE_CAPTURE
|
||||
};
|
||||
|
||||
return xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action));
|
||||
@@ -1384,8 +1384,8 @@ static void __guc_capture_process_output(struct xe_guc *guc)
|
||||
u32 log_buf_state_offset;
|
||||
u32 src_data_offset;
|
||||
|
||||
log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_BUFFER_CAPTURE;
|
||||
src_data_offset = xe_guc_get_log_buffer_offset(&guc->log, GUC_LOG_BUFFER_CAPTURE);
|
||||
log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_TYPE_STATE_CAPTURE;
|
||||
src_data_offset = XE_GUC_LOG_STATE_CAPTURE_OFFSET;
|
||||
|
||||
/*
|
||||
* Make a copy of the state structure, inside GuC log buffer
|
||||
@@ -1395,15 +1395,15 @@ static void __guc_capture_process_output(struct xe_guc *guc)
|
||||
xe_map_memcpy_from(guc_to_xe(guc), &log_buf_state_local, &guc->log.bo->vmap,
|
||||
log_buf_state_offset, sizeof(struct guc_log_buffer_state));
|
||||
|
||||
buffer_size = xe_guc_get_log_buffer_size(&guc->log, GUC_LOG_BUFFER_CAPTURE);
|
||||
buffer_size = XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE;
|
||||
read_offset = log_buf_state_local.read_ptr;
|
||||
write_offset = log_buf_state_local.sampled_write_ptr;
|
||||
full_count = FIELD_GET(GUC_LOG_BUFFER_STATE_BUFFER_FULL_CNT, log_buf_state_local.flags);
|
||||
|
||||
/* Bookkeeping stuff */
|
||||
tmp = FIELD_GET(GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE, log_buf_state_local.flags);
|
||||
guc->log.stats[GUC_LOG_BUFFER_CAPTURE].flush += tmp;
|
||||
new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_BUFFER_CAPTURE,
|
||||
guc->log.stats[GUC_LOG_TYPE_STATE_CAPTURE].flush += tmp;
|
||||
new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_TYPE_STATE_CAPTURE,
|
||||
full_count);
|
||||
|
||||
/* Now copy the actual logs. */
|
||||
|
||||
@@ -42,6 +42,21 @@ static void ct_exit_safe_mode(struct xe_guc_ct *ct);
|
||||
static void guc_ct_change_state(struct xe_guc_ct *ct,
|
||||
enum xe_guc_ct_state state);
|
||||
|
||||
static struct xe_guc *ct_to_guc(struct xe_guc_ct *ct)
|
||||
{
|
||||
return container_of(ct, struct xe_guc, ct);
|
||||
}
|
||||
|
||||
static struct xe_gt *ct_to_gt(struct xe_guc_ct *ct)
|
||||
{
|
||||
return container_of(ct, struct xe_gt, uc.guc.ct);
|
||||
}
|
||||
|
||||
static struct xe_device *ct_to_xe(struct xe_guc_ct *ct)
|
||||
{
|
||||
return gt_to_xe(ct_to_gt(ct));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
|
||||
enum {
|
||||
/* Internal states, not error conditions */
|
||||
@@ -68,14 +83,101 @@ enum {
|
||||
static void ct_dead_worker_func(struct work_struct *w);
|
||||
static void ct_dead_capture(struct xe_guc_ct *ct, struct guc_ctb *ctb, u32 reason_code);
|
||||
|
||||
#define CT_DEAD(ct, ctb, reason_code) ct_dead_capture((ct), (ctb), CT_DEAD_##reason_code)
|
||||
static void ct_dead_fini(struct xe_guc_ct *ct)
|
||||
{
|
||||
cancel_work_sync(&ct->dead.worker);
|
||||
}
|
||||
|
||||
static void ct_dead_init(struct xe_guc_ct *ct)
|
||||
{
|
||||
spin_lock_init(&ct->dead.lock);
|
||||
INIT_WORK(&ct->dead.worker, ct_dead_worker_func);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
stack_depot_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fast_req_stack_save(struct xe_guc_ct *ct, unsigned int slot)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
unsigned long entries[SZ_32];
|
||||
unsigned int n;
|
||||
|
||||
n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
|
||||
/* May be called under spinlock, so avoid sleeping */
|
||||
ct->fast_req[slot].stack = stack_depot_save(entries, n, GFP_NOWAIT);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fast_req_dump(struct xe_guc_ct *ct, u16 fence, unsigned int slot)
|
||||
{
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
char *buf __cleanup(kfree) = kmalloc(SZ_4K, GFP_NOWAIT);
|
||||
|
||||
if (buf && stack_depot_snprint(ct->fast_req[slot].stack, buf, SZ_4K, 0))
|
||||
xe_gt_err(gt, "Fence 0x%x was used by action %#04x sent at:\n%s\n",
|
||||
fence, ct->fast_req[slot].action, buf);
|
||||
else
|
||||
xe_gt_err(gt, "Fence 0x%x was used by action %#04x [failed to retrieve stack]\n",
|
||||
fence, ct->fast_req[slot].action);
|
||||
#else
|
||||
xe_gt_err(gt, "Fence 0x%x was used by action %#04x\n",
|
||||
fence, ct->fast_req[slot].action);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fast_req_report(struct xe_guc_ct *ct, u16 fence)
|
||||
{
|
||||
u16 fence_min = U16_MAX, fence_max = 0;
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
unsigned int n;
|
||||
|
||||
lockdep_assert_held(&ct->lock);
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(ct->fast_req); n++) {
|
||||
if (ct->fast_req[n].fence < fence_min)
|
||||
fence_min = ct->fast_req[n].fence;
|
||||
if (ct->fast_req[n].fence > fence_max)
|
||||
fence_max = ct->fast_req[n].fence;
|
||||
|
||||
if (ct->fast_req[n].fence != fence)
|
||||
continue;
|
||||
|
||||
return fast_req_dump(ct, fence, n);
|
||||
}
|
||||
|
||||
xe_gt_warn(gt, "Fence 0x%x not found - tracking buffer wrapped? [range = 0x%x -> 0x%x, next = 0x%X]\n",
|
||||
fence, fence_min, fence_max, ct->fence_seqno);
|
||||
}
|
||||
|
||||
static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action)
|
||||
{
|
||||
unsigned int slot = fence % ARRAY_SIZE(ct->fast_req);
|
||||
|
||||
fast_req_stack_save(ct, slot);
|
||||
ct->fast_req[slot].fence = fence;
|
||||
ct->fast_req[slot].action = action;
|
||||
}
|
||||
|
||||
#define CT_DEAD(ct, ctb, reason_code) ct_dead_capture((ct), (ctb), CT_DEAD_##reason_code)
|
||||
|
||||
#else
|
||||
|
||||
static void ct_dead_fini(struct xe_guc_ct *ct) { }
|
||||
static void ct_dead_init(struct xe_guc_ct *ct) { }
|
||||
|
||||
static void fast_req_report(struct xe_guc_ct *ct, u16 fence) { }
|
||||
static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action) { }
|
||||
|
||||
#define CT_DEAD(ct, ctb, reason) \
|
||||
do { \
|
||||
struct guc_ctb *_ctb = (ctb); \
|
||||
if (_ctb) \
|
||||
_ctb->info.broken = true; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
/* Used when a CT send wants to block and / or receive data */
|
||||
@@ -112,24 +214,6 @@ static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence)
|
||||
return g2h_fence->seqno == ~0x0;
|
||||
}
|
||||
|
||||
static struct xe_guc *
|
||||
ct_to_guc(struct xe_guc_ct *ct)
|
||||
{
|
||||
return container_of(ct, struct xe_guc, ct);
|
||||
}
|
||||
|
||||
static struct xe_gt *
|
||||
ct_to_gt(struct xe_guc_ct *ct)
|
||||
{
|
||||
return container_of(ct, struct xe_gt, uc.guc.ct);
|
||||
}
|
||||
|
||||
static struct xe_device *
|
||||
ct_to_xe(struct xe_guc_ct *ct)
|
||||
{
|
||||
return gt_to_xe(ct_to_gt(ct));
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: GuC CTB Blob
|
||||
*
|
||||
@@ -169,8 +253,11 @@ ct_to_xe(struct xe_guc_ct *ct)
|
||||
#define CTB_DESC_SIZE ALIGN(sizeof(struct guc_ct_buffer_desc), SZ_2K)
|
||||
#define CTB_H2G_BUFFER_OFFSET (CTB_DESC_SIZE * 2)
|
||||
#define CTB_H2G_BUFFER_SIZE (SZ_4K)
|
||||
#define CTB_H2G_BUFFER_DWORDS (CTB_H2G_BUFFER_SIZE / sizeof(u32))
|
||||
#define CTB_G2H_BUFFER_SIZE (SZ_128K)
|
||||
#define CTB_G2H_BUFFER_DWORDS (CTB_G2H_BUFFER_SIZE / sizeof(u32))
|
||||
#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 2)
|
||||
#define G2H_ROOM_BUFFER_DWORDS (CTB_G2H_BUFFER_DWORDS / 2)
|
||||
|
||||
/**
|
||||
* xe_guc_ct_queue_proc_time_jiffies - Return maximum time to process a full
|
||||
@@ -199,9 +286,7 @@ static void guc_ct_fini(struct drm_device *drm, void *arg)
|
||||
{
|
||||
struct xe_guc_ct *ct = arg;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
|
||||
cancel_work_sync(&ct->dead.worker);
|
||||
#endif
|
||||
ct_dead_fini(ct);
|
||||
ct_exit_safe_mode(ct);
|
||||
destroy_workqueue(ct->g2h_wq);
|
||||
xa_destroy(&ct->fence_lookup);
|
||||
@@ -239,13 +324,8 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct)
|
||||
xa_init(&ct->fence_lookup);
|
||||
INIT_WORK(&ct->g2h_worker, g2h_worker_func);
|
||||
INIT_DELAYED_WORK(&ct->safe_mode_worker, safe_mode_worker_func);
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
|
||||
spin_lock_init(&ct->dead.lock);
|
||||
INIT_WORK(&ct->dead.worker, ct_dead_worker_func);
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
stack_depot_init();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ct_dead_init(ct);
|
||||
init_waitqueue_head(&ct->wq);
|
||||
init_waitqueue_head(&ct->g2h_fence_wq);
|
||||
|
||||
@@ -326,7 +406,7 @@ int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct)
|
||||
static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g,
|
||||
struct iosys_map *map)
|
||||
{
|
||||
h2g->info.size = CTB_H2G_BUFFER_SIZE / sizeof(u32);
|
||||
h2g->info.size = CTB_H2G_BUFFER_DWORDS;
|
||||
h2g->info.resv_space = 0;
|
||||
h2g->info.tail = 0;
|
||||
h2g->info.head = 0;
|
||||
@@ -344,8 +424,8 @@ static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g,
|
||||
static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h,
|
||||
struct iosys_map *map)
|
||||
{
|
||||
g2h->info.size = CTB_G2H_BUFFER_SIZE / sizeof(u32);
|
||||
g2h->info.resv_space = G2H_ROOM_BUFFER_SIZE / sizeof(u32);
|
||||
g2h->info.size = CTB_G2H_BUFFER_DWORDS;
|
||||
g2h->info.resv_space = G2H_ROOM_BUFFER_DWORDS;
|
||||
g2h->info.head = 0;
|
||||
g2h->info.tail = 0;
|
||||
g2h->info.space = CIRC_SPACE(g2h->info.tail, g2h->info.head,
|
||||
@@ -640,6 +720,39 @@ void xe_guc_ct_stop(struct xe_guc_ct *ct)
|
||||
stop_g2h_handler(ct);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_ct_runtime_suspend() - GuC CT runtime suspend
|
||||
* @ct: the &xe_guc_ct
|
||||
*
|
||||
* Set GuC CT to disabled state.
|
||||
*/
|
||||
void xe_guc_ct_runtime_suspend(struct xe_guc_ct *ct)
|
||||
{
|
||||
struct guc_ctb *g2h = &ct->ctbs.g2h;
|
||||
u32 credits = CIRC_SPACE(0, 0, CTB_G2H_BUFFER_DWORDS) - G2H_ROOM_BUFFER_DWORDS;
|
||||
|
||||
/* We should be back to guc_ct_ctb_g2h_init() values */
|
||||
xe_gt_assert(ct_to_gt(ct), g2h->info.space == credits);
|
||||
|
||||
/*
|
||||
* Since we're already in runtime suspend path, we shouldn't have pending
|
||||
* messages. But if there happen to be any, we'd probably want them to be
|
||||
* thrown as errors for further investigation.
|
||||
*/
|
||||
xe_guc_ct_disable(ct);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_ct_runtime_resume() - GuC CT runtime resume
|
||||
* @ct: the &xe_guc_ct
|
||||
*
|
||||
* Restart GuC CT and set it to enabled state.
|
||||
*/
|
||||
void xe_guc_ct_runtime_resume(struct xe_guc_ct *ct)
|
||||
{
|
||||
xe_guc_ct_restart(ct);
|
||||
}
|
||||
|
||||
static bool h2g_has_room(struct xe_guc_ct *ct, u32 cmd_len)
|
||||
{
|
||||
struct guc_ctb *h2g = &ct->ctbs.h2g;
|
||||
@@ -747,28 +860,6 @@ static void g2h_release_space(struct xe_guc_ct *ct, u32 g2h_len)
|
||||
spin_unlock_irq(&ct->fast_lock);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
|
||||
static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action)
|
||||
{
|
||||
unsigned int slot = fence % ARRAY_SIZE(ct->fast_req);
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
unsigned long entries[SZ_32];
|
||||
unsigned int n;
|
||||
|
||||
n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
|
||||
|
||||
/* May be called under spinlock, so avoid sleeping */
|
||||
ct->fast_req[slot].stack = stack_depot_save(entries, n, GFP_NOWAIT);
|
||||
#endif
|
||||
ct->fast_req[slot].fence = fence;
|
||||
ct->fast_req[slot].action = action;
|
||||
}
|
||||
#else
|
||||
static void fast_req_track(struct xe_guc_ct *ct, u16 fence, u16 action)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The CT protocol accepts a 16 bits fence. This field is fully owned by the
|
||||
* driver, the GuC will just copy it to the reply message. Since we need to
|
||||
@@ -1310,10 +1401,12 @@ static int parse_g2h_event(struct xe_guc_ct *ct, u32 *msg, u32 len)
|
||||
lockdep_assert_held(&ct->lock);
|
||||
|
||||
switch (action) {
|
||||
case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE:
|
||||
case XE_GUC_ACTION_SCHED_CONTEXT_MODE_DONE:
|
||||
case XE_GUC_ACTION_DEREGISTER_CONTEXT_DONE:
|
||||
case XE_GUC_ACTION_SCHED_ENGINE_MODE_DONE:
|
||||
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
|
||||
case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
|
||||
g2h_release_space(ct, len);
|
||||
}
|
||||
|
||||
@@ -1338,55 +1431,6 @@ static int guc_crash_process_msg(struct xe_guc_ct *ct, u32 action)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
|
||||
static void fast_req_report(struct xe_guc_ct *ct, u16 fence)
|
||||
{
|
||||
u16 fence_min = U16_MAX, fence_max = 0;
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
bool found = false;
|
||||
unsigned int n;
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
char *buf;
|
||||
#endif
|
||||
|
||||
lockdep_assert_held(&ct->lock);
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(ct->fast_req); n++) {
|
||||
if (ct->fast_req[n].fence < fence_min)
|
||||
fence_min = ct->fast_req[n].fence;
|
||||
if (ct->fast_req[n].fence > fence_max)
|
||||
fence_max = ct->fast_req[n].fence;
|
||||
|
||||
if (ct->fast_req[n].fence != fence)
|
||||
continue;
|
||||
found = true;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
buf = kmalloc(SZ_4K, GFP_NOWAIT);
|
||||
if (buf && stack_depot_snprint(ct->fast_req[n].stack, buf, SZ_4K, 0))
|
||||
xe_gt_err(gt, "Fence 0x%x was used by action %#04x sent at:\n%s",
|
||||
fence, ct->fast_req[n].action, buf);
|
||||
else
|
||||
xe_gt_err(gt, "Fence 0x%x was used by action %#04x [failed to retrieve stack]\n",
|
||||
fence, ct->fast_req[n].action);
|
||||
kfree(buf);
|
||||
#else
|
||||
xe_gt_err(gt, "Fence 0x%x was used by action %#04x\n",
|
||||
fence, ct->fast_req[n].action);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
xe_gt_warn(gt, "Fence 0x%x not found - tracking buffer wrapped? [range = 0x%x -> 0x%x, next = 0x%X]\n",
|
||||
fence, fence_min, fence_max, ct->fence_seqno);
|
||||
}
|
||||
#else
|
||||
static void fast_req_report(struct xe_guc_ct *ct, u16 fence)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len)
|
||||
{
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
@@ -1549,6 +1593,15 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
|
||||
ret = xe_guc_pagefault_handler(guc, payload, adj_len);
|
||||
break;
|
||||
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
|
||||
case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
|
||||
/*
|
||||
* Page reclamation is an extension of TLB invalidation. Both
|
||||
* operations share the same seqno and fence. When either
|
||||
* action completes, we need to signal the corresponding
|
||||
* fence. Since the handling logic (lookup fence by seqno,
|
||||
* fence signalling) is identical, we use the same handler
|
||||
* for both G2H events.
|
||||
*/
|
||||
ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
|
||||
break;
|
||||
case XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF:
|
||||
@@ -1572,6 +1625,13 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
|
||||
ret = xe_guc_g2g_test_notification(guc, payload, adj_len);
|
||||
break;
|
||||
#endif
|
||||
case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CONTEXT_CGP_SYNC_DONE:
|
||||
ret = xe_guc_exec_queue_cgp_sync_done_handler(guc, payload, adj_len);
|
||||
break;
|
||||
case XE_GUC_ACTION_NOTIFY_MULTI_QUEUE_CGP_CONTEXT_ERROR:
|
||||
ret = xe_guc_exec_queue_cgp_context_error_handler(guc, payload,
|
||||
adj_len);
|
||||
break;
|
||||
default:
|
||||
xe_gt_err(gt, "unexpected G2H action 0x%04x\n", action);
|
||||
}
|
||||
@@ -1714,6 +1774,7 @@ static int g2h_read(struct xe_guc_ct *ct, u32 *msg, bool fast_path)
|
||||
switch (action) {
|
||||
case XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC:
|
||||
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
|
||||
case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
|
||||
break; /* Process these in fast-path */
|
||||
default:
|
||||
return 0;
|
||||
@@ -1750,6 +1811,12 @@ static void g2h_fast_path(struct xe_guc_ct *ct, u32 *msg, u32 len)
|
||||
ret = xe_guc_pagefault_handler(guc, payload, adj_len);
|
||||
break;
|
||||
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
|
||||
case XE_GUC_ACTION_PAGE_RECLAMATION_DONE:
|
||||
/*
|
||||
* Seqno and fence handling of page reclamation and TLB
|
||||
* invalidation is identical, so we can use the same handler
|
||||
* for both actions.
|
||||
*/
|
||||
__g2h_release_space(ct, len);
|
||||
ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
|
||||
break;
|
||||
|
||||
@@ -17,6 +17,8 @@ int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct);
|
||||
int xe_guc_ct_enable(struct xe_guc_ct *ct);
|
||||
int xe_guc_ct_restart(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_disable(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_runtime_resume(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_runtime_suspend(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_stop(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_fast_path(struct xe_guc_ct *ct);
|
||||
|
||||
@@ -70,13 +70,9 @@ static int guc_debugfs_show(struct seq_file *m, void *data)
|
||||
struct xe_gt *gt = grandparent->d_inode->i_private;
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
int (*print)(struct xe_guc *, struct drm_printer *) = node->info_ent->data;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
ret = print(>->uc.guc, &p);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret;
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return print(>->uc.guc, &p);
|
||||
}
|
||||
|
||||
static int guc_log(struct xe_guc *guc, struct drm_printer *p)
|
||||
@@ -85,6 +81,12 @@ static int guc_log(struct xe_guc *guc, struct drm_printer *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int guc_log_lfd(struct xe_guc *guc, struct drm_printer *p)
|
||||
{
|
||||
xe_guc_log_print_lfd(&guc->log, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int guc_log_dmesg(struct xe_guc *guc, struct drm_printer *p)
|
||||
{
|
||||
xe_guc_log_print_dmesg(&guc->log);
|
||||
@@ -121,6 +123,7 @@ static const struct drm_info_list slpc_debugfs_list[] = {
|
||||
/* everything else should be added here */
|
||||
static const struct drm_info_list pf_only_debugfs_list[] = {
|
||||
{ "guc_log", .show = guc_debugfs_show, .data = guc_log },
|
||||
{ "guc_log_lfd", .show = guc_debugfs_show, .data = guc_log_lfd },
|
||||
{ "guc_log_dmesg", .show = guc_debugfs_show, .data = guc_log_dmesg },
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#define G2H_LEN_DW_DEREGISTER_CONTEXT 3
|
||||
#define G2H_LEN_DW_TLB_INVALIDATE 3
|
||||
#define G2H_LEN_DW_G2G_NOTIFY_MIN 3
|
||||
#define G2H_LEN_DW_MULTI_QUEUE_CONTEXT 3
|
||||
#define G2H_LEN_DW_PAGE_RECLAMATION 3
|
||||
|
||||
#define GUC_ID_MAX 65535
|
||||
#define GUC_ID_UNKNOWN 0xffffffff
|
||||
@@ -62,6 +64,8 @@ struct guc_ctxt_registration_info {
|
||||
u32 wq_base_lo;
|
||||
u32 wq_base_hi;
|
||||
u32 wq_size;
|
||||
u32 cgp_lo;
|
||||
u32 cgp_hi;
|
||||
u32 hwlrca_lo;
|
||||
u32 hwlrca_hi;
|
||||
};
|
||||
@@ -91,9 +95,9 @@ struct guc_update_exec_queue_policy {
|
||||
#define GUC_LOG_NOTIFY_ON_HALF_FULL BIT(1)
|
||||
#define GUC_LOG_CAPTURE_ALLOC_UNITS BIT(2)
|
||||
#define GUC_LOG_LOG_ALLOC_UNITS BIT(3)
|
||||
#define GUC_LOG_CRASH REG_GENMASK(5, 4)
|
||||
#define GUC_LOG_DEBUG REG_GENMASK(9, 6)
|
||||
#define GUC_LOG_CAPTURE REG_GENMASK(11, 10)
|
||||
#define GUC_LOG_CRASH_DUMP REG_GENMASK(5, 4)
|
||||
#define GUC_LOG_EVENT_DATA REG_GENMASK(9, 6)
|
||||
#define GUC_LOG_STATE_CAPTURE REG_GENMASK(11, 10)
|
||||
#define GUC_LOG_BUF_ADDR REG_GENMASK(31, 12)
|
||||
|
||||
#define GUC_CTL_WA 1
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
* ABI and the associated &NAME, that may be used in code or debugfs/sysfs::
|
||||
*
|
||||
* define(TAG, NAME)
|
||||
*
|
||||
* If required, KLVs can be labeled with GuC firmware version that added them::
|
||||
*
|
||||
* define(TAG, NAME, MAJOR, MINOR)
|
||||
* define(TAG, NAME, MAJOR, MINOR, PATCH)
|
||||
*/
|
||||
#define MAKE_XE_GUC_KLV_THRESHOLDS_SET(define) \
|
||||
define(CAT_ERR, cat_error_count) \
|
||||
@@ -32,6 +37,7 @@
|
||||
define(H2G_STORM, guc_time_us) \
|
||||
define(IRQ_STORM, irq_time_us) \
|
||||
define(DOORBELL_STORM, doorbell_time_us) \
|
||||
define(MULTI_LRC_COUNT, multi_lrc_count, 70, 53)\
|
||||
/* end */
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
|
||||
#include <linux/fault-inject.h>
|
||||
|
||||
#include <linux/utsname.h>
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include "abi/guc_lfd_abi.h"
|
||||
#include "regs/xe_guc_regs.h"
|
||||
#include "xe_bo.h"
|
||||
#include "xe_devcoredump.h"
|
||||
@@ -19,6 +21,77 @@
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_module.h"
|
||||
|
||||
#define GUC_LOG_CHUNK_SIZE SZ_2M
|
||||
|
||||
/* Magic keys define */
|
||||
#define GUC_LFD_DRIVER_KEY_STREAMING 0x8086AAAA474C5346
|
||||
#define GUC_LFD_LOG_BUFFER_MARKER_2 0xDEADFEED
|
||||
#define GUC_LFD_CRASH_DUMP_BUFFER_MARKER_2 0x8086DEAD
|
||||
#define GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_2 0xBEEFFEED
|
||||
#define GUC_LFD_LOG_BUFFER_MARKER_1V2 0xCABBA9E6
|
||||
#define GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_1V2 0xCABBA9F7
|
||||
#define GUC_LFD_DATA_HEADER_MAGIC 0x8086
|
||||
|
||||
/* LFD supported LIC type range */
|
||||
#define GUC_LIC_TYPE_FIRST GUC_LIC_TYPE_GUC_SW_VERSION
|
||||
#define GUC_LIC_TYPE_LAST GUC_LIC_TYPE_BUILD_PLATFORM_ID
|
||||
#define GUC_LFD_TYPE_FW_RANGE_FIRST GUC_LFD_TYPE_FW_VERSION
|
||||
#define GUC_LFD_TYPE_FW_RANGE_LAST GUC_LFD_TYPE_BUILD_PLATFORM_ID
|
||||
|
||||
#define GUC_LOG_BUFFER_STATE_HEADER_LENGTH 4096
|
||||
#define GUC_LOG_BUFFER_INIT_CONFIG 3
|
||||
|
||||
struct guc_log_buffer_entry_list {
|
||||
u32 offset;
|
||||
u32 rd_ptr;
|
||||
u32 wr_ptr;
|
||||
u32 wrap_offset;
|
||||
u32 buf_size;
|
||||
};
|
||||
|
||||
struct guc_lic_save {
|
||||
u32 version;
|
||||
/*
|
||||
* Array of init config KLV values.
|
||||
* Range from GUC_LOG_LIC_TYPE_FIRST to GUC_LOG_LIC_TYPE_LAST
|
||||
*/
|
||||
u32 values[GUC_LIC_TYPE_LAST - GUC_LIC_TYPE_FIRST + 1];
|
||||
struct guc_log_buffer_entry_list entry[GUC_LOG_BUFFER_INIT_CONFIG];
|
||||
};
|
||||
|
||||
static struct guc_log_buffer_entry_markers {
|
||||
u32 key[2];
|
||||
} const entry_markers[GUC_LOG_BUFFER_INIT_CONFIG + 1] = {
|
||||
{{
|
||||
GUC_LFD_LOG_BUFFER_MARKER_1V2,
|
||||
GUC_LFD_LOG_BUFFER_MARKER_2
|
||||
}},
|
||||
{{
|
||||
GUC_LFD_LOG_BUFFER_MARKER_1V2,
|
||||
GUC_LFD_CRASH_DUMP_BUFFER_MARKER_2
|
||||
}},
|
||||
{{
|
||||
GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_1V2,
|
||||
GUC_LFD_STATE_CAPTURE_BUFFER_MARKER_2
|
||||
}},
|
||||
{{
|
||||
GUC_LIC_MAGIC,
|
||||
(FIELD_PREP_CONST(GUC_LIC_VERSION_MASK_MAJOR, GUC_LIC_VERSION_MAJOR) |
|
||||
FIELD_PREP_CONST(GUC_LIC_VERSION_MASK_MINOR, GUC_LIC_VERSION_MINOR))
|
||||
}}
|
||||
};
|
||||
|
||||
static struct guc_log_lic_lfd_map {
|
||||
u32 lic;
|
||||
u32 lfd;
|
||||
} const lic_lfd_type_map[] = {
|
||||
{GUC_LIC_TYPE_GUC_SW_VERSION, GUC_LFD_TYPE_FW_VERSION},
|
||||
{GUC_LIC_TYPE_GUC_DEVICE_ID, GUC_LFD_TYPE_GUC_DEVICE_ID},
|
||||
{GUC_LIC_TYPE_TSC_FREQUENCY, GUC_LFD_TYPE_TSC_FREQUENCY},
|
||||
{GUC_LIC_TYPE_GMD_ID, GUC_LFD_TYPE_GMD_ID},
|
||||
{GUC_LIC_TYPE_BUILD_PLATFORM_ID, GUC_LFD_TYPE_BUILD_PLATFORM_ID}
|
||||
};
|
||||
|
||||
static struct xe_guc *
|
||||
log_to_guc(struct xe_guc_log *log)
|
||||
{
|
||||
@@ -37,33 +110,6 @@ log_to_xe(struct xe_guc_log *log)
|
||||
return gt_to_xe(log_to_gt(log));
|
||||
}
|
||||
|
||||
static size_t guc_log_size(void)
|
||||
{
|
||||
/*
|
||||
* GuC Log buffer Layout
|
||||
*
|
||||
* +===============================+ 00B
|
||||
* | Crash dump state header |
|
||||
* +-------------------------------+ 32B
|
||||
* | Debug state header |
|
||||
* +-------------------------------+ 64B
|
||||
* | Capture state header |
|
||||
* +-------------------------------+ 96B
|
||||
* | |
|
||||
* +===============================+ PAGE_SIZE (4KB)
|
||||
* | Crash Dump logs |
|
||||
* +===============================+ + CRASH_SIZE
|
||||
* | Debug logs |
|
||||
* +===============================+ + DEBUG_SIZE
|
||||
* | Capture logs |
|
||||
* +===============================+ + CAPTURE_SIZE
|
||||
*/
|
||||
return PAGE_SIZE + CRASH_BUFFER_SIZE + DEBUG_BUFFER_SIZE +
|
||||
CAPTURE_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
#define GUC_LOG_CHUNK_SIZE SZ_2M
|
||||
|
||||
static struct xe_guc_log_snapshot *xe_guc_log_snapshot_alloc(struct xe_guc_log *log, bool atomic)
|
||||
{
|
||||
struct xe_guc_log_snapshot *snapshot;
|
||||
@@ -145,7 +191,6 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log,
|
||||
struct xe_device *xe = log_to_xe(log);
|
||||
struct xe_guc *guc = log_to_guc(log);
|
||||
struct xe_gt *gt = log_to_gt(log);
|
||||
unsigned int fw_ref;
|
||||
size_t remain;
|
||||
int i;
|
||||
|
||||
@@ -165,13 +210,12 @@ struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log,
|
||||
remain -= size;
|
||||
}
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref) {
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
snapshot->stamp = ~0ULL;
|
||||
} else {
|
||||
else
|
||||
snapshot->stamp = xe_mmio_read64_2x32(>->mmio, GUC_PMTIMESTAMP_LO);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
snapshot->ktime = ktime_get_boottime_ns();
|
||||
snapshot->level = log->level;
|
||||
snapshot->ver_found = guc->fw.versions.found[XE_UC_FW_VER_RELEASE];
|
||||
@@ -216,6 +260,318 @@ void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_
|
||||
}
|
||||
}
|
||||
|
||||
static inline void lfd_output_binary(struct drm_printer *p, char *buf, int buf_size)
|
||||
{
|
||||
seq_write(p->arg, buf, buf_size);
|
||||
}
|
||||
|
||||
static inline int xe_guc_log_add_lfd_header(struct guc_lfd_data *lfd)
|
||||
{
|
||||
lfd->header = FIELD_PREP_CONST(GUC_LFD_DATA_HEADER_MASK_MAGIC, GUC_LFD_DATA_HEADER_MAGIC);
|
||||
return offsetof(struct guc_lfd_data, data);
|
||||
}
|
||||
|
||||
static int xe_guc_log_add_typed_payload(struct drm_printer *p, u32 type,
|
||||
u32 data_len, void *data)
|
||||
{
|
||||
struct guc_lfd_data lfd;
|
||||
int len;
|
||||
|
||||
len = xe_guc_log_add_lfd_header(&lfd);
|
||||
lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, type);
|
||||
/* make length DW aligned */
|
||||
lfd.data_count = DIV_ROUND_UP(data_len, sizeof(u32));
|
||||
lfd_output_binary(p, (char *)&lfd, len);
|
||||
|
||||
lfd_output_binary(p, data, data_len);
|
||||
len += lfd.data_count * sizeof(u32);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline int lic_type_to_index(u32 lic_type)
|
||||
{
|
||||
XE_WARN_ON(lic_type < GUC_LIC_TYPE_FIRST || lic_type > GUC_LIC_TYPE_LAST);
|
||||
|
||||
return lic_type - GUC_LIC_TYPE_FIRST;
|
||||
}
|
||||
|
||||
static inline int lfd_type_to_index(u32 lfd_type)
|
||||
{
|
||||
int i, lic_type = 0;
|
||||
|
||||
XE_WARN_ON(lfd_type < GUC_LFD_TYPE_FW_RANGE_FIRST || lfd_type > GUC_LFD_TYPE_FW_RANGE_LAST);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lic_lfd_type_map); i++)
|
||||
if (lic_lfd_type_map[i].lfd == lfd_type)
|
||||
lic_type = lic_lfd_type_map[i].lic;
|
||||
|
||||
/* If not found, lic_type_to_index will warning invalid type */
|
||||
return lic_type_to_index(lic_type);
|
||||
}
|
||||
|
||||
static int xe_guc_log_add_klv(struct drm_printer *p, u32 lfd_type,
|
||||
struct guc_lic_save *config)
|
||||
{
|
||||
int klv_index = lfd_type_to_index(lfd_type);
|
||||
|
||||
return xe_guc_log_add_typed_payload(p, lfd_type, sizeof(u32), &config->values[klv_index]);
|
||||
}
|
||||
|
||||
static int xe_guc_log_add_os_id(struct drm_printer *p, u32 id)
|
||||
{
|
||||
struct guc_lfd_data_os_info os_id;
|
||||
struct guc_lfd_data lfd;
|
||||
int len, info_len, section_len;
|
||||
char *version;
|
||||
u32 blank = 0;
|
||||
|
||||
len = xe_guc_log_add_lfd_header(&lfd);
|
||||
lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_OS_ID);
|
||||
|
||||
os_id.os_id = id;
|
||||
section_len = offsetof(struct guc_lfd_data_os_info, build_version);
|
||||
|
||||
version = init_utsname()->release;
|
||||
info_len = strlen(version);
|
||||
|
||||
/* make length DW aligned */
|
||||
lfd.data_count = DIV_ROUND_UP(section_len + info_len, sizeof(u32));
|
||||
lfd_output_binary(p, (char *)&lfd, len);
|
||||
lfd_output_binary(p, (char *)&os_id, section_len);
|
||||
lfd_output_binary(p, version, info_len);
|
||||
|
||||
/* Padding with 0 */
|
||||
section_len = lfd.data_count * sizeof(u32) - section_len - info_len;
|
||||
if (section_len)
|
||||
lfd_output_binary(p, (char *)&blank, section_len);
|
||||
|
||||
len += lfd.data_count * sizeof(u32);
|
||||
return len;
|
||||
}
|
||||
|
||||
static void xe_guc_log_loop_log_init(struct guc_lic *init, struct guc_lic_save *config)
|
||||
{
|
||||
struct guc_klv_generic_dw_t *p = (void *)init->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < init->data_count;) {
|
||||
int klv_len = FIELD_GET(GUC_KLV_0_LEN, p->kl) + 1;
|
||||
int key = FIELD_GET(GUC_KLV_0_KEY, p->kl);
|
||||
|
||||
if (key < GUC_LIC_TYPE_FIRST || key > GUC_LIC_TYPE_LAST) {
|
||||
XE_WARN_ON(key < GUC_LIC_TYPE_FIRST || key > GUC_LIC_TYPE_LAST);
|
||||
break;
|
||||
}
|
||||
config->values[lic_type_to_index(key)] = p->value;
|
||||
i += klv_len + 1; /* Whole KLV structure length in dwords */
|
||||
p = (void *)((u32 *)p + klv_len);
|
||||
}
|
||||
}
|
||||
|
||||
static int find_marker(u32 mark0, u32 mark1)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(entry_markers); i++)
|
||||
if (mark0 == entry_markers[i].key[0] && mark1 == entry_markers[i].key[1])
|
||||
return i;
|
||||
|
||||
return ARRAY_SIZE(entry_markers);
|
||||
}
|
||||
|
||||
static void xe_guc_log_load_lic(void *guc_log, struct guc_lic_save *config)
|
||||
{
|
||||
u32 offset = GUC_LOG_BUFFER_STATE_HEADER_LENGTH;
|
||||
struct guc_log_buffer_state *p = guc_log;
|
||||
|
||||
config->version = p->version;
|
||||
while (p->marker[0]) {
|
||||
int index;
|
||||
|
||||
index = find_marker(p->marker[0], p->marker[1]);
|
||||
|
||||
if (index < ARRAY_SIZE(entry_markers)) {
|
||||
if (index == GUC_LOG_BUFFER_INIT_CONFIG) {
|
||||
/* Load log init config */
|
||||
xe_guc_log_loop_log_init((void *)p, config);
|
||||
|
||||
/* LIC structure is the last */
|
||||
return;
|
||||
}
|
||||
config->entry[index].offset = offset;
|
||||
config->entry[index].rd_ptr = p->read_ptr;
|
||||
config->entry[index].wr_ptr = p->write_ptr;
|
||||
config->entry[index].wrap_offset = p->wrap_offset;
|
||||
config->entry[index].buf_size = p->size;
|
||||
}
|
||||
offset += p->size;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
xe_guc_log_output_lfd_init(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
|
||||
struct guc_lic_save *config)
|
||||
{
|
||||
int type, len;
|
||||
size_t size = 0;
|
||||
|
||||
/* FW required types */
|
||||
for (type = GUC_LFD_TYPE_FW_RANGE_FIRST; type <= GUC_LFD_TYPE_FW_RANGE_LAST; type++)
|
||||
size += xe_guc_log_add_klv(p, type, config);
|
||||
|
||||
/* KMD required type(s) */
|
||||
len = xe_guc_log_add_os_id(p, GUC_LFD_OS_TYPE_OSID_LIN);
|
||||
size += len;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
xe_guc_log_print_chunks(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
|
||||
u32 from, u32 to)
|
||||
{
|
||||
int chunk_from = from % GUC_LOG_CHUNK_SIZE;
|
||||
int chunk_id = from / GUC_LOG_CHUNK_SIZE;
|
||||
int to_chunk_id = to / GUC_LOG_CHUNK_SIZE;
|
||||
int chunk_to = to % GUC_LOG_CHUNK_SIZE;
|
||||
int pos = from;
|
||||
|
||||
do {
|
||||
size_t size = (to_chunk_id == chunk_id ? chunk_to : GUC_LOG_CHUNK_SIZE) -
|
||||
chunk_from;
|
||||
|
||||
lfd_output_binary(p, snapshot->copy[chunk_id] + chunk_from, size);
|
||||
pos += size;
|
||||
chunk_id++;
|
||||
chunk_from = 0;
|
||||
} while (pos < to);
|
||||
}
|
||||
|
||||
static inline int
|
||||
xe_guc_log_add_log_event(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
|
||||
struct guc_lic_save *config)
|
||||
{
|
||||
size_t size;
|
||||
u32 data_len, section_len;
|
||||
struct guc_lfd_data lfd;
|
||||
struct guc_log_buffer_entry_list *entry;
|
||||
struct guc_lfd_data_log_events_buf events_buf;
|
||||
|
||||
entry = &config->entry[GUC_LOG_TYPE_EVENT_DATA];
|
||||
|
||||
/* Skip empty log */
|
||||
if (entry->rd_ptr == entry->wr_ptr)
|
||||
return 0;
|
||||
|
||||
size = xe_guc_log_add_lfd_header(&lfd);
|
||||
lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_LOG_EVENTS_BUFFER);
|
||||
events_buf.log_events_format_version = config->version;
|
||||
|
||||
/* Adjust to log_format_buf */
|
||||
section_len = offsetof(struct guc_lfd_data_log_events_buf, log_event);
|
||||
data_len = section_len;
|
||||
|
||||
/* Calculate data length */
|
||||
data_len += entry->rd_ptr < entry->wr_ptr ? (entry->wr_ptr - entry->rd_ptr) :
|
||||
(entry->wr_ptr + entry->wrap_offset - entry->rd_ptr);
|
||||
/* make length u32 aligned */
|
||||
lfd.data_count = DIV_ROUND_UP(data_len, sizeof(u32));
|
||||
|
||||
/* Output GUC_LFD_TYPE_LOG_EVENTS_BUFFER header */
|
||||
lfd_output_binary(p, (char *)&lfd, size);
|
||||
lfd_output_binary(p, (char *)&events_buf, section_len);
|
||||
|
||||
/* Output data from guc log chunks directly */
|
||||
if (entry->rd_ptr < entry->wr_ptr) {
|
||||
xe_guc_log_print_chunks(p, snapshot, entry->offset + entry->rd_ptr,
|
||||
entry->offset + entry->wr_ptr);
|
||||
} else {
|
||||
/* 1st, print from rd to wrap offset */
|
||||
xe_guc_log_print_chunks(p, snapshot, entry->offset + entry->rd_ptr,
|
||||
entry->offset + entry->wrap_offset);
|
||||
|
||||
/* 2nd, print from buf start to wr */
|
||||
xe_guc_log_print_chunks(p, snapshot, entry->offset, entry->offset + entry->wr_ptr);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
xe_guc_log_add_crash_dump(struct drm_printer *p, struct xe_guc_log_snapshot *snapshot,
|
||||
struct guc_lic_save *config)
|
||||
{
|
||||
struct guc_log_buffer_entry_list *entry;
|
||||
int chunk_from, chunk_id;
|
||||
int from, to, i;
|
||||
size_t size = 0;
|
||||
u32 *buf32;
|
||||
|
||||
entry = &config->entry[GUC_LOG_TYPE_CRASH_DUMP];
|
||||
|
||||
/* Skip zero sized crash dump */
|
||||
if (!entry->buf_size)
|
||||
return 0;
|
||||
|
||||
/* Check if crash dump section are all zero */
|
||||
from = entry->offset;
|
||||
to = entry->offset + entry->buf_size;
|
||||
chunk_from = from % GUC_LOG_CHUNK_SIZE;
|
||||
chunk_id = from / GUC_LOG_CHUNK_SIZE;
|
||||
buf32 = snapshot->copy[chunk_id] + chunk_from;
|
||||
|
||||
for (i = 0; i < entry->buf_size / sizeof(u32); i++)
|
||||
if (buf32[i])
|
||||
break;
|
||||
|
||||
/* Buffer has non-zero data? */
|
||||
if (i < entry->buf_size / sizeof(u32)) {
|
||||
struct guc_lfd_data lfd;
|
||||
|
||||
size = xe_guc_log_add_lfd_header(&lfd);
|
||||
lfd.header |= FIELD_PREP(GUC_LFD_DATA_HEADER_MASK_TYPE, GUC_LFD_TYPE_FW_CRASH_DUMP);
|
||||
/* Calculate data length */
|
||||
lfd.data_count = DIV_ROUND_UP(entry->buf_size, sizeof(u32));
|
||||
/* Output GUC_LFD_TYPE_FW_CRASH_DUMP header */
|
||||
lfd_output_binary(p, (char *)&lfd, size);
|
||||
|
||||
/* rd/wr ptr is not used for crash dump */
|
||||
xe_guc_log_print_chunks(p, snapshot, from, to);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
xe_guc_log_snapshot_print_lfd(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p)
|
||||
{
|
||||
struct guc_lfd_file_header header;
|
||||
struct guc_lic_save config;
|
||||
size_t size;
|
||||
|
||||
if (!snapshot || !snapshot->size)
|
||||
return;
|
||||
|
||||
header.magic = GUC_LFD_DRIVER_KEY_STREAMING;
|
||||
header.version = FIELD_PREP_CONST(GUC_LFD_FILE_HEADER_VERSION_MASK_MINOR,
|
||||
GUC_LFD_FORMAT_VERSION_MINOR) |
|
||||
FIELD_PREP_CONST(GUC_LFD_FILE_HEADER_VERSION_MASK_MAJOR,
|
||||
GUC_LFD_FORMAT_VERSION_MAJOR);
|
||||
|
||||
/* Output LFD file header */
|
||||
lfd_output_binary(p, (char *)&header,
|
||||
offsetof(struct guc_lfd_file_header, stream));
|
||||
|
||||
/* Output LFD stream */
|
||||
xe_guc_log_load_lic(snapshot->copy[0], &config);
|
||||
size = xe_guc_log_output_lfd_init(p, snapshot, &config);
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
xe_guc_log_add_log_event(p, snapshot, &config);
|
||||
xe_guc_log_add_crash_dump(p, snapshot, &config);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_log_print_dmesg - dump a copy of the GuC log to dmesg
|
||||
* @log: GuC log structure
|
||||
@@ -251,13 +607,27 @@ void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p)
|
||||
xe_guc_log_snapshot_free(snapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_log_print_lfd - dump a copy of the GuC log in LFD format
|
||||
* @log: GuC log structure
|
||||
* @p: the printer object to output to
|
||||
*/
|
||||
void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p)
|
||||
{
|
||||
struct xe_guc_log_snapshot *snapshot;
|
||||
|
||||
snapshot = xe_guc_log_snapshot_capture(log, false);
|
||||
xe_guc_log_snapshot_print_lfd(snapshot, p);
|
||||
xe_guc_log_snapshot_free(snapshot);
|
||||
}
|
||||
|
||||
int xe_guc_log_init(struct xe_guc_log *log)
|
||||
{
|
||||
struct xe_device *xe = log_to_xe(log);
|
||||
struct xe_tile *tile = gt_to_tile(log_to_gt(log));
|
||||
struct xe_bo *bo;
|
||||
|
||||
bo = xe_managed_bo_create_pin_map(xe, tile, guc_log_size(),
|
||||
bo = xe_managed_bo_create_pin_map(xe, tile, GUC_LOG_SIZE,
|
||||
XE_BO_FLAG_SYSTEM |
|
||||
XE_BO_FLAG_GGTT |
|
||||
XE_BO_FLAG_GGTT_INVALIDATE |
|
||||
@@ -265,7 +635,7 @@ int xe_guc_log_init(struct xe_guc_log *log)
|
||||
if (IS_ERR(bo))
|
||||
return PTR_ERR(bo);
|
||||
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0, guc_log_size());
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0, xe_bo_size(bo));
|
||||
log->bo = bo;
|
||||
log->level = xe_modparam.guc_log_level;
|
||||
|
||||
@@ -274,71 +644,6 @@ int xe_guc_log_init(struct xe_guc_log *log)
|
||||
|
||||
ALLOW_ERROR_INJECTION(xe_guc_log_init, ERRNO); /* See xe_pci_probe() */
|
||||
|
||||
static u32 xe_guc_log_section_size_crash(struct xe_guc_log *log)
|
||||
{
|
||||
return CRASH_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
static u32 xe_guc_log_section_size_debug(struct xe_guc_log *log)
|
||||
{
|
||||
return DEBUG_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_log_section_size_capture - Get capture buffer size within log sections.
|
||||
* @log: The log object.
|
||||
*
|
||||
* This function will return the capture buffer size within log sections.
|
||||
*
|
||||
* Return: capture buffer size.
|
||||
*/
|
||||
u32 xe_guc_log_section_size_capture(struct xe_guc_log *log)
|
||||
{
|
||||
return CAPTURE_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_get_log_buffer_size - Get log buffer size for a type.
|
||||
* @log: The log object.
|
||||
* @type: The log buffer type
|
||||
*
|
||||
* Return: buffer size.
|
||||
*/
|
||||
u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case GUC_LOG_BUFFER_CRASH_DUMP:
|
||||
return xe_guc_log_section_size_crash(log);
|
||||
case GUC_LOG_BUFFER_DEBUG:
|
||||
return xe_guc_log_section_size_debug(log);
|
||||
case GUC_LOG_BUFFER_CAPTURE:
|
||||
return xe_guc_log_section_size_capture(log);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_get_log_buffer_offset - Get offset in log buffer for a type.
|
||||
* @log: The log object.
|
||||
* @type: The log buffer type
|
||||
*
|
||||
* This function will return the offset in the log buffer for a type.
|
||||
* Return: buffer offset.
|
||||
*/
|
||||
u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type)
|
||||
{
|
||||
enum guc_log_buffer_type i;
|
||||
u32 offset = PAGE_SIZE;/* for the log_buffer_states */
|
||||
|
||||
for (i = GUC_LOG_BUFFER_CRASH_DUMP; i < GUC_LOG_BUFFER_TYPE_MAX; ++i) {
|
||||
if (i == type)
|
||||
break;
|
||||
offset += xe_guc_get_log_buffer_size(log, i);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_check_log_buf_overflow - Check if log buffer overflowed
|
||||
* @log: The log object.
|
||||
@@ -352,7 +657,7 @@ u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_typ
|
||||
*
|
||||
* Return: True if overflowed.
|
||||
*/
|
||||
bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_buffer_type type,
|
||||
bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log, enum guc_log_type type,
|
||||
unsigned int full_cnt)
|
||||
{
|
||||
unsigned int prev_full_cnt = log->stats[type].sampled_overflow;
|
||||
|
||||
@@ -13,14 +13,26 @@ struct drm_printer;
|
||||
struct xe_device;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
|
||||
#define CRASH_BUFFER_SIZE SZ_1M
|
||||
#define DEBUG_BUFFER_SIZE SZ_8M
|
||||
#define CAPTURE_BUFFER_SIZE SZ_2M
|
||||
#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_8M
|
||||
#define XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE SZ_1M
|
||||
#define XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE SZ_2M
|
||||
#else
|
||||
#define CRASH_BUFFER_SIZE SZ_16K
|
||||
#define DEBUG_BUFFER_SIZE SZ_64K
|
||||
#define CAPTURE_BUFFER_SIZE SZ_1M
|
||||
#define XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE SZ_64K
|
||||
#define XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE SZ_16K
|
||||
#define XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE SZ_1M
|
||||
#endif
|
||||
|
||||
#define GUC_LOG_SIZE (SZ_4K + \
|
||||
XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE + \
|
||||
XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE + \
|
||||
XE_GUC_LOG_STATE_CAPTURE_BUFFER_SIZE)
|
||||
|
||||
#define XE_GUC_LOG_EVENT_DATA_OFFSET SZ_4K
|
||||
#define XE_GUC_LOG_CRASH_DUMP_OFFSET (XE_GUC_LOG_EVENT_DATA_OFFSET + \
|
||||
XE_GUC_LOG_EVENT_DATA_BUFFER_SIZE)
|
||||
#define XE_GUC_LOG_STATE_CAPTURE_OFFSET (XE_GUC_LOG_CRASH_DUMP_OFFSET + \
|
||||
XE_GUC_LOG_CRASH_DUMP_BUFFER_SIZE)
|
||||
|
||||
/*
|
||||
* While we're using plain log level in i915, GuC controls are much more...
|
||||
* "elaborate"? We have a couple of bits for verbosity, separate bit for actual
|
||||
@@ -40,6 +52,7 @@ struct xe_device;
|
||||
|
||||
int xe_guc_log_init(struct xe_guc_log *log);
|
||||
void xe_guc_log_print(struct xe_guc_log *log, struct drm_printer *p);
|
||||
void xe_guc_log_print_lfd(struct xe_guc_log *log, struct drm_printer *p);
|
||||
void xe_guc_log_print_dmesg(struct xe_guc_log *log);
|
||||
struct xe_guc_log_snapshot *xe_guc_log_snapshot_capture(struct xe_guc_log *log, bool atomic);
|
||||
void xe_guc_log_snapshot_print(struct xe_guc_log_snapshot *snapshot, struct drm_printer *p);
|
||||
@@ -51,11 +64,8 @@ xe_guc_log_get_level(struct xe_guc_log *log)
|
||||
return log->level;
|
||||
}
|
||||
|
||||
u32 xe_guc_log_section_size_capture(struct xe_guc_log *log);
|
||||
u32 xe_guc_get_log_buffer_size(struct xe_guc_log *log, enum guc_log_buffer_type type);
|
||||
u32 xe_guc_get_log_buffer_offset(struct xe_guc_log *log, enum guc_log_buffer_type type);
|
||||
bool xe_guc_check_log_buf_overflow(struct xe_guc_log *log,
|
||||
enum guc_log_buffer_type type,
|
||||
enum guc_log_type type,
|
||||
unsigned int full_cnt);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
* exposes a programming interface to the host for the control of SLPC.
|
||||
*
|
||||
* Frequency management:
|
||||
* =====================
|
||||
* ---------------------
|
||||
*
|
||||
* Xe driver enables SLPC with all of its defaults features and frequency
|
||||
* selection, which varies per platform.
|
||||
@@ -87,7 +87,7 @@
|
||||
* for any workload.
|
||||
*
|
||||
* Render-C States:
|
||||
* ================
|
||||
* ----------------
|
||||
*
|
||||
* Render-C states is also a GuC PC feature that is now enabled in Xe for
|
||||
* all platforms.
|
||||
@@ -499,21 +499,17 @@ u32 xe_guc_pc_get_cur_freq_fw(struct xe_guc_pc *pc)
|
||||
int xe_guc_pc_get_cur_freq(struct xe_guc_pc *pc, u32 *freq)
|
||||
{
|
||||
struct xe_gt *gt = pc_to_gt(pc);
|
||||
unsigned int fw_ref;
|
||||
|
||||
/*
|
||||
* GuC SLPC plays with cur freq request when GuCRC is enabled
|
||||
* Block RC6 for a more reliable read.
|
||||
*/
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) {
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*freq = get_cur_freq(gt);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1087,13 +1083,8 @@ int xe_guc_pc_gucrc_disable(struct xe_guc_pc *pc)
|
||||
*/
|
||||
int xe_guc_pc_override_gucrc_mode(struct xe_guc_pc *pc, enum slpc_gucrc_mode mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(pc_to_xe(pc));
|
||||
ret = pc_action_set_param(pc, SLPC_PARAM_PWRGATE_RC_MODE, mode);
|
||||
xe_pm_runtime_put(pc_to_xe(pc));
|
||||
|
||||
return ret;
|
||||
guard(xe_pm_runtime)(pc_to_xe(pc));
|
||||
return pc_action_set_param(pc, SLPC_PARAM_PWRGATE_RC_MODE, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1104,13 +1095,8 @@ int xe_guc_pc_override_gucrc_mode(struct xe_guc_pc *pc, enum slpc_gucrc_mode mod
|
||||
*/
|
||||
int xe_guc_pc_unset_gucrc_mode(struct xe_guc_pc *pc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(pc_to_xe(pc));
|
||||
ret = pc_action_unset_param(pc, SLPC_PARAM_PWRGATE_RC_MODE);
|
||||
xe_pm_runtime_put(pc_to_xe(pc));
|
||||
|
||||
return ret;
|
||||
guard(xe_pm_runtime)(pc_to_xe(pc));
|
||||
return pc_action_unset_param(pc, SLPC_PARAM_PWRGATE_RC_MODE);
|
||||
}
|
||||
|
||||
static void pc_init_pcode_freq(struct xe_guc_pc *pc)
|
||||
@@ -1198,7 +1184,7 @@ int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
xe_pm_runtime_get_noresume(pc_to_xe(pc));
|
||||
guard(xe_pm_runtime_noresume)(pc_to_xe(pc));
|
||||
|
||||
ret = pc_action_set_param(pc,
|
||||
SLPC_PARAM_POWER_PROFILE,
|
||||
@@ -1209,8 +1195,6 @@ int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf)
|
||||
else
|
||||
pc->power_profile = val;
|
||||
|
||||
xe_pm_runtime_put(pc_to_xe(pc));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1223,17 +1207,14 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
|
||||
struct xe_device *xe = pc_to_xe(pc);
|
||||
struct xe_gt *gt = pc_to_gt(pc);
|
||||
u32 size = PAGE_ALIGN(sizeof(struct slpc_shared_data));
|
||||
unsigned int fw_ref;
|
||||
ktime_t earlier;
|
||||
int ret;
|
||||
|
||||
xe_gt_assert(gt, xe_device_uc_enabled(xe));
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_GT)) {
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FW_GT))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (xe->info.skip_guc_pc) {
|
||||
if (xe->info.platform != XE_PVC)
|
||||
@@ -1241,9 +1222,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
|
||||
|
||||
/* Request max possible since dynamic freq mgmt is not enabled */
|
||||
pc_set_cur_freq(pc, UINT_MAX);
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xe_map_memset(xe, &pc->bo->vmap, 0, 0, size);
|
||||
@@ -1252,7 +1231,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
|
||||
earlier = ktime_get();
|
||||
ret = pc_action_reset(pc);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING,
|
||||
SLPC_RESET_TIMEOUT_MS)) {
|
||||
@@ -1263,8 +1242,7 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
|
||||
if (wait_for_pc_state(pc, SLPC_GLOBAL_STATE_RUNNING,
|
||||
SLPC_RESET_EXTENDED_TIMEOUT_MS)) {
|
||||
xe_gt_err(gt, "GuC PC Start failed: Dynamic GT frequency control and GT sleep states are now disabled.\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
xe_gt_warn(gt, "GuC PC excessive start time: %lldms",
|
||||
@@ -1273,21 +1251,20 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
|
||||
|
||||
ret = pc_init_freqs(pc);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = pc_set_mert_freq_cap(pc);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (xe->info.platform == XE_PVC) {
|
||||
xe_guc_pc_gucrc_disable(pc);
|
||||
ret = 0;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = pc_action_setup_gucrc(pc, GUCRC_FIRMWARE_CONTROL);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
/* Enable SLPC Optimized Strategy for compute */
|
||||
ret = pc_action_set_strategy(pc, SLPC_OPTIMIZED_STRATEGY_COMPUTE);
|
||||
@@ -1297,8 +1274,6 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
|
||||
if (unlikely(ret))
|
||||
xe_gt_err(gt, "Failed to set SLPC power profile: %pe\n", ERR_PTR(ret));
|
||||
|
||||
out:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1330,19 +1305,16 @@ static void xe_guc_pc_fini_hw(void *arg)
|
||||
{
|
||||
struct xe_guc_pc *pc = arg;
|
||||
struct xe_device *xe = pc_to_xe(pc);
|
||||
unsigned int fw_ref;
|
||||
|
||||
if (xe_device_wedged(xe))
|
||||
return;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL);
|
||||
xe_guc_pc_gucrc_disable(pc);
|
||||
XE_WARN_ON(xe_guc_pc_stop(pc));
|
||||
|
||||
/* Bind requested freq to mert_freq_cap before unload */
|
||||
pc_set_cur_freq(pc, min(pc_max_freq_cap(pc), xe_guc_pc_get_rpe_freq(pc)));
|
||||
|
||||
xe_force_wake_put(gt_to_fw(pc_to_gt(pc)), fw_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,11 @@ void xe_guc_submit_reset_wait(struct xe_guc *guc);
|
||||
void xe_guc_submit_stop(struct xe_guc *guc);
|
||||
int xe_guc_submit_start(struct xe_guc *guc);
|
||||
void xe_guc_submit_pause(struct xe_guc *guc);
|
||||
void xe_guc_submit_unpause(struct xe_guc *guc);
|
||||
void xe_guc_submit_unpause_prepare(struct xe_guc *guc);
|
||||
void xe_guc_submit_pause_abort(struct xe_guc *guc);
|
||||
void xe_guc_submit_pause_vf(struct xe_guc *guc);
|
||||
void xe_guc_submit_unpause(struct xe_guc *guc);
|
||||
void xe_guc_submit_unpause_vf(struct xe_guc *guc);
|
||||
void xe_guc_submit_unpause_prepare_vf(struct xe_guc *guc);
|
||||
void xe_guc_submit_wedge(struct xe_guc *guc);
|
||||
|
||||
int xe_guc_read_stopped(struct xe_guc *guc);
|
||||
@@ -34,6 +36,9 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
|
||||
u32 len);
|
||||
int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len);
|
||||
int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len);
|
||||
int xe_guc_exec_queue_cgp_sync_done_handler(struct xe_guc *guc, u32 *msg, u32 len);
|
||||
int xe_guc_exec_queue_cgp_context_error_handler(struct xe_guc *guc, u32 *msg,
|
||||
u32 len);
|
||||
|
||||
struct xe_guc_submit_exec_queue_snapshot *
|
||||
xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q);
|
||||
|
||||
@@ -135,6 +135,19 @@ struct xe_guc_submit_exec_queue_snapshot {
|
||||
u32 wq[WQ_SIZE / sizeof(u32)];
|
||||
} parallel;
|
||||
|
||||
/** @multi_queue: snapshot of the multi queue information */
|
||||
struct {
|
||||
/**
|
||||
* @multi_queue.primary: GuC id of the primary exec queue
|
||||
* of the multi queue group.
|
||||
*/
|
||||
u32 primary;
|
||||
/** @multi_queue.pos: Position of the exec queue within the multi queue group */
|
||||
u8 pos;
|
||||
/** @valid: The exec queue is part of a multi queue group */
|
||||
bool valid;
|
||||
} multi_queue;
|
||||
|
||||
/** @pending_list_size: Size of the pending list snapshot array */
|
||||
int pending_list_size;
|
||||
/** @pending_list: snapshot of the pending list info */
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "xe_guc_tlb_inval.h"
|
||||
#include "xe_force_wake.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_sa.h"
|
||||
#include "xe_tlb_inval.h"
|
||||
|
||||
#include "regs/xe_guc_regs.h"
|
||||
@@ -34,9 +35,12 @@ static int send_tlb_inval(struct xe_guc *guc, const u32 *action, int len)
|
||||
G2H_LEN_DW_TLB_INVALIDATE, 1);
|
||||
}
|
||||
|
||||
#define MAKE_INVAL_OP(type) ((type << XE_GUC_TLB_INVAL_TYPE_SHIFT) | \
|
||||
#define MAKE_INVAL_OP_FLUSH(type, flush_cache) ((type << XE_GUC_TLB_INVAL_TYPE_SHIFT) | \
|
||||
XE_GUC_TLB_INVAL_MODE_HEAVY << XE_GUC_TLB_INVAL_MODE_SHIFT | \
|
||||
XE_GUC_TLB_INVAL_FLUSH_CACHE)
|
||||
(flush_cache ? \
|
||||
XE_GUC_TLB_INVAL_FLUSH_CACHE : 0))
|
||||
|
||||
#define MAKE_INVAL_OP(type) MAKE_INVAL_OP_FLUSH(type, true)
|
||||
|
||||
static int send_tlb_inval_all(struct xe_tlb_inval *tlb_inval, u32 seqno)
|
||||
{
|
||||
@@ -71,12 +75,11 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno)
|
||||
return send_tlb_inval(guc, action, ARRAY_SIZE(action));
|
||||
} else if (xe_device_uc_enabled(xe) && !xe_device_wedged(xe)) {
|
||||
struct xe_mmio *mmio = >->mmio;
|
||||
unsigned int fw_ref;
|
||||
|
||||
if (IS_SRIOV_VF(xe))
|
||||
return -ECANCELED;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (xe->info.platform == XE_PVC || GRAPHICS_VER(xe) >= 20) {
|
||||
xe_mmio_write32(mmio, PVC_GUC_TLB_INV_DESC1,
|
||||
PVC_GUC_TLB_INV_DESC1_INVALIDATE);
|
||||
@@ -86,12 +89,25 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno)
|
||||
xe_mmio_write32(mmio, GUC_TLB_INV_CR,
|
||||
GUC_TLB_INV_CR_INVALIDATE);
|
||||
}
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
static int send_page_reclaim(struct xe_guc *guc, u32 seqno,
|
||||
u64 gpu_addr)
|
||||
{
|
||||
u32 action[] = {
|
||||
XE_GUC_ACTION_PAGE_RECLAMATION,
|
||||
seqno,
|
||||
lower_32_bits(gpu_addr),
|
||||
upper_32_bits(gpu_addr),
|
||||
};
|
||||
|
||||
return xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
|
||||
G2H_LEN_DW_PAGE_RECLAMATION, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that roundup_pow_of_two(length) doesn't overflow.
|
||||
* Note that roundup_pow_of_two() operates on unsigned long,
|
||||
@@ -100,20 +116,21 @@ static int send_tlb_inval_ggtt(struct xe_tlb_inval *tlb_inval, u32 seqno)
|
||||
#define MAX_RANGE_TLB_INVALIDATION_LENGTH (rounddown_pow_of_two(ULONG_MAX))
|
||||
|
||||
static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
|
||||
u64 start, u64 end, u32 asid)
|
||||
u64 start, u64 end, u32 asid,
|
||||
struct drm_suballoc *prl_sa)
|
||||
{
|
||||
#define MAX_TLB_INVALIDATION_LEN 7
|
||||
struct xe_guc *guc = tlb_inval->private;
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
u32 action[MAX_TLB_INVALIDATION_LEN];
|
||||
u64 length = end - start;
|
||||
int len = 0;
|
||||
int len = 0, err;
|
||||
|
||||
if (guc_to_xe(guc)->info.force_execlist)
|
||||
return -ECANCELED;
|
||||
|
||||
action[len++] = XE_GUC_ACTION_TLB_INVALIDATION;
|
||||
action[len++] = seqno;
|
||||
action[len++] = !prl_sa ? seqno : TLB_INVALIDATION_SEQNO_INVALID;
|
||||
if (!gt_to_xe(gt)->info.has_range_tlb_inval ||
|
||||
length > MAX_RANGE_TLB_INVALIDATION_LENGTH) {
|
||||
action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_FULL);
|
||||
@@ -154,7 +171,8 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
|
||||
ilog2(SZ_2M) + 1)));
|
||||
xe_gt_assert(gt, IS_ALIGNED(start, length));
|
||||
|
||||
action[len++] = MAKE_INVAL_OP(XE_GUC_TLB_INVAL_PAGE_SELECTIVE);
|
||||
/* Flush on NULL case, Media is not required to modify flush due to no PPC so NOP */
|
||||
action[len++] = MAKE_INVAL_OP_FLUSH(XE_GUC_TLB_INVAL_PAGE_SELECTIVE, !prl_sa);
|
||||
action[len++] = asid;
|
||||
action[len++] = lower_32_bits(start);
|
||||
action[len++] = upper_32_bits(start);
|
||||
@@ -163,7 +181,10 @@ static int send_tlb_inval_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
|
||||
|
||||
xe_gt_assert(gt, len <= MAX_TLB_INVALIDATION_LEN);
|
||||
|
||||
return send_tlb_inval(guc, action, len);
|
||||
err = send_tlb_inval(guc, action, len);
|
||||
if (!err && prl_sa)
|
||||
err = send_page_reclaim(guc, seqno, xe_sa_bo_gpu_addr(prl_sa));
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool tlb_inval_initialized(struct xe_tlb_inval *tlb_inval)
|
||||
|
||||
@@ -300,19 +300,16 @@ void xe_huc_sanitize(struct xe_huc *huc)
|
||||
void xe_huc_print_info(struct xe_huc *huc, struct drm_printer *p)
|
||||
{
|
||||
struct xe_gt *gt = huc_to_gt(huc);
|
||||
unsigned int fw_ref;
|
||||
|
||||
xe_uc_fw_print(&huc->fw, p);
|
||||
|
||||
if (!xe_uc_fw_is_enabled(&huc->fw))
|
||||
return;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref.domains)
|
||||
return;
|
||||
|
||||
drm_printf(p, "\nHuC status: 0x%08x\n",
|
||||
xe_mmio_read32(>->mmio, HUC_KERNEL_LOAD_INFO));
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
@@ -37,9 +37,8 @@ static int huc_info(struct seq_file *m, void *data)
|
||||
struct xe_device *xe = huc_to_xe(huc);
|
||||
struct drm_printer p = drm_seq_file_printer(m);
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
xe_huc_print_info(huc, &p);
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -43,16 +43,14 @@ static ssize_t xe_hw_engine_class_sysfs_attr_show(struct kobject *kobj,
|
||||
{
|
||||
struct xe_device *xe = kobj_to_xe(kobj);
|
||||
struct kobj_attribute *kattr;
|
||||
ssize_t ret = -EIO;
|
||||
|
||||
kattr = container_of(attr, struct kobj_attribute, attr);
|
||||
if (kattr->show) {
|
||||
xe_pm_runtime_get(xe);
|
||||
ret = kattr->show(kobj, kattr, buf);
|
||||
xe_pm_runtime_put(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return kattr->show(kobj, kattr, buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj,
|
||||
@@ -62,16 +60,14 @@ static ssize_t xe_hw_engine_class_sysfs_attr_store(struct kobject *kobj,
|
||||
{
|
||||
struct xe_device *xe = kobj_to_xe(kobj);
|
||||
struct kobj_attribute *kattr;
|
||||
ssize_t ret = -EIO;
|
||||
|
||||
kattr = container_of(attr, struct kobj_attribute, attr);
|
||||
if (kattr->store) {
|
||||
xe_pm_runtime_get(xe);
|
||||
ret = kattr->store(kobj, kattr, buf, count);
|
||||
xe_pm_runtime_put(xe);
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return kattr->store(kobj, kattr, buf, count);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static const struct sysfs_ops xe_hw_engine_class_sysfs_ops = {
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "xe_device.h"
|
||||
#include "xe_exec_queue.h"
|
||||
#include "xe_gt.h"
|
||||
#include "xe_gt_stats.h"
|
||||
#include "xe_hw_engine_group.h"
|
||||
#include "xe_sync.h"
|
||||
#include "xe_vm.h"
|
||||
|
||||
static void
|
||||
@@ -20,7 +22,8 @@ hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
|
||||
int err;
|
||||
enum xe_hw_engine_group_execution_mode previous_mode;
|
||||
|
||||
err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode);
|
||||
err = xe_hw_engine_group_get_mode(group, EXEC_MODE_LR, &previous_mode,
|
||||
NULL, 0);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
@@ -188,23 +191,39 @@ void xe_hw_engine_group_resume_faulting_lr_jobs(struct xe_hw_engine_group *group
|
||||
/**
|
||||
* xe_hw_engine_group_suspend_faulting_lr_jobs() - Suspend the faulting LR jobs of this group
|
||||
* @group: The hw engine group
|
||||
* @has_deps: dma-fence job triggering suspend has dependencies
|
||||
*
|
||||
* Return: 0 on success, negative error code on error.
|
||||
*/
|
||||
static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group)
|
||||
static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group *group,
|
||||
bool has_deps)
|
||||
{
|
||||
int err;
|
||||
struct xe_exec_queue *q;
|
||||
struct xe_gt *gt = NULL;
|
||||
bool need_resume = false;
|
||||
ktime_t start = xe_gt_stats_ktime_get();
|
||||
|
||||
lockdep_assert_held_write(&group->mode_sem);
|
||||
|
||||
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
|
||||
bool idle_skip_suspend;
|
||||
|
||||
if (!xe_vm_in_fault_mode(q->vm))
|
||||
continue;
|
||||
|
||||
need_resume = true;
|
||||
idle_skip_suspend = xe_exec_queue_idle_skip_suspend(q);
|
||||
if (!idle_skip_suspend && has_deps)
|
||||
return -EAGAIN;
|
||||
|
||||
xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_COUNT, 1);
|
||||
if (idle_skip_suspend)
|
||||
xe_gt_stats_incr(q->gt,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_SKIP_LR_QUEUE_COUNT, 1);
|
||||
|
||||
need_resume |= !idle_skip_suspend;
|
||||
q->ops->suspend(q);
|
||||
gt = q->gt;
|
||||
}
|
||||
|
||||
list_for_each_entry(q, &group->exec_queue_list, hw_engine_group_link) {
|
||||
@@ -216,6 +235,12 @@ static int xe_hw_engine_group_suspend_faulting_lr_jobs(struct xe_hw_engine_group
|
||||
return err;
|
||||
}
|
||||
|
||||
if (gt) {
|
||||
xe_gt_stats_incr(gt,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_SUSPEND_LR_QUEUE_US,
|
||||
xe_gt_stats_ktime_us_delta(start));
|
||||
}
|
||||
|
||||
if (need_resume)
|
||||
xe_hw_engine_group_resume_faulting_lr_jobs(group);
|
||||
|
||||
@@ -236,7 +261,9 @@ static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group
|
||||
{
|
||||
long timeout;
|
||||
struct xe_exec_queue *q;
|
||||
struct xe_gt *gt = NULL;
|
||||
struct dma_fence *fence;
|
||||
ktime_t start = xe_gt_stats_ktime_get();
|
||||
|
||||
lockdep_assert_held_write(&group->mode_sem);
|
||||
|
||||
@@ -244,18 +271,26 @@ static int xe_hw_engine_group_wait_for_dma_fence_jobs(struct xe_hw_engine_group
|
||||
if (xe_vm_in_lr_mode(q->vm))
|
||||
continue;
|
||||
|
||||
xe_gt_stats_incr(q->gt, XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_COUNT, 1);
|
||||
fence = xe_exec_queue_last_fence_get_for_resume(q, q->vm);
|
||||
timeout = dma_fence_wait(fence, false);
|
||||
dma_fence_put(fence);
|
||||
gt = q->gt;
|
||||
|
||||
if (timeout < 0)
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
if (gt) {
|
||||
xe_gt_stats_incr(gt,
|
||||
XE_GT_STATS_ID_HW_ENGINE_GROUP_WAIT_DMA_QUEUE_US,
|
||||
xe_gt_stats_ktime_us_delta(start));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int switch_mode(struct xe_hw_engine_group *group)
|
||||
static int switch_mode(struct xe_hw_engine_group *group, bool has_deps)
|
||||
{
|
||||
int err = 0;
|
||||
enum xe_hw_engine_group_execution_mode new_mode;
|
||||
@@ -265,7 +300,8 @@ static int switch_mode(struct xe_hw_engine_group *group)
|
||||
switch (group->cur_mode) {
|
||||
case EXEC_MODE_LR:
|
||||
new_mode = EXEC_MODE_DMA_FENCE;
|
||||
err = xe_hw_engine_group_suspend_faulting_lr_jobs(group);
|
||||
err = xe_hw_engine_group_suspend_faulting_lr_jobs(group,
|
||||
has_deps);
|
||||
break;
|
||||
case EXEC_MODE_DMA_FENCE:
|
||||
new_mode = EXEC_MODE_LR;
|
||||
@@ -281,19 +317,36 @@ static int switch_mode(struct xe_hw_engine_group *group)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_syncs(struct xe_sync_entry *syncs, int num_syncs)
|
||||
{
|
||||
int err, i;
|
||||
|
||||
for (i = 0; i < num_syncs; ++i) {
|
||||
err = xe_sync_entry_wait(syncs + i);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_hw_engine_group_get_mode() - Get the group to execute in the new mode
|
||||
* @group: The hw engine group
|
||||
* @new_mode: The new execution mode
|
||||
* @previous_mode: Pointer to the previous mode provided for use by caller
|
||||
* @syncs: Syncs from exec IOCTL
|
||||
* @num_syncs: Number of syncs from exec IOCTL
|
||||
*
|
||||
* Return: 0 if successful, -EINTR if locking failed.
|
||||
*/
|
||||
int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
|
||||
enum xe_hw_engine_group_execution_mode new_mode,
|
||||
enum xe_hw_engine_group_execution_mode *previous_mode)
|
||||
enum xe_hw_engine_group_execution_mode *previous_mode,
|
||||
struct xe_sync_entry *syncs, int num_syncs)
|
||||
__acquires(&group->mode_sem)
|
||||
{
|
||||
bool has_deps = !!num_syncs;
|
||||
int err = down_read_interruptible(&group->mode_sem);
|
||||
|
||||
if (err)
|
||||
@@ -303,15 +356,25 @@ __acquires(&group->mode_sem)
|
||||
|
||||
if (new_mode != group->cur_mode) {
|
||||
up_read(&group->mode_sem);
|
||||
retry:
|
||||
err = down_write_killable(&group->mode_sem);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (new_mode != group->cur_mode) {
|
||||
err = switch_mode(group);
|
||||
err = switch_mode(group, has_deps);
|
||||
if (err) {
|
||||
up_write(&group->mode_sem);
|
||||
return err;
|
||||
|
||||
if (err != -EAGAIN)
|
||||
return err;
|
||||
|
||||
err = wait_syncs(syncs, num_syncs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
has_deps = false;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
downgrade_write(&group->mode_sem);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
struct drm_device;
|
||||
struct xe_exec_queue;
|
||||
struct xe_gt;
|
||||
struct xe_sync_entry;
|
||||
|
||||
int xe_hw_engine_setup_groups(struct xe_gt *gt);
|
||||
|
||||
@@ -19,7 +20,8 @@ void xe_hw_engine_group_del_exec_queue(struct xe_hw_engine_group *group, struct
|
||||
|
||||
int xe_hw_engine_group_get_mode(struct xe_hw_engine_group *group,
|
||||
enum xe_hw_engine_group_execution_mode new_mode,
|
||||
enum xe_hw_engine_group_execution_mode *previous_mode);
|
||||
enum xe_hw_engine_group_execution_mode *previous_mode,
|
||||
struct xe_sync_entry *syncs, int num_syncs);
|
||||
void xe_hw_engine_group_put(struct xe_hw_engine_group *group);
|
||||
|
||||
enum xe_hw_engine_group_execution_mode
|
||||
|
||||
@@ -502,7 +502,7 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at
|
||||
|
||||
int ret = 0;
|
||||
|
||||
xe_pm_runtime_get(hwmon->xe);
|
||||
guard(xe_pm_runtime)(hwmon->xe);
|
||||
|
||||
mutex_lock(&hwmon->hwmon_lock);
|
||||
|
||||
@@ -521,8 +521,6 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at
|
||||
|
||||
mutex_unlock(&hwmon->hwmon_lock);
|
||||
|
||||
xe_pm_runtime_put(hwmon->xe);
|
||||
|
||||
x = REG_FIELD_GET(PWR_LIM_TIME_X, reg_val);
|
||||
y = REG_FIELD_GET(PWR_LIM_TIME_Y, reg_val);
|
||||
|
||||
@@ -604,7 +602,7 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a
|
||||
rxy = REG_FIELD_PREP(PWR_LIM_TIME_X, x) |
|
||||
REG_FIELD_PREP(PWR_LIM_TIME_Y, y);
|
||||
|
||||
xe_pm_runtime_get(hwmon->xe);
|
||||
guard(xe_pm_runtime)(hwmon->xe);
|
||||
|
||||
mutex_lock(&hwmon->hwmon_lock);
|
||||
|
||||
@@ -616,8 +614,6 @@ xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *a
|
||||
|
||||
mutex_unlock(&hwmon->hwmon_lock);
|
||||
|
||||
xe_pm_runtime_put(hwmon->xe);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1124,37 +1120,25 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(hwmon->xe);
|
||||
guard(xe_pm_runtime)(hwmon->xe);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
ret = xe_hwmon_temp_read(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_temp_read(hwmon, attr, channel, val);
|
||||
case hwmon_power:
|
||||
ret = xe_hwmon_power_read(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_power_read(hwmon, attr, channel, val);
|
||||
case hwmon_curr:
|
||||
ret = xe_hwmon_curr_read(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_curr_read(hwmon, attr, channel, val);
|
||||
case hwmon_in:
|
||||
ret = xe_hwmon_in_read(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_in_read(hwmon, attr, channel, val);
|
||||
case hwmon_energy:
|
||||
ret = xe_hwmon_energy_read(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_energy_read(hwmon, attr, channel, val);
|
||||
case hwmon_fan:
|
||||
ret = xe_hwmon_fan_read(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_fan_read(hwmon, attr, channel, val);
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(hwmon->xe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1162,25 +1146,17 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(hwmon->xe);
|
||||
guard(xe_pm_runtime)(hwmon->xe);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_power:
|
||||
ret = xe_hwmon_power_write(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_power_write(hwmon, attr, channel, val);
|
||||
case hwmon_curr:
|
||||
ret = xe_hwmon_curr_write(hwmon, attr, channel, val);
|
||||
break;
|
||||
return xe_hwmon_curr_write(hwmon, attr, channel, val);
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
xe_pm_runtime_put(hwmon->xe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xe_hwmon_read_label(struct device *dev,
|
||||
|
||||
@@ -319,7 +319,7 @@ int xe_i2c_probe(struct xe_device *xe)
|
||||
struct xe_i2c *i2c;
|
||||
int ret;
|
||||
|
||||
if (xe->info.platform != XE_BATTLEMAGE)
|
||||
if (!xe->info.has_i2c)
|
||||
return 0;
|
||||
|
||||
if (IS_SRIOV_VF(xe))
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "xe_hw_error.h"
|
||||
#include "xe_i2c.h"
|
||||
#include "xe_memirq.h"
|
||||
#include "xe_mert.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_pxp.h"
|
||||
#include "xe_sriov.h"
|
||||
@@ -525,6 +526,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
|
||||
xe_heci_csc_irq_handler(xe, master_ctl);
|
||||
xe_display_irq_handler(xe, master_ctl);
|
||||
xe_i2c_irq_handler(xe, master_ctl);
|
||||
xe_mert_irq_handler(xe, master_ctl);
|
||||
gu_misc_iir = gu_misc_irq_ack(xe, master_ctl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,18 @@
|
||||
#include <drm/drm_managed.h>
|
||||
|
||||
#include "regs/xe_gt_regs.h"
|
||||
#include "regs/xe_mert_regs.h"
|
||||
|
||||
#include "xe_assert.h"
|
||||
#include "xe_bo.h"
|
||||
#include "xe_tlb_inval.h"
|
||||
#include "xe_lmtt.h"
|
||||
#include "xe_map.h"
|
||||
#include "xe_mert.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_res_cursor.h"
|
||||
#include "xe_sriov.h"
|
||||
#include "xe_tile.h"
|
||||
#include "xe_tile_sriov_printk.h"
|
||||
|
||||
/**
|
||||
@@ -196,16 +199,22 @@ static void lmtt_setup_dir_ptr(struct xe_lmtt *lmtt)
|
||||
struct xe_device *xe = tile_to_xe(tile);
|
||||
dma_addr_t offset = xe_bo_main_addr(lmtt->pd->bo, XE_PAGE_SIZE);
|
||||
struct xe_gt *gt;
|
||||
u32 config;
|
||||
u8 id;
|
||||
|
||||
lmtt_debug(lmtt, "DIR offset %pad\n", &offset);
|
||||
lmtt_assert(lmtt, xe_bo_is_vram(lmtt->pd->bo));
|
||||
lmtt_assert(lmtt, IS_ALIGNED(offset, SZ_64K));
|
||||
|
||||
config = LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K);
|
||||
|
||||
for_each_gt_on_tile(gt, tile, id)
|
||||
xe_mmio_write32(>->mmio,
|
||||
GRAPHICS_VER(xe) >= 20 ? XE2_LMEM_CFG : LMEM_CFG,
|
||||
LMEM_EN | REG_FIELD_PREP(LMTT_DIR_PTR, offset / SZ_64K));
|
||||
config);
|
||||
|
||||
if (xe_device_has_mert(xe) && xe_tile_is_root(tile))
|
||||
xe_mmio_write32(&tile->mmio, MERT_LMEM_CFG, config);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,19 +271,29 @@ static int lmtt_invalidate_hw(struct xe_lmtt *lmtt)
|
||||
* @lmtt: the &xe_lmtt to invalidate
|
||||
*
|
||||
* Send requests to all GuCs on this tile to invalidate all TLBs.
|
||||
* If the platform has a standalone MERT, also invalidate MERT's TLB.
|
||||
*
|
||||
* This function should be called only when running as a PF driver.
|
||||
*/
|
||||
void xe_lmtt_invalidate_hw(struct xe_lmtt *lmtt)
|
||||
{
|
||||
struct xe_tile *tile = lmtt_to_tile(lmtt);
|
||||
struct xe_device *xe = lmtt_to_xe(lmtt);
|
||||
int err;
|
||||
|
||||
lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt)));
|
||||
lmtt_assert(lmtt, IS_SRIOV_PF(xe));
|
||||
|
||||
err = lmtt_invalidate_hw(lmtt);
|
||||
if (err)
|
||||
xe_tile_sriov_err(lmtt_to_tile(lmtt), "LMTT invalidation failed (%pe)",
|
||||
xe_tile_sriov_err(tile, "LMTT invalidation failed (%pe)",
|
||||
ERR_PTR(err));
|
||||
|
||||
if (xe_device_has_mert(xe) && xe_tile_is_root(tile)) {
|
||||
err = xe_mert_invalidate_lmtt(tile);
|
||||
if (err)
|
||||
xe_tile_sriov_err(tile, "MERT LMTT invalidation failed (%pe)",
|
||||
ERR_PTR(err));
|
||||
}
|
||||
}
|
||||
|
||||
static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt,
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
#define LRC_INDIRECT_CTX_BO_SIZE SZ_4K
|
||||
#define LRC_INDIRECT_RING_STATE_SIZE SZ_4K
|
||||
|
||||
#define LRC_PRIORITY GENMASK_ULL(10, 9)
|
||||
#define LRC_PRIORITY_LOW 0
|
||||
#define LRC_PRIORITY_NORMAL 1
|
||||
#define LRC_PRIORITY_HIGH 2
|
||||
|
||||
/*
|
||||
* Layout of the LRC and associated data allocated as
|
||||
* lrc->bo:
|
||||
@@ -91,13 +96,19 @@ gt_engine_needs_indirect_ctx(struct xe_gt *gt, enum xe_engine_class class)
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
|
||||
/**
|
||||
* xe_gt_lrc_hang_replay_size() - Hang replay size
|
||||
* @gt: The GT
|
||||
* @class: Hardware engine class
|
||||
*
|
||||
* Determine size of GPU hang replay state for a GT and hardware engine class.
|
||||
*
|
||||
* Return: Size of GPU hang replay size
|
||||
*/
|
||||
size_t xe_gt_lrc_hang_replay_size(struct xe_gt *gt, enum xe_engine_class class)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
size_t size;
|
||||
|
||||
/* Per-process HW status page (PPHWSP) */
|
||||
size = LRC_PPHWSP_SIZE;
|
||||
size_t size = 0;
|
||||
|
||||
/* Engine context image */
|
||||
switch (class) {
|
||||
@@ -123,11 +134,18 @@ size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
|
||||
size += 1 * SZ_4K;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class)
|
||||
{
|
||||
size_t size = xe_gt_lrc_hang_replay_size(gt, class);
|
||||
|
||||
/* Add indirect ring state page */
|
||||
if (xe_gt_has_indirect_ring_state(gt))
|
||||
size += LRC_INDIRECT_RING_STATE_SIZE;
|
||||
|
||||
return size;
|
||||
return size + LRC_PPHWSP_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1386,8 +1404,33 @@ setup_indirect_ctx(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 xe_multi_queue_prio_to_lrc(struct xe_lrc *lrc, enum xe_multi_queue_priority priority)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(lrc->gt);
|
||||
|
||||
xe_assert(xe, (priority >= XE_MULTI_QUEUE_PRIORITY_LOW &&
|
||||
priority <= XE_MULTI_QUEUE_PRIORITY_HIGH));
|
||||
|
||||
/* xe_multi_queue_priority is directly mapped to LRC priority values */
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_lrc_set_multi_queue_priority() - Set multi queue priority in LRC
|
||||
* @lrc: Logical Ring Context
|
||||
* @priority: Multi queue priority of the exec queue
|
||||
*
|
||||
* Convert @priority to LRC multi queue priority and update the @lrc descriptor
|
||||
*/
|
||||
void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_priority priority)
|
||||
{
|
||||
lrc->desc &= ~LRC_PRIORITY;
|
||||
lrc->desc |= FIELD_PREP(LRC_PRIORITY, xe_multi_queue_prio_to_lrc(lrc, priority));
|
||||
}
|
||||
|
||||
static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
struct xe_vm *vm, u32 ring_size, u16 msix_vec,
|
||||
struct xe_vm *vm, void *replay_state, u32 ring_size,
|
||||
u16 msix_vec,
|
||||
u32 init_flags)
|
||||
{
|
||||
struct xe_gt *gt = hwe->gt;
|
||||
@@ -1402,6 +1445,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
|
||||
kref_init(&lrc->refcount);
|
||||
lrc->gt = gt;
|
||||
lrc->replay_size = xe_gt_lrc_hang_replay_size(gt, hwe->class);
|
||||
lrc->size = lrc_size;
|
||||
lrc->flags = 0;
|
||||
lrc->ring.size = ring_size;
|
||||
@@ -1438,11 +1482,14 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
* scratch.
|
||||
*/
|
||||
map = __xe_lrc_pphwsp_map(lrc);
|
||||
if (gt->default_lrc[hwe->class]) {
|
||||
if (gt->default_lrc[hwe->class] || replay_state) {
|
||||
xe_map_memset(xe, &map, 0, 0, LRC_PPHWSP_SIZE); /* PPHWSP */
|
||||
xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE,
|
||||
gt->default_lrc[hwe->class] + LRC_PPHWSP_SIZE,
|
||||
lrc_size - LRC_PPHWSP_SIZE);
|
||||
if (replay_state)
|
||||
xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE,
|
||||
replay_state, lrc->replay_size);
|
||||
} else {
|
||||
void *init_data = empty_lrc_data(hwe);
|
||||
|
||||
@@ -1550,6 +1597,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
* xe_lrc_create - Create a LRC
|
||||
* @hwe: Hardware Engine
|
||||
* @vm: The VM (address space)
|
||||
* @replay_state: GPU hang replay state
|
||||
* @ring_size: LRC ring size
|
||||
* @msix_vec: MSI-X interrupt vector (for platforms that support it)
|
||||
* @flags: LRC initialization flags
|
||||
@@ -1560,7 +1608,7 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
* upon failure.
|
||||
*/
|
||||
struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
|
||||
u32 ring_size, u16 msix_vec, u32 flags)
|
||||
void *replay_state, u32 ring_size, u16 msix_vec, u32 flags)
|
||||
{
|
||||
struct xe_lrc *lrc;
|
||||
int err;
|
||||
@@ -1569,7 +1617,7 @@ struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
|
||||
if (!lrc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = xe_lrc_init(lrc, hwe, vm, ring_size, msix_vec, flags);
|
||||
err = xe_lrc_init(lrc, hwe, vm, replay_state, ring_size, msix_vec, flags);
|
||||
if (err) {
|
||||
kfree(lrc);
|
||||
return ERR_PTR(err);
|
||||
@@ -2235,6 +2283,8 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
|
||||
snapshot->lrc_bo = xe_bo_get(lrc->bo);
|
||||
snapshot->lrc_offset = xe_lrc_pphwsp_offset(lrc);
|
||||
snapshot->lrc_size = lrc->size;
|
||||
snapshot->replay_offset = 0;
|
||||
snapshot->replay_size = lrc->replay_size;
|
||||
snapshot->lrc_snapshot = NULL;
|
||||
snapshot->ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(lrc));
|
||||
snapshot->ctx_job_timestamp = xe_lrc_ctx_job_timestamp(lrc);
|
||||
@@ -2305,6 +2355,9 @@ void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer
|
||||
}
|
||||
|
||||
drm_printf(p, "\n\t[HWCTX].length: 0x%lx\n", snapshot->lrc_size - LRC_PPHWSP_SIZE);
|
||||
drm_printf(p, "\n\t[HWCTX].replay_offset: 0x%lx\n", snapshot->replay_offset);
|
||||
drm_printf(p, "\n\t[HWCTX].replay_length: 0x%lx\n", snapshot->replay_size);
|
||||
|
||||
drm_puts(p, "\t[HWCTX].data: ");
|
||||
for (; i < snapshot->lrc_size; i += sizeof(u32)) {
|
||||
u32 *val = snapshot->lrc_snapshot + i;
|
||||
|
||||
@@ -13,6 +13,7 @@ struct drm_printer;
|
||||
struct xe_bb;
|
||||
struct xe_device;
|
||||
struct xe_exec_queue;
|
||||
enum xe_multi_queue_priority;
|
||||
enum xe_engine_class;
|
||||
struct xe_gt;
|
||||
struct xe_hw_engine;
|
||||
@@ -23,6 +24,7 @@ struct xe_lrc_snapshot {
|
||||
struct xe_bo *lrc_bo;
|
||||
void *lrc_snapshot;
|
||||
unsigned long lrc_size, lrc_offset;
|
||||
unsigned long replay_size, replay_offset;
|
||||
|
||||
u32 context_desc;
|
||||
u32 ring_addr;
|
||||
@@ -49,7 +51,7 @@ struct xe_lrc_snapshot {
|
||||
#define XE_LRC_CREATE_USER_CTX BIT(2)
|
||||
|
||||
struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
|
||||
u32 ring_size, u16 msix_vec, u32 flags);
|
||||
void *replay_state, u32 ring_size, u16 msix_vec, u32 flags);
|
||||
void xe_lrc_destroy(struct kref *ref);
|
||||
|
||||
/**
|
||||
@@ -86,6 +88,7 @@ static inline size_t xe_lrc_ring_size(void)
|
||||
return SZ_16K;
|
||||
}
|
||||
|
||||
size_t xe_gt_lrc_hang_replay_size(struct xe_gt *gt, enum xe_engine_class class);
|
||||
size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class);
|
||||
u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc);
|
||||
u32 xe_lrc_regs_offset(struct xe_lrc *lrc);
|
||||
@@ -133,6 +136,8 @@ void xe_lrc_dump_default(struct drm_printer *p,
|
||||
|
||||
u32 *xe_lrc_emit_hwe_state_instructions(struct xe_exec_queue *q, u32 *cs);
|
||||
|
||||
void xe_lrc_set_multi_queue_priority(struct xe_lrc *lrc, enum xe_multi_queue_priority priority);
|
||||
|
||||
struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc);
|
||||
void xe_lrc_snapshot_capture_delayed(struct xe_lrc_snapshot *snapshot);
|
||||
void xe_lrc_snapshot_print(struct xe_lrc_snapshot *snapshot, struct drm_printer *p);
|
||||
|
||||
@@ -25,6 +25,9 @@ struct xe_lrc {
|
||||
/** @size: size of the lrc and optional indirect ring state */
|
||||
u32 size;
|
||||
|
||||
/** @replay_size: Size LRC needed for replaying a hang */
|
||||
u32 replay_size;
|
||||
|
||||
/** @gt: gt which this LRC belongs to */
|
||||
struct xe_gt *gt;
|
||||
|
||||
|
||||
82
drivers/gpu/drm/xe/xe_mert.c
Normal file
82
drivers/gpu/drm/xe/xe_mert.c
Normal file
@@ -0,0 +1,82 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright(c) 2025, Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "regs/xe_irq_regs.h"
|
||||
#include "regs/xe_mert_regs.h"
|
||||
|
||||
#include "xe_device.h"
|
||||
#include "xe_mert.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_tile.h"
|
||||
|
||||
/**
|
||||
* xe_mert_invalidate_lmtt - Invalidate MERT LMTT
|
||||
* @tile: the &xe_tile
|
||||
*
|
||||
* Trigger invalidation of the MERT LMTT and wait for completion.
|
||||
*
|
||||
* Return: 0 on success or -ETIMEDOUT in case of a timeout.
|
||||
*/
|
||||
int xe_mert_invalidate_lmtt(struct xe_tile *tile)
|
||||
{
|
||||
struct xe_device *xe = tile_to_xe(tile);
|
||||
struct xe_mert *mert = &tile->mert;
|
||||
const long timeout = HZ / 4;
|
||||
unsigned long flags;
|
||||
|
||||
xe_assert(xe, xe_device_has_mert(xe));
|
||||
xe_assert(xe, xe_tile_is_root(tile));
|
||||
|
||||
spin_lock_irqsave(&mert->lock, flags);
|
||||
if (!mert->tlb_inv_triggered) {
|
||||
mert->tlb_inv_triggered = true;
|
||||
reinit_completion(&mert->tlb_inv_done);
|
||||
xe_mmio_write32(&tile->mmio, MERT_TLB_INV_DESC_A, MERT_TLB_INV_DESC_A_VALID);
|
||||
}
|
||||
spin_unlock_irqrestore(&mert->lock, flags);
|
||||
|
||||
if (!wait_for_completion_timeout(&mert->tlb_inv_done, timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_mert_irq_handler - Handler for MERT interrupts
|
||||
* @xe: the &xe_device
|
||||
* @master_ctl: interrupt register
|
||||
*
|
||||
* Handle interrupts generated by MERT.
|
||||
*/
|
||||
void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl)
|
||||
{
|
||||
struct xe_tile *tile = xe_device_get_root_tile(xe);
|
||||
unsigned long flags;
|
||||
u32 reg_val;
|
||||
u8 err;
|
||||
|
||||
if (!(master_ctl & SOC_H2DMEMINT_IRQ))
|
||||
return;
|
||||
|
||||
reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT);
|
||||
xe_mmio_write32(&tile->mmio, MERT_TLB_CT_INTR_ERR_ID_PORT, 0);
|
||||
|
||||
err = FIELD_GET(MERT_TLB_CT_ERROR_MASK, reg_val);
|
||||
if (err == MERT_TLB_CT_LMTT_FAULT)
|
||||
drm_dbg(&xe->drm, "MERT catastrophic error: LMTT fault (VF%u)\n",
|
||||
FIELD_GET(MERT_TLB_CT_VFID_MASK, reg_val));
|
||||
else if (err)
|
||||
drm_dbg(&xe->drm, "MERT catastrophic error: Unexpected fault (0x%x)\n", err);
|
||||
|
||||
spin_lock_irqsave(&tile->mert.lock, flags);
|
||||
if (tile->mert.tlb_inv_triggered) {
|
||||
reg_val = xe_mmio_read32(&tile->mmio, MERT_TLB_INV_DESC_A);
|
||||
if (!(reg_val & MERT_TLB_INV_DESC_A_VALID)) {
|
||||
tile->mert.tlb_inv_triggered = false;
|
||||
complete_all(&tile->mert.tlb_inv_done);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&tile->mert.lock, flags);
|
||||
}
|
||||
32
drivers/gpu/drm/xe/xe_mert.h
Normal file
32
drivers/gpu/drm/xe/xe_mert.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright(c) 2025, Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __XE_MERT_H__
|
||||
#define __XE_MERT_H__
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct xe_device;
|
||||
struct xe_tile;
|
||||
|
||||
struct xe_mert {
|
||||
/** @lock: protects the TLB invalidation status */
|
||||
spinlock_t lock;
|
||||
/** @tlb_inv_triggered: indicates if TLB invalidation was triggered */
|
||||
bool tlb_inv_triggered;
|
||||
/** @mert.tlb_inv_done: completion of TLB invalidation */
|
||||
struct completion tlb_inv_done;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
int xe_mert_invalidate_lmtt(struct xe_tile *tile);
|
||||
void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl);
|
||||
#else
|
||||
static inline void xe_mert_irq_handler(struct xe_device *xe, u32 master_ctl) { }
|
||||
#endif
|
||||
|
||||
#endif /* __XE_MERT_H__ */
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "xe_res_cursor.h"
|
||||
#include "xe_sa.h"
|
||||
#include "xe_sched_job.h"
|
||||
#include "xe_sriov_vf_ccs.h"
|
||||
#include "xe_sync.h"
|
||||
#include "xe_trace_bo.h"
|
||||
#include "xe_validation.h"
|
||||
@@ -1103,12 +1104,16 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
|
||||
u32 batch_size, batch_size_allocated;
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
struct xe_res_cursor src_it, ccs_it;
|
||||
struct xe_sriov_vf_ccs_ctx *ctx;
|
||||
struct xe_sa_manager *bb_pool;
|
||||
u64 size = xe_bo_size(src_bo);
|
||||
struct xe_bb *bb = NULL;
|
||||
u64 src_L0, src_L0_ofs;
|
||||
u32 src_L0_pt;
|
||||
int err;
|
||||
|
||||
ctx = &xe->sriov.vf.ccs.contexts[read_write];
|
||||
|
||||
xe_res_first_sg(xe_bo_sg(src_bo), 0, size, &src_it);
|
||||
|
||||
xe_res_first_sg(xe_bo_sg(src_bo), xe_bo_ccs_pages_start(src_bo),
|
||||
@@ -1141,11 +1146,15 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
|
||||
size -= src_L0;
|
||||
}
|
||||
|
||||
bb_pool = ctx->mem.ccs_bb_pool;
|
||||
guard(mutex) (xe_sa_bo_swap_guard(bb_pool));
|
||||
xe_sa_bo_swap_shadow(bb_pool);
|
||||
|
||||
bb = xe_bb_ccs_new(gt, batch_size, read_write);
|
||||
if (IS_ERR(bb)) {
|
||||
drm_err(&xe->drm, "BB allocation failed.\n");
|
||||
err = PTR_ERR(bb);
|
||||
goto err_ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
batch_size_allocated = batch_size;
|
||||
@@ -1194,10 +1203,52 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
|
||||
xe_assert(xe, (batch_size_allocated == bb->len));
|
||||
src_bo->bb_ccs[read_write] = bb;
|
||||
|
||||
xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
|
||||
xe_sa_bo_sync_shadow(bb->bo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return err;
|
||||
/**
|
||||
* xe_migrate_ccs_rw_copy_clear() - Clear the CCS read/write batch buffer
|
||||
* content.
|
||||
* @src_bo: The buffer object @src is currently bound to.
|
||||
* @read_write : Creates BB commands for CCS read/write.
|
||||
*
|
||||
* Directly clearing the BB lacks atomicity and can lead to undefined
|
||||
* behavior if the vCPU is halted mid-operation during the clearing
|
||||
* process. To avoid this issue, we use a shadow buffer object approach.
|
||||
*
|
||||
* First swap the SA BO address with the shadow BO, perform the clearing
|
||||
* operation on the BB, update the shadow BO in the ring buffer, then
|
||||
* sync the shadow and the actual buffer to maintain consistency.
|
||||
*
|
||||
* Returns: None.
|
||||
*/
|
||||
void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
|
||||
enum xe_sriov_vf_ccs_rw_ctxs read_write)
|
||||
{
|
||||
struct xe_bb *bb = src_bo->bb_ccs[read_write];
|
||||
struct xe_device *xe = xe_bo_device(src_bo);
|
||||
struct xe_sriov_vf_ccs_ctx *ctx;
|
||||
struct xe_sa_manager *bb_pool;
|
||||
u32 *cs;
|
||||
|
||||
xe_assert(xe, IS_SRIOV_VF(xe));
|
||||
|
||||
ctx = &xe->sriov.vf.ccs.contexts[read_write];
|
||||
bb_pool = ctx->mem.ccs_bb_pool;
|
||||
|
||||
guard(mutex) (xe_sa_bo_swap_guard(bb_pool));
|
||||
xe_sa_bo_swap_shadow(bb_pool);
|
||||
|
||||
cs = xe_sa_bo_cpu_addr(bb->bo);
|
||||
memset(cs, MI_NOOP, bb->len * sizeof(u32));
|
||||
xe_sriov_vf_ccs_rw_update_bb_addr(ctx);
|
||||
|
||||
xe_sa_bo_sync_shadow(bb->bo);
|
||||
|
||||
xe_bb_free(bb, NULL);
|
||||
src_bo->bb_ccs[read_write] = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -134,6 +134,9 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
|
||||
struct xe_bo *src_bo,
|
||||
enum xe_sriov_vf_ccs_rw_ctxs read_write);
|
||||
|
||||
void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,
|
||||
enum xe_sriov_vf_ccs_rw_ctxs read_write);
|
||||
|
||||
struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate);
|
||||
struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate);
|
||||
struct dma_fence *xe_migrate_vram_copy_chunk(struct xe_bo *vram_bo, u64 vram_offset,
|
||||
|
||||
@@ -811,26 +811,20 @@ int xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
enum xe_force_wake_domains domain;
|
||||
struct xe_mocs_info table;
|
||||
unsigned int fw_ref, flags;
|
||||
int err = 0;
|
||||
unsigned int flags;
|
||||
|
||||
flags = get_mocs_settings(xe, &table);
|
||||
|
||||
domain = flags & HAS_LNCF_MOCS ? XE_FORCEWAKE_ALL : XE_FW_GT;
|
||||
xe_pm_runtime_get_noresume(xe);
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), domain);
|
||||
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref, domain)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto err_fw;
|
||||
}
|
||||
guard(xe_pm_runtime_noresume)(xe);
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), domain);
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, domain))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
table.ops->dump(&table, flags, gt, p);
|
||||
|
||||
err_fw:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
xe_pm_runtime_put(xe);
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "xe_device_types.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_nvm.h"
|
||||
#include "xe_pcode_api.h"
|
||||
#include "regs/xe_gsc_regs.h"
|
||||
#include "xe_sriov.h"
|
||||
|
||||
@@ -45,39 +46,50 @@ static bool xe_nvm_non_posted_erase(struct xe_device *xe)
|
||||
{
|
||||
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
|
||||
|
||||
if (xe->info.platform != XE_BATTLEMAGE)
|
||||
switch (xe->info.platform) {
|
||||
case XE_CRESCENTISLAND:
|
||||
case XE_BATTLEMAGE:
|
||||
return !(xe_mmio_read32(mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
|
||||
NVM_NON_POSTED_ERASE_CHICKEN_BIT);
|
||||
default:
|
||||
return false;
|
||||
return !(xe_mmio_read32(mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
|
||||
NVM_NON_POSTED_ERASE_CHICKEN_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
static bool xe_nvm_writable_override(struct xe_device *xe)
|
||||
{
|
||||
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
|
||||
bool writable_override;
|
||||
resource_size_t base;
|
||||
struct xe_reg reg;
|
||||
u32 test_bit;
|
||||
|
||||
switch (xe->info.platform) {
|
||||
case XE_CRESCENTISLAND:
|
||||
reg = PCODE_SCRATCH(0);
|
||||
test_bit = FDO_MODE;
|
||||
break;
|
||||
case XE_BATTLEMAGE:
|
||||
base = DG2_GSC_HECI2_BASE;
|
||||
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
|
||||
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
|
||||
break;
|
||||
case XE_PVC:
|
||||
base = PVC_GSC_HECI2_BASE;
|
||||
reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE);
|
||||
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
|
||||
break;
|
||||
case XE_DG2:
|
||||
base = DG2_GSC_HECI2_BASE;
|
||||
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
|
||||
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
|
||||
break;
|
||||
case XE_DG1:
|
||||
base = DG1_GSC_HECI2_BASE;
|
||||
reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE);
|
||||
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
|
||||
break;
|
||||
default:
|
||||
drm_err(&xe->drm, "Unknown platform\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
writable_override =
|
||||
!(xe_mmio_read32(mmio, HECI_FWSTS2(base)) &
|
||||
HECI_FW_STATUS_2_NVM_ACCESS_MODE);
|
||||
writable_override = !(xe_mmio_read32(mmio, reg) & test_bit);
|
||||
if (writable_override)
|
||||
drm_info(&xe->drm, "NVM access overridden by jumper\n");
|
||||
return writable_override;
|
||||
|
||||
@@ -1941,6 +1941,7 @@ static bool oa_unit_supports_oa_format(struct xe_oa_open_param *param, int type)
|
||||
type == DRM_XE_OA_FMT_TYPE_OAC || type == DRM_XE_OA_FMT_TYPE_PEC;
|
||||
case DRM_XE_OA_UNIT_TYPE_OAM:
|
||||
case DRM_XE_OA_UNIT_TYPE_OAM_SAG:
|
||||
case DRM_XE_OA_UNIT_TYPE_MERT:
|
||||
return type == DRM_XE_OA_FMT_TYPE_OAM || type == DRM_XE_OA_FMT_TYPE_OAM_MPEC;
|
||||
default:
|
||||
return false;
|
||||
@@ -1966,10 +1967,6 @@ static int xe_oa_assign_hwe(struct xe_oa *oa, struct xe_oa_open_param *param)
|
||||
enum xe_hw_engine_id id;
|
||||
int ret = 0;
|
||||
|
||||
/* If not provided, OA unit defaults to OA unit 0 as per uapi */
|
||||
if (!param->oa_unit)
|
||||
param->oa_unit = &xe_root_mmio_gt(oa->xe)->oa.oa_unit[0];
|
||||
|
||||
/* When we have an exec_q, get hwe from the exec_q */
|
||||
if (param->exec_q) {
|
||||
param->hwe = xe_gt_hw_engine(param->exec_q->gt, param->exec_q->class,
|
||||
@@ -2035,7 +2032,15 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If not provided, OA unit defaults to OA unit 0 as per uapi */
|
||||
if (!param.oa_unit)
|
||||
param.oa_unit = &xe_root_mmio_gt(oa->xe)->oa.oa_unit[0];
|
||||
|
||||
if (param.exec_queue_id > 0) {
|
||||
/* An exec_queue is only needed for OAR/OAC functionality on OAG */
|
||||
if (XE_IOCTL_DBG(oa->xe, param.oa_unit->type != DRM_XE_OA_UNIT_TYPE_OAG))
|
||||
return -EINVAL;
|
||||
|
||||
param.exec_q = xe_exec_queue_lookup(xef, param.exec_queue_id);
|
||||
if (XE_IOCTL_DBG(oa->xe, !param.exec_q))
|
||||
return -ENOENT;
|
||||
@@ -2224,6 +2229,8 @@ static const struct xe_mmio_range xe2_oa_mux_regs[] = {
|
||||
{ .start = 0xE18C, .end = 0xE18C }, /* SAMPLER_MODE */
|
||||
{ .start = 0xE590, .end = 0xE590 }, /* TDL_LSC_LAT_MEASURE_TDL_GFX */
|
||||
{ .start = 0x13000, .end = 0x137FC }, /* PES_0_PESL0 - PES_63_UPPER_PESL3 */
|
||||
{ .start = 0x145194, .end = 0x145194 }, /* SYS_MEM_LAT_MEASURE */
|
||||
{ .start = 0x145340, .end = 0x14537C }, /* MERTSS_PES_0 - MERTSS_PES_7 */
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -2515,7 +2522,12 @@ int xe_oa_register(struct xe_device *xe)
|
||||
static u32 num_oa_units_per_gt(struct xe_gt *gt)
|
||||
{
|
||||
if (xe_gt_is_main_type(gt) || GRAPHICS_VER(gt_to_xe(gt)) < 20)
|
||||
return 1;
|
||||
/*
|
||||
* Mert OA unit belongs to the SoC, not a gt, so should be accessed using
|
||||
* xe_root_tile_mmio(). However, for all known platforms this is the same as
|
||||
* accessing via xe_root_mmio_gt()->mmio.
|
||||
*/
|
||||
return xe_device_has_mert(gt_to_xe(gt)) ? 2 : 1;
|
||||
else if (!IS_DGFX(gt_to_xe(gt)))
|
||||
return XE_OAM_UNIT_SCMI_0 + 1; /* SAG + SCMI_0 */
|
||||
else
|
||||
@@ -2570,40 +2582,57 @@ static u32 __hwe_oa_unit(struct xe_hw_engine *hwe)
|
||||
static struct xe_oa_regs __oam_regs(u32 base)
|
||||
{
|
||||
return (struct xe_oa_regs) {
|
||||
base,
|
||||
OAM_HEAD_POINTER(base),
|
||||
OAM_TAIL_POINTER(base),
|
||||
OAM_BUFFER(base),
|
||||
OAM_CONTEXT_CONTROL(base),
|
||||
OAM_CONTROL(base),
|
||||
OAM_DEBUG(base),
|
||||
OAM_STATUS(base),
|
||||
OAM_CONTROL_COUNTER_SEL_MASK,
|
||||
.base = base,
|
||||
.oa_head_ptr = OAM_HEAD_POINTER(base),
|
||||
.oa_tail_ptr = OAM_TAIL_POINTER(base),
|
||||
.oa_buffer = OAM_BUFFER(base),
|
||||
.oa_ctx_ctrl = OAM_CONTEXT_CONTROL(base),
|
||||
.oa_ctrl = OAM_CONTROL(base),
|
||||
.oa_debug = OAM_DEBUG(base),
|
||||
.oa_status = OAM_STATUS(base),
|
||||
.oa_mmio_trg = OAM_MMIO_TRG(base),
|
||||
.oa_ctrl_counter_select_mask = OAM_CONTROL_COUNTER_SEL_MASK,
|
||||
};
|
||||
}
|
||||
|
||||
static struct xe_oa_regs __oag_regs(void)
|
||||
{
|
||||
return (struct xe_oa_regs) {
|
||||
0,
|
||||
OAG_OAHEADPTR,
|
||||
OAG_OATAILPTR,
|
||||
OAG_OABUFFER,
|
||||
OAG_OAGLBCTXCTRL,
|
||||
OAG_OACONTROL,
|
||||
OAG_OA_DEBUG,
|
||||
OAG_OASTATUS,
|
||||
OAG_OACONTROL_OA_COUNTER_SEL_MASK,
|
||||
.base = 0,
|
||||
.oa_head_ptr = OAG_OAHEADPTR,
|
||||
.oa_tail_ptr = OAG_OATAILPTR,
|
||||
.oa_buffer = OAG_OABUFFER,
|
||||
.oa_ctx_ctrl = OAG_OAGLBCTXCTRL,
|
||||
.oa_ctrl = OAG_OACONTROL,
|
||||
.oa_debug = OAG_OA_DEBUG,
|
||||
.oa_status = OAG_OASTATUS,
|
||||
.oa_mmio_trg = OAG_MMIOTRIGGER,
|
||||
.oa_ctrl_counter_select_mask = OAG_OACONTROL_OA_COUNTER_SEL_MASK,
|
||||
};
|
||||
}
|
||||
|
||||
static struct xe_oa_regs __oamert_regs(void)
|
||||
{
|
||||
return (struct xe_oa_regs) {
|
||||
.base = 0,
|
||||
.oa_head_ptr = OAMERT_HEAD_POINTER,
|
||||
.oa_tail_ptr = OAMERT_TAIL_POINTER,
|
||||
.oa_buffer = OAMERT_BUFFER,
|
||||
.oa_ctx_ctrl = OAMERT_CONTEXT_CONTROL,
|
||||
.oa_ctrl = OAMERT_CONTROL,
|
||||
.oa_debug = OAMERT_DEBUG,
|
||||
.oa_status = OAMERT_STATUS,
|
||||
.oa_mmio_trg = OAMERT_MMIO_TRG,
|
||||
.oa_ctrl_counter_select_mask = OAM_CONTROL_COUNTER_SEL_MASK,
|
||||
};
|
||||
}
|
||||
|
||||
static void __xe_oa_init_oa_units(struct xe_gt *gt)
|
||||
{
|
||||
/* Actual address is MEDIA_GT_GSI_OFFSET + oam_base_addr[i] */
|
||||
const u32 oam_base_addr[] = {
|
||||
[XE_OAM_UNIT_SAG] = 0x13000,
|
||||
[XE_OAM_UNIT_SCMI_0] = 0x14000,
|
||||
[XE_OAM_UNIT_SCMI_1] = 0x14800,
|
||||
[XE_OAM_UNIT_SAG] = XE_OAM_SAG_BASE,
|
||||
[XE_OAM_UNIT_SCMI_0] = XE_OAM_SCMI_0_BASE,
|
||||
[XE_OAM_UNIT_SCMI_1] = XE_OAM_SCMI_1_BASE,
|
||||
};
|
||||
int i, num_units = gt->oa.num_oa_units;
|
||||
|
||||
@@ -2611,8 +2640,15 @@ static void __xe_oa_init_oa_units(struct xe_gt *gt)
|
||||
struct xe_oa_unit *u = >->oa.oa_unit[i];
|
||||
|
||||
if (xe_gt_is_main_type(gt)) {
|
||||
u->regs = __oag_regs();
|
||||
u->type = DRM_XE_OA_UNIT_TYPE_OAG;
|
||||
if (!i) {
|
||||
u->regs = __oag_regs();
|
||||
u->type = DRM_XE_OA_UNIT_TYPE_OAG;
|
||||
} else {
|
||||
xe_gt_assert(gt, xe_device_has_mert(gt_to_xe(gt)));
|
||||
xe_gt_assert(gt, gt == xe_root_mmio_gt(gt_to_xe(gt)));
|
||||
u->regs = __oamert_regs();
|
||||
u->type = DRM_XE_OA_UNIT_TYPE_MERT;
|
||||
}
|
||||
} else {
|
||||
xe_gt_assert(gt, GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270);
|
||||
u->regs = __oam_regs(oam_base_addr[i]);
|
||||
|
||||
@@ -87,6 +87,7 @@ struct xe_oa_regs {
|
||||
struct xe_reg oa_ctrl;
|
||||
struct xe_reg oa_debug;
|
||||
struct xe_reg oa_status;
|
||||
struct xe_reg oa_mmio_trg;
|
||||
u32 oa_ctrl_counter_select_mask;
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user