mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-23 03:45:48 -04:00
Merge tag 'drm-xe-next-2026-07-03' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next
UAPI Changes:
- Add additional error components to xe drm_ras (Riana)
- Drop 'force_execlist' module parameter (Roper)
Cross-subsystem Changes:
- Perf events: Export perf_allow_{cpu,tracepoint} to be used by Xe (John)
Display Changes:
- Skip FORCE_WC and vm_bound check for external dma-bufs (Auld)
Driver Changes:
- Gate observation streams with perf_allow_cpu (John Hubbard)
- Documentation updates and fixes (Michal, Zhan)
- Remove unreleased NVL-S GuC (Daniele)
- Fix pcode init path (Michal)
- RTP fixes and improvements (Gustavo, Violet, Thomas, Roper)
- TLB invalidation fixes and improvements (Tilak)
- PXP detachment from HuC for newer platforms (Daniele)
- Multi-queue fix (Niranjana)
- Improve Kconfig.profile help (Rodrigo)
- Xe_drm_ras and hw_error updates and fixes (Raag, Riana)
- NVL-S updated PCI-IDs and W/a (Gustavo, Nitin)
- Fix dma_fence refcound (Wentao)
- Madvise: optimize invalidation path (Arvind)
- Fix a infinite gt-reset loop in the timeout recovery (Rodrigo)
- Fix wa_oob codegen recipe for external module builds (Thomas)
- Avoid global forcewake in cycle query path (Xin)
- Update TTM device benefical_order (Brost)
- Fix buffer overflow in guc capture (Tejas)
- Page-table fixes and improvements (Brian, Francois, Auld, Brost)
- Removing redundant check (Lu)
- General MCR and MMIO clean-up and improvements (Michal)
- Don't whitelist OA registers unconditionally (Ashutosh)
- SVM error return fix (Brost)
- Userptr fix and small related clean-ups (Shuicheng)
- Don't attempt to process FAST_REQ or EVENT relays on PF (Michal)
- Couple fdinfo improvements (Auld)
- Drop manual VF check on i2c (Raag)
- Probe info outside of xe_info_init functions (Gustavo)
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patch.msgid.link/akf7xr96MI4Rd6Qj@intel.com
This commit is contained in:
@@ -251,6 +251,13 @@ Description: RO. Fan 2 speed in RPM.
|
||||
|
||||
Only supported for particular Intel Xe graphics platforms.
|
||||
|
||||
On DG2 the driver always shows two fan channels, because the
|
||||
FSC_READ_NUM_FANS command does not work on some cards. OEMs
|
||||
decide how the fans map to tach channels, so two fans can share
|
||||
one tach line. When that happens, the second channel
|
||||
reads 0 RPM even though the fan is spinning. This is normal, not
|
||||
a bug.
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/fan3_input
|
||||
Date: March 2025
|
||||
KernelVersion: 6.16
|
||||
|
||||
@@ -1,50 +1,71 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config DRM_XE_JOB_TIMEOUT_MAX
|
||||
int "Default max job timeout (ms)"
|
||||
int "Hard upper limit for job timeout (ms)"
|
||||
default 10000 # milliseconds
|
||||
help
|
||||
Configures the default max job timeout after which job will
|
||||
be forcefully taken away from scheduler.
|
||||
Absolute upper bound (in milliseconds) for the per-engine-class job
|
||||
timeout. This is the maximum value that can be written to the sysfs
|
||||
job_timeout_ms knob, regardless of privileges. To raise this ceiling,
|
||||
increase this value and rebuild the kernel.
|
||||
config DRM_XE_JOB_TIMEOUT_MIN
|
||||
int "Default min job timeout (ms)"
|
||||
int "Hard lower limit for job timeout (ms)"
|
||||
default 1 # milliseconds
|
||||
help
|
||||
Configures the default min job timeout after which job will
|
||||
be forcefully taken away from scheduler.
|
||||
Absolute lower bound (in milliseconds) for the per-engine-class job
|
||||
timeout. This is the minimum value that can be written to the sysfs
|
||||
job_timeout_ms knob, regardless of privileges.
|
||||
|
||||
Note: the job timeout default (5000 ms) is hardcoded in the driver
|
||||
and is not configurable here. Use the sysfs job_timeout_ms knob at
|
||||
runtime to change the engine-class default.
|
||||
config DRM_XE_TIMESLICE_MAX
|
||||
int "Default max timeslice duration (us)"
|
||||
int "Hard upper limit for timeslice duration (us)"
|
||||
default 10000000 # microseconds
|
||||
help
|
||||
Configures the default max timeslice duration between multiple
|
||||
contexts by guc scheduling.
|
||||
Absolute upper bound (in microseconds) for the timeslice duration.
|
||||
This caps both the sysfs timeslice_duration_us knob and the value
|
||||
accepted via the DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE UAPI for
|
||||
processes with CAP_SYS_NICE when DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT
|
||||
is enabled.
|
||||
config DRM_XE_TIMESLICE_MIN
|
||||
int "Default min timeslice duration (us)"
|
||||
int "Hard lower limit for timeslice duration (us)"
|
||||
default 1 # microseconds
|
||||
help
|
||||
Configures the default min timeslice duration between multiple
|
||||
contexts by guc scheduling.
|
||||
Absolute lower bound (in microseconds) for the timeslice duration.
|
||||
This caps both the sysfs timeslice_duration_us knob and the value
|
||||
accepted via the DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE UAPI for
|
||||
processes with CAP_SYS_NICE when DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT
|
||||
is enabled.
|
||||
config DRM_XE_PREEMPT_TIMEOUT
|
||||
int "Preempt timeout (us, jiffy granularity)"
|
||||
int "Default preempt timeout (us, jiffy granularity)"
|
||||
default 640000 # microseconds
|
||||
help
|
||||
How long to wait (in microseconds) for a preemption event to occur
|
||||
when submitting a new context. If the current context does not hit
|
||||
an arbitration point and yield to HW before the timer expires, the
|
||||
HW will be reset to allow the more important context to execute.
|
||||
Initial per-engine-class preemption timeout (in microseconds). This
|
||||
is the value the driver programs at boot; it can be changed at
|
||||
runtime via the sysfs preempt_timeout_us knob.
|
||||
|
||||
This is how long the driver waits for the current context to reach
|
||||
an arbitration point and yield the GPU voluntarily when a
|
||||
higher-priority context becomes runnable. If the context does not
|
||||
yield before the timer expires, the HW is reset to allow the
|
||||
higher-priority context to execute.
|
||||
|
||||
The range userspace may write via sysfs is bounded by
|
||||
DRM_XE_PREEMPT_TIMEOUT_MIN and DRM_XE_PREEMPT_TIMEOUT_MAX.
|
||||
config DRM_XE_PREEMPT_TIMEOUT_MAX
|
||||
int "Default max preempt timeout (us)"
|
||||
int "Hard upper limit for preempt timeout (us)"
|
||||
default 10000000 # microseconds
|
||||
help
|
||||
Configures the default max preempt timeout after which context
|
||||
will be forcefully taken away and higher priority context will
|
||||
run.
|
||||
Absolute upper bound (in microseconds) for the per-engine-class
|
||||
preemption timeout. This is the maximum value that can be written to
|
||||
the sysfs preempt_timeout_us knob, regardless of privileges.
|
||||
config DRM_XE_PREEMPT_TIMEOUT_MIN
|
||||
int "Default min preempt timeout (us)"
|
||||
int "Hard lower limit for preempt timeout (us)"
|
||||
default 1 # microseconds
|
||||
help
|
||||
Configures the default min preempt timeout after which context
|
||||
will be forcefully taken away and higher priority context will
|
||||
run.
|
||||
Absolute lower bound (in microseconds) for the per-engine-class
|
||||
preemption timeout. This is the minimum value that can be written to
|
||||
the sysfs preempt_timeout_us knob, regardless of privileges.
|
||||
config DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT
|
||||
bool "Default configuration of limitation on scheduler timeout"
|
||||
default y
|
||||
|
||||
@@ -101,6 +101,7 @@ xe-y += xe_bb.o \
|
||||
xe_page_reclaim.o \
|
||||
xe_pat.o \
|
||||
xe_pci.o \
|
||||
xe_pci_error.o \
|
||||
xe_pci_rebar.o \
|
||||
xe_pcode.o \
|
||||
xe_pm.o \
|
||||
|
||||
@@ -9,5 +9,6 @@ obj-$(CONFIG_DRM_XE_KUNIT_TEST) += xe_test.o
|
||||
xe_test-y = xe_test_mod.o \
|
||||
xe_args_test.o \
|
||||
xe_pci_test.o \
|
||||
xe_rtp_tables_test.o \
|
||||
xe_rtp_test.o \
|
||||
xe_wa_test.o
|
||||
|
||||
@@ -311,40 +311,44 @@ const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_pci_id_gen_param);
|
||||
|
||||
static void fake_init_devid(struct xe_device *xe)
|
||||
static int fake_probe_info(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
struct xe_pci_fake_data *data,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
/* Nothing to do, just keep zero. */
|
||||
}
|
||||
probed_info->tile_count = 1 + desc->max_remote_tiles;
|
||||
|
||||
static int fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
|
||||
u32 *ver, u32 *revid)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct xe_pci_fake_data *data = test->priv;
|
||||
|
||||
if (type == GMDID_MEDIA) {
|
||||
*ver = data->media_verx100;
|
||||
*revid = xe_step_to_gmdid(data->step.media);
|
||||
if (!data || desc->pre_gmdid_graphics_ip) {
|
||||
probed_info->graphics_ip = desc->pre_gmdid_graphics_ip;
|
||||
probed_info->media_ip = desc->pre_gmdid_media_ip;
|
||||
} else {
|
||||
*ver = data->graphics_verx100;
|
||||
*revid = xe_step_to_gmdid(data->step.graphics);
|
||||
probed_info->graphics_ip = find_graphics_ip(data->graphics_verx100);
|
||||
|
||||
if (data->media_verx100) {
|
||||
probed_info->media_ip = find_media_ip(data->media_verx100);
|
||||
xe_assert(xe, probed_info->media_ip);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
xe_assert(xe, probed_info->graphics_ip);
|
||||
if (!probed_info->graphics_ip)
|
||||
return -ENODEV;
|
||||
|
||||
static void fake_xe_info_probe_tile_count(struct xe_device *xe)
|
||||
{
|
||||
/* Nothing to do, just use the statically defined value. */
|
||||
if (data)
|
||||
probed_info->step = data->step;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_pci_fake_device_init(struct xe_device *xe)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct xe_pci_fake_data *data = test->priv;
|
||||
struct xe_probed_info probed_info = {};
|
||||
const struct pci_device_id *ent = pciidlist;
|
||||
const struct xe_device_desc *desc;
|
||||
const struct xe_subplatform_desc *subplatform_desc;
|
||||
int err;
|
||||
|
||||
if (!data) {
|
||||
desc = (const void *)ent->driver_data;
|
||||
@@ -374,13 +378,12 @@ int xe_pci_fake_device_init(struct xe_device *xe)
|
||||
xe->sriov.__mode = data && data->sriov_mode ?
|
||||
data->sriov_mode : XE_SRIOV_MODE_NONE;
|
||||
|
||||
kunit_activate_static_stub(test, init_devid, fake_init_devid);
|
||||
kunit_activate_static_stub(test, read_gmdid, fake_read_gmdid);
|
||||
kunit_activate_static_stub(test, xe_info_probe_tile_count,
|
||||
fake_xe_info_probe_tile_count);
|
||||
err = fake_probe_info(xe, desc, data, &probed_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xe_info_init_early(xe, desc, subplatform_desc);
|
||||
xe_info_init(xe, desc);
|
||||
xe_info_init_early(xe, desc, subplatform_desc, &probed_info);
|
||||
xe_info_init(xe, desc, &probed_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
112
drivers/gpu/drm/xe/tests/xe_rtp_tables_test.c
Normal file
112
drivers/gpu/drm/xe/tests/xe_rtp_tables_test.c
Normal file
@@ -0,0 +1,112 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright © 2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include "xe_reg_whitelist.h"
|
||||
#include "xe_rtp_types.h"
|
||||
#include "xe_tuning.h"
|
||||
#include "xe_wa.h"
|
||||
|
||||
#define RTP_TABLE_PARAM(table) \
|
||||
static const void *table##_gen_params(struct kunit *test, \
|
||||
const void *prev, char *desc) \
|
||||
{ \
|
||||
typeof((table.entries)[0]) *__next = prev ? \
|
||||
((typeof(__next))prev) + 1 : (table.entries); \
|
||||
if (__next - table.entries < table.n_entries) { \
|
||||
scnprintf(desc, KUNIT_PARAM_DESC_SIZE, #table "/%s", __next->name); \
|
||||
return __next; \
|
||||
} \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
static void xe_rtp_table_gt_test(struct kunit *test)
|
||||
{
|
||||
const struct xe_rtp_entry_sr *entry = test->param_value;
|
||||
|
||||
for (int i = 0; i < entry->n_rules; i++) {
|
||||
KUNIT_EXPECT_TRUE(test,
|
||||
entry->rules[i].match_type != XE_RTP_MATCH_ENGINE_CLASS ||
|
||||
entry->flags & XE_RTP_ENTRY_FLAG_FOREACH_ENGINE);
|
||||
KUNIT_EXPECT_TRUE(test,
|
||||
entry->rules[i].match_type != XE_RTP_MATCH_NOT_ENGINE_CLASS ||
|
||||
entry->flags & XE_RTP_ENTRY_FLAG_FOREACH_ENGINE);
|
||||
}
|
||||
}
|
||||
|
||||
RTP_TABLE_PARAM(gt_was);
|
||||
RTP_TABLE_PARAM(gt_tunings);
|
||||
|
||||
static void xe_rtp_table_oob_test(struct kunit *test)
|
||||
{
|
||||
const struct xe_rtp_entry *entry = test->param_value;
|
||||
|
||||
for (int i = 0; i < entry->n_rules; i++) {
|
||||
u8 match_type = entry->rules[i].match_type;
|
||||
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_ENGINE_CLASS);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_NOT_ENGINE_CLASS);
|
||||
}
|
||||
}
|
||||
|
||||
RTP_TABLE_PARAM(oob_was);
|
||||
|
||||
static void xe_rtp_table_dev_oob_test(struct kunit *test)
|
||||
{
|
||||
const struct xe_rtp_entry *entry = test->param_value;
|
||||
|
||||
for (int i = 0; i < entry->n_rules; i++) {
|
||||
u8 match_type = entry->rules[i].match_type;
|
||||
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_ENGINE_CLASS);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_NOT_ENGINE_CLASS);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_VERSION);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_VERSION_RANGE);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_VERSION_ANY_GT);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_STEP);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_VERSION);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_VERSION_RANGE);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_VERSION_ANY_GT);
|
||||
KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_STEP);
|
||||
}
|
||||
}
|
||||
|
||||
RTP_TABLE_PARAM(device_oob_was);
|
||||
|
||||
static void xe_rtp_table_missing_upper_bound_test(struct kunit *test)
|
||||
{
|
||||
const struct xe_rtp_entry_sr *entry = test->param_value;
|
||||
|
||||
for (int i = 0; i < entry->n_rules; i++) {
|
||||
u8 match_type = entry->rules[i].match_type;
|
||||
|
||||
KUNIT_EXPECT_FALSE(test,
|
||||
match_type == XE_RTP_MATCH_GRAPHICS_VERSION_RANGE &&
|
||||
entry->rules[i].ver_end == XE_RTP_END_VERSION_UNDEFINED);
|
||||
KUNIT_EXPECT_FALSE(test,
|
||||
match_type == XE_RTP_MATCH_MEDIA_VERSION_RANGE &&
|
||||
entry->rules[i].ver_end == XE_RTP_END_VERSION_UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
RTP_TABLE_PARAM(register_whitelist);
|
||||
|
||||
static struct kunit_case xe_rtp_table_tests[] = {
|
||||
KUNIT_CASE_PARAM(xe_rtp_table_gt_test, gt_was_gen_params),
|
||||
KUNIT_CASE_PARAM(xe_rtp_table_gt_test, gt_tunings_gen_params),
|
||||
KUNIT_CASE_PARAM(xe_rtp_table_oob_test, oob_was_gen_params),
|
||||
KUNIT_CASE_PARAM(xe_rtp_table_dev_oob_test, device_oob_was_gen_params),
|
||||
KUNIT_CASE_PARAM(xe_rtp_table_missing_upper_bound_test,
|
||||
register_whitelist_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite xe_rtp_tables_test_suite = {
|
||||
.name = "xe_rtp_tables_test",
|
||||
.test_cases = xe_rtp_table_tests,
|
||||
};
|
||||
|
||||
kunit_test_suite(xe_rtp_tables_test_suite);
|
||||
@@ -280,6 +280,11 @@ static void xe_rtp_rules_tests(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, err, param->expected_err);
|
||||
}
|
||||
|
||||
static u32 bits_2_3_set(struct xe_gt *gt, struct xe_hw_engine *hwe)
|
||||
{
|
||||
return REG_BIT(2) | REG_BIT(3);
|
||||
}
|
||||
|
||||
static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = {
|
||||
{
|
||||
.name = "coalesce-same-reg",
|
||||
@@ -300,6 +305,29 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = {
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
.name = "coalesce-same-reg-literal-and-func",
|
||||
.expected_reg = REGULAR_REG1,
|
||||
.expected_set_bits = REG_BIT(0) | REG_BIT(1) | REG_BIT(2) | REG_BIT(3),
|
||||
.expected_clr_bits = REG_BIT(0) | REG_BIT(1) | REG_BIT(2) | REG_BIT(3),
|
||||
.expected_active = BIT(0) | BIT(1),
|
||||
.expected_count_sr_entries = 1,
|
||||
/* Different bits on the same register: create a single entry */
|
||||
.table = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("basic-1"),
|
||||
XE_RTP_RULES(FUNC(match_yes)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(REGULAR_REG1,
|
||||
REG_BIT(0) | REG_BIT(1),
|
||||
REG_BIT(0) | REG_BIT(1)))
|
||||
},
|
||||
{ XE_RTP_NAME("basic-2"),
|
||||
XE_RTP_RULES(FUNC(match_yes)),
|
||||
XE_RTP_ACTIONS(FIELD_SET_FUNC(REGULAR_REG1,
|
||||
REG_BIT(2) | REG_BIT(3),
|
||||
bits_2_3_set))
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
.name = "no-match-no-add",
|
||||
.expected_reg = REGULAR_REG1,
|
||||
@@ -417,6 +445,30 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = {
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
.name = "conflict-not-disjoint-literal-and-func",
|
||||
.expected_reg = REGULAR_REG1,
|
||||
.expected_set_bits = REG_BIT(1) | REG_BIT(2),
|
||||
.expected_clr_bits = REG_BIT(1) | REG_BIT(2),
|
||||
.expected_active = BIT(0) | BIT(1),
|
||||
.expected_count_sr_entries = 1,
|
||||
.expected_sr_errors = 1,
|
||||
.table = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("basic-1"),
|
||||
XE_RTP_RULES(FUNC(match_yes)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(REGULAR_REG1,
|
||||
REG_BIT(1) | REG_BIT(2),
|
||||
REG_BIT(1) | REG_BIT(2)))
|
||||
},
|
||||
/* drop: bits are not disjoint with previous entries */
|
||||
{ XE_RTP_NAME("basic-2"),
|
||||
XE_RTP_RULES(FUNC(match_yes)),
|
||||
XE_RTP_ACTIONS(FIELD_SET_FUNC(REGULAR_REG1,
|
||||
REG_BIT(2) | REG_BIT(3),
|
||||
bits_2_3_set))
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
.name = "conflict-reg-type",
|
||||
.expected_reg = REGULAR_REG1,
|
||||
|
||||
@@ -43,9 +43,6 @@ static int xe_wa_test_init(struct kunit *test)
|
||||
xe_gt_mmio_init(gt);
|
||||
}
|
||||
|
||||
if (!param->graphics_verx100)
|
||||
xe->info.step = param->step;
|
||||
|
||||
/* TODO: init hw engines for engine/LRC WAs */
|
||||
xe->drm.dev = dev;
|
||||
test->priv = xe;
|
||||
|
||||
@@ -117,7 +117,6 @@ static int info(struct seq_file *m, void *data)
|
||||
drm_printf(&p, "revid %d\n", xe->info.revid);
|
||||
drm_printf(&p, "tile_count %d\n", xe->info.tile_count);
|
||||
drm_printf(&p, "vm_max_level %d\n", xe->info.vm_max_level);
|
||||
drm_printf(&p, "force_execlist %s\n", str_yes_no(xe->info.force_execlist));
|
||||
drm_printf(&p, "has_flat_ccs %s\n", str_yes_no(xe->info.has_flat_ccs));
|
||||
drm_printf(&p, "has_usm %s\n", str_yes_no(xe->info.has_usm));
|
||||
drm_printf(&p, "skip_guc_pc %s\n", str_yes_no(xe->info.skip_guc_pc));
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "xe_psmi.h"
|
||||
#include "xe_pxp.h"
|
||||
#include "xe_query.h"
|
||||
#include "xe_ras.h"
|
||||
#include "xe_shrinker.h"
|
||||
#include "xe_soc_remapper.h"
|
||||
#include "xe_survivability_mode.h"
|
||||
@@ -738,9 +739,11 @@ static void vf_update_device_info(struct xe_device *xe)
|
||||
xe->info.probe_display = 0;
|
||||
xe->info.has_heci_cscfi = 0;
|
||||
xe->info.has_heci_gscfi = 0;
|
||||
xe->info.has_i2c = 0;
|
||||
xe->info.has_late_bind = 0;
|
||||
xe->info.skip_guc_pc = 1;
|
||||
xe->info.skip_pcode = 1;
|
||||
xe->info.has_drm_ras = false;
|
||||
}
|
||||
|
||||
static int xe_device_vram_alloc(struct xe_device *xe)
|
||||
@@ -949,6 +952,15 @@ int xe_device_probe(struct xe_device *xe)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wa_16029380221: The affected GT will always use non-coherent
|
||||
* access to page tables, so we must do uncached writes from the
|
||||
* CPU.
|
||||
*/
|
||||
for_each_gt(gt, xe, id)
|
||||
if (XE_GT_WA(gt, 16029380221))
|
||||
xe->info.has_cached_pt = false;
|
||||
|
||||
for_each_tile(tile, xe, id) {
|
||||
err = xe_ggtt_init_early(tile->mem.ggtt);
|
||||
if (err)
|
||||
@@ -989,6 +1001,16 @@ int xe_device_probe(struct xe_device *xe)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_soc_remapper_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_sysctrl_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xe_ras_init(xe);
|
||||
|
||||
/*
|
||||
* Now that GT is initialized (TTM in particular),
|
||||
* we can try to init display, and inherit the initial fb.
|
||||
@@ -1029,10 +1051,6 @@ int xe_device_probe(struct xe_device *xe)
|
||||
|
||||
xe_nvm_init(xe);
|
||||
|
||||
err = xe_soc_remapper_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_heci_gsc_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -1071,10 +1089,6 @@ int xe_device_probe(struct xe_device *xe)
|
||||
if (err)
|
||||
goto err_unregister_display;
|
||||
|
||||
err = xe_sysctrl_init(xe);
|
||||
if (err)
|
||||
goto err_unregister_display;
|
||||
|
||||
err = xe_device_sysfs_init(xe);
|
||||
if (err)
|
||||
goto err_unregister_display;
|
||||
|
||||
@@ -116,7 +116,7 @@ static inline struct xe_mmio *xe_root_tile_mmio(struct xe_device *xe)
|
||||
|
||||
static inline bool xe_device_uc_enabled(struct xe_device *xe)
|
||||
{
|
||||
return !xe->info.force_execlist;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define for_each_tile(tile__, xe__, id__) \
|
||||
@@ -181,6 +181,21 @@ static inline bool xe_device_has_mert(const struct xe_device *xe)
|
||||
return xe->info.has_mert;
|
||||
}
|
||||
|
||||
static inline bool xe_device_is_in_reset(struct xe_device *xe)
|
||||
{
|
||||
return atomic_read(&xe->in_reset);
|
||||
}
|
||||
|
||||
static inline void xe_device_set_in_reset(struct xe_device *xe)
|
||||
{
|
||||
atomic_set(&xe->in_reset, 1);
|
||||
}
|
||||
|
||||
static inline void xe_device_clear_in_reset(struct xe_device *xe)
|
||||
{
|
||||
atomic_set(&xe->in_reset, 0);
|
||||
}
|
||||
|
||||
u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size);
|
||||
|
||||
void xe_device_snapshot_print(struct xe_device *xe, struct drm_printer *p);
|
||||
|
||||
@@ -144,8 +144,6 @@ struct xe_device {
|
||||
* Keep all flags below alphabetically sorted
|
||||
*/
|
||||
|
||||
/** @info.force_execlist: Forced execlist submission */
|
||||
u8 force_execlist:1;
|
||||
/** @info.has_access_counter: Device supports access counter */
|
||||
u8 has_access_counter:1;
|
||||
/** @info.has_asid: Has address space ID */
|
||||
@@ -156,6 +154,8 @@ struct xe_device {
|
||||
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_drm_ras: Device supports drm_ras (Reliability, Availability, Serviceability) */
|
||||
u8 has_drm_ras:1;
|
||||
/** @info.has_fan_control: Device supports fan control */
|
||||
u8 has_fan_control:1;
|
||||
/** @info.has_flat_ccs: Whether flat CCS metadata is used */
|
||||
@@ -483,6 +483,9 @@ struct xe_device {
|
||||
/** @needs_flr_on_fini: requests function-reset on fini */
|
||||
bool needs_flr_on_fini;
|
||||
|
||||
/** @in_reset: Indicates if device is in reset */
|
||||
atomic_t in_reset;
|
||||
|
||||
/** @wedged: Struct to control Wedged States and mode */
|
||||
struct {
|
||||
/** @wedged.flag: Xe device faced a critical error and is now blocked. */
|
||||
@@ -495,6 +498,9 @@ struct xe_device {
|
||||
bool inconsistent_reset;
|
||||
} wedged;
|
||||
|
||||
/** @devres_group: devres group */
|
||||
void *devres_group;
|
||||
|
||||
/** @bo_device: Struct to control async free of BOs */
|
||||
struct xe_bo_dev {
|
||||
/** @bo_device.async_free: Free worker */
|
||||
|
||||
@@ -193,7 +193,7 @@ static void bo_meminfo(struct xe_bo *bo,
|
||||
if (!dma_resv_test_signaled(bo->ttm.base.resv,
|
||||
DMA_RESV_USAGE_BOOKKEEP))
|
||||
stats[mem_type].active += sz;
|
||||
else if (mem_type == XE_PL_SYSTEM)
|
||||
else if (mem_type == XE_PL_SYSTEM || xe_bo_madv_is_dontneed(bo))
|
||||
stats[mem_type].purgeable += sz;
|
||||
}
|
||||
}
|
||||
@@ -273,8 +273,7 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file)
|
||||
&stats[mem_type],
|
||||
DRM_GEM_OBJECT_ACTIVE |
|
||||
DRM_GEM_OBJECT_RESIDENT |
|
||||
(mem_type != XE_PL_SYSTEM ? 0 :
|
||||
DRM_GEM_OBJECT_PURGEABLE),
|
||||
DRM_GEM_OBJECT_PURGEABLE,
|
||||
xe_mem_type_to_name[mem_type]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,27 +11,46 @@
|
||||
|
||||
#include "xe_device_types.h"
|
||||
#include "xe_drm_ras.h"
|
||||
#include "xe_ras.h"
|
||||
|
||||
static const char * const error_components[] = DRM_XE_RAS_ERROR_COMPONENT_NAMES;
|
||||
static const char * const error_severity[] = DRM_XE_RAS_ERROR_SEVERITY_NAMES;
|
||||
|
||||
static int hw_query_error_counter(struct xe_drm_ras_counter *info,
|
||||
u32 error_id, const char **name, u32 *val)
|
||||
static int query_error_counter(struct xe_device *xe,
|
||||
enum drm_xe_ras_error_severity severity,
|
||||
u32 error_id, const char **name, u32 *val)
|
||||
{
|
||||
struct xe_drm_ras *ras = &xe->ras;
|
||||
struct xe_drm_ras_counter *info = ras->info[severity];
|
||||
|
||||
if (!info || !info[error_id].name)
|
||||
return -ENOENT;
|
||||
|
||||
*name = info[error_id].name;
|
||||
|
||||
/* Fetch counter from system controller if supported */
|
||||
if (xe->info.has_sysctrl)
|
||||
return xe_ras_get_counter(xe, severity, error_id, val);
|
||||
|
||||
*val = atomic_read(&info[error_id].counter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hw_clear_error_counter(struct xe_drm_ras_counter *info, u32 error_id)
|
||||
static int clear_error_counter(struct xe_device *xe,
|
||||
enum drm_xe_ras_error_severity severity,
|
||||
u32 error_id)
|
||||
{
|
||||
struct xe_drm_ras *ras = &xe->ras;
|
||||
struct xe_drm_ras_counter *info = ras->info[severity];
|
||||
|
||||
if (!info || !info[error_id].name)
|
||||
return -ENOENT;
|
||||
|
||||
/* Clear counter from system controller if supported */
|
||||
if (xe->info.has_sysctrl)
|
||||
return xe_ras_clear_counter(xe, severity, error_id);
|
||||
|
||||
atomic_set(&info[error_id].counter, 0);
|
||||
|
||||
return 0;
|
||||
@@ -41,38 +60,30 @@ static int query_uncorrectable_error_counter(struct drm_ras_node *ep, u32 error_
|
||||
const char **name, u32 *val)
|
||||
{
|
||||
struct xe_device *xe = ep->priv;
|
||||
struct xe_drm_ras *ras = &xe->ras;
|
||||
struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_UNCORRECTABLE];
|
||||
|
||||
return hw_query_error_counter(info, error_id, name, val);
|
||||
return query_error_counter(xe, DRM_XE_RAS_ERR_SEV_UNCORRECTABLE, error_id, name, val);
|
||||
}
|
||||
|
||||
static int clear_uncorrectable_error_counter(struct drm_ras_node *node, u32 error_id)
|
||||
{
|
||||
struct xe_device *xe = node->priv;
|
||||
struct xe_drm_ras *ras = &xe->ras;
|
||||
struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_UNCORRECTABLE];
|
||||
|
||||
return hw_clear_error_counter(info, error_id);
|
||||
return clear_error_counter(xe, DRM_XE_RAS_ERR_SEV_UNCORRECTABLE, error_id);
|
||||
}
|
||||
|
||||
static int query_correctable_error_counter(struct drm_ras_node *ep, u32 error_id,
|
||||
const char **name, u32 *val)
|
||||
{
|
||||
struct xe_device *xe = ep->priv;
|
||||
struct xe_drm_ras *ras = &xe->ras;
|
||||
struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE];
|
||||
|
||||
return hw_query_error_counter(info, error_id, name, val);
|
||||
return query_error_counter(xe, DRM_XE_RAS_ERR_SEV_CORRECTABLE, error_id, name, val);
|
||||
}
|
||||
|
||||
static int clear_correctable_error_counter(struct drm_ras_node *node, u32 error_id)
|
||||
{
|
||||
struct xe_device *xe = node->priv;
|
||||
struct xe_drm_ras *ras = &xe->ras;
|
||||
struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE];
|
||||
|
||||
return hw_clear_error_counter(info, error_id);
|
||||
return clear_error_counter(xe, DRM_XE_RAS_ERR_SEV_CORRECTABLE, error_id);
|
||||
}
|
||||
|
||||
static struct xe_drm_ras_counter *allocate_and_copy_counters(struct xe_device *xe)
|
||||
|
||||
@@ -985,9 +985,10 @@ int xe_eu_stall_stream_open(struct drm_device *dev, u64 data, struct drm_file *f
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (xe_observation_paranoid && !perfmon_capable()) {
|
||||
ret = xe_observation_paranoid_check();
|
||||
if (ret) {
|
||||
drm_dbg(&xe->drm, "Insufficient privileges for EU stall monitoring\n");
|
||||
return -EACCES;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize and set default values */
|
||||
|
||||
@@ -318,8 +318,6 @@ struct xe_exec_queue_ops {
|
||||
void (*resume)(struct xe_exec_queue *q);
|
||||
/** @reset_status: check exec queue reset status */
|
||||
bool (*reset_status)(struct xe_exec_queue *q);
|
||||
/** @active: check exec queue is active */
|
||||
bool (*active)(struct xe_exec_queue *q);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -458,12 +458,6 @@ static bool execlist_exec_queue_reset_status(struct xe_exec_queue *q)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool execlist_exec_queue_active(struct xe_exec_queue *q)
|
||||
{
|
||||
/* NIY */
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct xe_exec_queue_ops execlist_exec_queue_ops = {
|
||||
.init = execlist_exec_queue_init,
|
||||
.kill = execlist_exec_queue_kill,
|
||||
@@ -476,7 +470,6 @@ static const struct xe_exec_queue_ops execlist_exec_queue_ops = {
|
||||
.suspend_wait = execlist_exec_queue_suspend_wait,
|
||||
.resume = execlist_exec_queue_resume,
|
||||
.reset_status = execlist_exec_queue_reset_status,
|
||||
.active = execlist_exec_queue_active,
|
||||
};
|
||||
|
||||
int xe_execlist_init(struct xe_gt *gt)
|
||||
|
||||
@@ -111,14 +111,14 @@ struct xe_ggtt_pt_ops {
|
||||
struct xe_ggtt {
|
||||
/** @tile: Back pointer to tile where this GGTT belongs */
|
||||
struct xe_tile *tile;
|
||||
/** @start: Start offset of GGTT */
|
||||
/** @start: Start offset of GGTT */
|
||||
u64 start;
|
||||
/** @size: Total usable size of this GGTT */
|
||||
u64 size;
|
||||
|
||||
/**
|
||||
* @flags: Flags for this GGTT
|
||||
* @flags: Flags for this GGTT.
|
||||
* Acceptable flags:
|
||||
*
|
||||
* - %XE_GGTT_FLAGS_64K - if PTE size is 64K. Otherwise, regular is 4K.
|
||||
* - %XE_GGTT_FLAGS_ONLINE - is GGTT online, protected by ggtt->lock
|
||||
* after init
|
||||
@@ -129,7 +129,7 @@ struct xe_ggtt {
|
||||
/** @lock: Mutex lock to protect GGTT data */
|
||||
struct mutex lock;
|
||||
/**
|
||||
* @gsm: The iomem pointer to the actual location of the translation
|
||||
* @gsm: The iomem pointer to the actual location of the translation
|
||||
* table located in the GSM for easy PTE manipulation
|
||||
*/
|
||||
u64 __iomem *gsm;
|
||||
|
||||
@@ -917,6 +917,9 @@ static void gt_reset_worker(struct work_struct *w)
|
||||
if (xe_device_wedged(gt_to_xe(gt)))
|
||||
goto err_pm_put;
|
||||
|
||||
if (xe_device_is_in_reset(gt_to_xe(gt)))
|
||||
goto err_pm_put;
|
||||
|
||||
/* We only support GT resets with GuC submission */
|
||||
if (!xe_device_uc_enabled(gt_to_xe(gt)))
|
||||
goto err_pm_put;
|
||||
@@ -977,18 +980,21 @@ static void gt_reset_worker(struct work_struct *w)
|
||||
|
||||
void xe_gt_reset_async(struct xe_gt *gt)
|
||||
{
|
||||
xe_gt_info(gt, "trying reset from %ps\n", __builtin_return_address(0));
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
|
||||
if (xe_device_is_in_reset(xe))
|
||||
return;
|
||||
|
||||
/* Don't do a reset while one is already in flight */
|
||||
if (!xe_fault_inject_gt_reset() && xe_uc_reset_prepare(>->uc))
|
||||
return;
|
||||
|
||||
xe_gt_info(gt, "reset queued\n");
|
||||
xe_gt_info(gt, "reset queued from %ps\n", __builtin_return_address(0));
|
||||
|
||||
/* Pair with put in gt_reset_worker() if work is enqueued */
|
||||
xe_pm_runtime_get_noresume(gt_to_xe(gt));
|
||||
xe_pm_runtime_get_noresume(xe);
|
||||
if (!queue_work(gt->ordered_wq, >->reset.worker))
|
||||
xe_pm_runtime_put(gt_to_xe(gt));
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
void xe_gt_suspend_prepare(struct xe_gt *gt)
|
||||
|
||||
@@ -404,8 +404,7 @@ static unsigned int dss_per_group(struct xe_gt *gt)
|
||||
* Some older platforms don't have tables or don't have complete tables.
|
||||
* Newer platforms should always have the required info.
|
||||
*/
|
||||
if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 2000 &&
|
||||
!gt_to_xe(gt)->info.force_execlist)
|
||||
if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 2000)
|
||||
xe_gt_err(gt, "Slice/Subslice counts missing from hwconfig table; using typical fallback values\n");
|
||||
|
||||
if (gt_to_xe(gt)->info.platform == XE_PVC)
|
||||
@@ -507,7 +506,7 @@ void xe_gt_mcr_init_early(struct xe_gt *gt)
|
||||
spin_lock_init(>->mcr_lock);
|
||||
|
||||
if (gt->info.type == XE_GT_TYPE_MEDIA) {
|
||||
drm_WARN_ON(&xe->drm, MEDIA_VER(xe) < 13);
|
||||
xe_gt_WARN_ON(gt, MEDIA_VER(xe) < 13);
|
||||
|
||||
if (MEDIA_VER(xe) >= 30) {
|
||||
gt->steering[OADDRM].ranges = xe2lpm_gpmxmt_steering_table;
|
||||
@@ -662,9 +661,9 @@ bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt,
|
||||
|
||||
for (int type = 0; type < IMPLICIT_STEERING; type++) {
|
||||
if (reg_in_steering_type_ranges(gt, reg, type)) {
|
||||
drm_WARN(>_to_xe(gt)->drm, !gt->steering[type].initialized,
|
||||
"Uninitialized usage of MCR register %s/%#x\n",
|
||||
xe_steering_types[type].name, reg.addr);
|
||||
xe_gt_WARN(gt, !gt->steering[type].initialized,
|
||||
"Uninitialized usage of MCR register %s/%#x\n",
|
||||
xe_steering_types[type].name, reg.addr);
|
||||
|
||||
*group = gt->steering[type].group_target;
|
||||
*instance = gt->steering[type].instance_target;
|
||||
@@ -679,9 +678,9 @@ bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt,
|
||||
* Not found in a steering table and not a register with implicit
|
||||
* steering. Just steer to 0/0 as a guess and raise a warning.
|
||||
*/
|
||||
drm_WARN(>_to_xe(gt)->drm, true,
|
||||
"Did not find MCR register %#x in any MCR steering table\n",
|
||||
reg.addr);
|
||||
xe_gt_WARN(gt, true,
|
||||
"Did not find MCR register %#x in any MCR steering table\n",
|
||||
reg.addr);
|
||||
*group = 0;
|
||||
*instance = 0;
|
||||
|
||||
@@ -710,7 +709,7 @@ static void mcr_lock(struct xe_gt *gt) __acquires(>->mcr_lock)
|
||||
ret = xe_mmio_wait32(>->mmio, STEER_SEMAPHORE, 0x1, 0x1, 10, NULL,
|
||||
true);
|
||||
|
||||
drm_WARN_ON_ONCE(&xe->drm, ret == -ETIMEDOUT);
|
||||
xe_gt_WARN_ON_ONCE(gt, ret == -ETIMEDOUT);
|
||||
}
|
||||
|
||||
static void mcr_unlock(struct xe_gt *gt) __releases(>->mcr_lock)
|
||||
|
||||
@@ -1532,7 +1532,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
|
||||
* If devcoredump not captured and GuC capture for the job is not ready
|
||||
* do manual capture first and decide later if we need to use it
|
||||
*/
|
||||
if (!exec_queue_killed(q) && !xe->devcoredump.captured &&
|
||||
if (!xe_device_is_in_reset(xe) && !exec_queue_killed(q) && !xe->devcoredump.captured &&
|
||||
!xe_guc_capture_get_matching_and_lock(q)) {
|
||||
/* take force wake before engine register manual capture */
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
|
||||
@@ -1554,8 +1554,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
|
||||
set_exec_queue_banned(q);
|
||||
|
||||
/* Kick job / queue off hardware */
|
||||
if (!wedged && (exec_queue_enabled(primary) ||
|
||||
exec_queue_pending_disable(primary))) {
|
||||
if (!xe_device_is_in_reset(xe) && !wedged &&
|
||||
(exec_queue_enabled(primary) || exec_queue_pending_disable(primary))) {
|
||||
int ret;
|
||||
|
||||
if (exec_queue_reset(primary))
|
||||
@@ -1623,7 +1623,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
|
||||
|
||||
trace_xe_sched_job_timedout(job);
|
||||
|
||||
if (!exec_queue_killed(q))
|
||||
/* Do not access device if in reset */
|
||||
if (!xe_device_is_in_reset(xe) && !exec_queue_killed(q))
|
||||
xe_devcoredump(q, job,
|
||||
"Timedout job - seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx",
|
||||
xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
|
||||
@@ -2244,14 +2245,6 @@ static bool guc_exec_queue_reset_status(struct xe_exec_queue *q)
|
||||
return exec_queue_reset(q) || exec_queue_killed_or_banned_or_wedged(q);
|
||||
}
|
||||
|
||||
static bool guc_exec_queue_active(struct xe_exec_queue *q)
|
||||
{
|
||||
struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q);
|
||||
|
||||
return exec_queue_enabled(primary) &&
|
||||
!exec_queue_pending_disable(primary);
|
||||
}
|
||||
|
||||
/*
|
||||
* All of these functions are an abstraction layer which other parts of Xe can
|
||||
* use to trap into the GuC backend. All of these functions, aside from init,
|
||||
@@ -2271,7 +2264,6 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = {
|
||||
.suspend_wait = guc_exec_queue_suspend_wait,
|
||||
.resume = guc_exec_queue_resume,
|
||||
.reset_status = guc_exec_queue_reset_status,
|
||||
.active = guc_exec_queue_active,
|
||||
};
|
||||
|
||||
static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
|
||||
|
||||
@@ -208,9 +208,6 @@ static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
|
||||
|
||||
lockdep_assert_held(&tlb_inval->seqno_lock);
|
||||
|
||||
if (guc_to_xe(guc)->info.force_execlist)
|
||||
return -ECANCELED;
|
||||
|
||||
return send_tlb_inval_ppgtt(guc, seqno, start, end, asid,
|
||||
XE_GUC_TLB_INVAL_PAGE_SELECTIVE, prl_sa);
|
||||
}
|
||||
@@ -228,9 +225,6 @@ static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno,
|
||||
|
||||
lockdep_assert_held(&tlb_inval->seqno_lock);
|
||||
|
||||
if (xe->info.force_execlist)
|
||||
return -ECANCELED;
|
||||
|
||||
vm = xe_device_asid_to_vm(xe, asid);
|
||||
if (IS_ERR(vm))
|
||||
return PTR_ERR(vm);
|
||||
|
||||
@@ -337,39 +337,41 @@ static bool xe_rtp_cfeg_wmtp_disabled(const struct xe_device *xe,
|
||||
return xe_mmio_read32(&hwe->gt->mmio, XEHP_FUSE4) & CFEG_WMTP_DISABLE;
|
||||
}
|
||||
|
||||
static u32 blit_cctl_val(struct xe_gt *gt, struct xe_hw_engine *hwe)
|
||||
{
|
||||
return REG_FIELD_PREP(BLIT_CCTL_DST_MOCS_MASK, gt->mocs.uc_index) |
|
||||
REG_FIELD_PREP(BLIT_CCTL_SRC_MOCS_MASK, gt->mocs.uc_index);
|
||||
}
|
||||
|
||||
static const struct xe_rtp_table_sr lrc_setup = XE_RTP_TABLE_SR(
|
||||
/*
|
||||
* Some blitter commands do not have a field for MOCS, those
|
||||
* commands will use MOCS index pointed by BLIT_CCTL.
|
||||
* BLIT_CCTL registers are needed to be programmed to un-cached.
|
||||
*/
|
||||
{ XE_RTP_NAME("BLIT_CCTL_default_MOCS"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1274),
|
||||
ENGINE_CLASS(COPY)),
|
||||
XE_RTP_ACTIONS(FIELD_SET_FUNC(BLIT_CCTL(0),
|
||||
BLIT_CCTL_DST_MOCS_MASK |
|
||||
BLIT_CCTL_SRC_MOCS_MASK,
|
||||
blit_cctl_val,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
/* Disable WMTP if HW doesn't support it */
|
||||
{ XE_RTP_NAME("DISABLE_WMTP_ON_UNSUPPORTED_HW"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_cfeg_wmtp_disabled)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(CS_CHICKEN1(0),
|
||||
PREEMPT_GPGPU_LEVEL_MASK,
|
||||
PREEMPT_GPGPU_THREAD_GROUP_LEVEL)),
|
||||
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE)
|
||||
},
|
||||
);
|
||||
|
||||
static void
|
||||
hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe)
|
||||
{
|
||||
struct xe_gt *gt = hwe->gt;
|
||||
const u8 mocs_write_idx = gt->mocs.uc_index;
|
||||
const u8 mocs_read_idx = gt->mocs.uc_index;
|
||||
u32 blit_cctl_val = REG_FIELD_PREP(BLIT_CCTL_DST_MOCS_MASK, mocs_write_idx) |
|
||||
REG_FIELD_PREP(BLIT_CCTL_SRC_MOCS_MASK, mocs_read_idx);
|
||||
struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe);
|
||||
const struct xe_rtp_table_sr lrc_setup = XE_RTP_TABLE_SR(
|
||||
/*
|
||||
* Some blitter commands do not have a field for MOCS, those
|
||||
* commands will use MOCS index pointed by BLIT_CCTL.
|
||||
* BLIT_CCTL registers are needed to be programmed to un-cached.
|
||||
*/
|
||||
{ XE_RTP_NAME("BLIT_CCTL_default_MOCS"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1274),
|
||||
ENGINE_CLASS(COPY)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(BLIT_CCTL(0),
|
||||
BLIT_CCTL_DST_MOCS_MASK |
|
||||
BLIT_CCTL_SRC_MOCS_MASK,
|
||||
blit_cctl_val,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
/* Disable WMTP if HW doesn't support it */
|
||||
{ XE_RTP_NAME("DISABLE_WMTP_ON_UNSUPPORTED_HW"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_cfeg_wmtp_disabled)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(CS_CHICKEN1(0),
|
||||
PREEMPT_GPGPU_LEVEL_MASK,
|
||||
PREEMPT_GPGPU_THREAD_GROUP_LEVEL)),
|
||||
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE)
|
||||
},
|
||||
);
|
||||
|
||||
xe_rtp_process_to_sr(&ctx, &lrc_setup, &hwe->reg_lrc, true);
|
||||
}
|
||||
@@ -385,86 +387,92 @@ void xe_hw_engine_setup_reg_lrc(struct xe_hw_engine *hwe)
|
||||
xe_tuning_process_lrc(hwe);
|
||||
}
|
||||
|
||||
/*
|
||||
* RING_CMD_CCTL specifies the default MOCS entry that will be
|
||||
* used by the command streamer when executing commands that
|
||||
* don't have a way to explicitly specify a MOCS setting.
|
||||
* The default should usually reference whichever MOCS entry
|
||||
* corresponds to uncached behavior, although use of a WB cached
|
||||
* entry is recommended by the spec in certain circumstances on
|
||||
* specific platforms.
|
||||
* Bspec: 72161
|
||||
*/
|
||||
static u32 ring_cmd_cctl_val(struct xe_gt *gt, struct xe_hw_engine *hwe)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
u8 mocs_read_idx = gt->mocs.uc_index;
|
||||
|
||||
if (hwe->class == XE_ENGINE_CLASS_COMPUTE && IS_DGFX(xe) &&
|
||||
(GRAPHICS_VER(xe) >= 20 || xe->info.platform == XE_PVC))
|
||||
mocs_read_idx = gt->mocs.wb_index;
|
||||
|
||||
return REG_FIELD_PREP(CMD_CCTL_WRITE_OVERRIDE_MASK, gt->mocs.uc_index) |
|
||||
REG_FIELD_PREP(CMD_CCTL_READ_OVERRIDE_MASK, mocs_read_idx);
|
||||
}
|
||||
|
||||
static const struct xe_rtp_table_sr engine_sr = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("RING_CMD_CCTL_default_MOCS"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_always)),
|
||||
XE_RTP_ACTIONS(FIELD_SET_FUNC(RING_CMD_CCTL(0),
|
||||
CMD_CCTL_WRITE_OVERRIDE_MASK |
|
||||
CMD_CCTL_READ_OVERRIDE_MASK,
|
||||
ring_cmd_cctl_val,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("Disable HW status page updates for interrupts"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_always)),
|
||||
XE_RTP_ACTIONS(SET(RING_HWSTAM(0), ~0x0,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("Disable engine 'legacy' mode"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_always)),
|
||||
XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_DISABLE_LEGACY_MODE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
/*
|
||||
* To allow the GSC engine to go idle on MTL we need to enable
|
||||
* idle messaging and set the hysteresis value (we use 0xA=5us
|
||||
* as recommended in spec). On platforms after MTL this is
|
||||
* enabled by default.
|
||||
*/
|
||||
{ XE_RTP_NAME("MTL GSCCS IDLE MSG enable"),
|
||||
XE_RTP_RULES(MEDIA_VERSION(1300), ENGINE_CLASS(OTHER)),
|
||||
XE_RTP_ACTIONS(CLR(RING_PSMI_CTL(0),
|
||||
IDLE_MSG_DISABLE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)),
|
||||
FIELD_SET(RING_PWRCTX_MAXCNT(0),
|
||||
IDLE_WAIT_TIME,
|
||||
0xA,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
/* Enable Priority Mem Read */
|
||||
{ XE_RTP_NAME("Priority_Mem_Read"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
|
||||
XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0), CS_PRIORITY_MEM_READ,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("Enable CCS Engine(s)"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1255, XE_RTP_END_VERSION_UNDEFINED),
|
||||
FUNC(xe_rtp_match_first_render_or_compute)),
|
||||
XE_RTP_ACTIONS(SET(RCU_MODE, RCU_MODE_CCS_ENABLE))
|
||||
},
|
||||
/* Use Fixed slice CCS mode */
|
||||
{ XE_RTP_NAME("RCU_MODE_FIXED_SLICE_CCS_MODE"),
|
||||
XE_RTP_RULES(FUNC(xe_hw_engine_match_fixed_cslice_mode)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(RCU_MODE, RCU_MODE_FIXED_SLICE_CCS_MODE,
|
||||
RCU_MODE_FIXED_SLICE_CCS_MODE))
|
||||
},
|
||||
{ XE_RTP_NAME("Enable MSI-X interrupt support"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_has_msix)),
|
||||
XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_MSIX_INTERRUPT_ENABLE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
);
|
||||
|
||||
static void
|
||||
hw_engine_setup_default_state(struct xe_hw_engine *hwe)
|
||||
{
|
||||
struct xe_gt *gt = hwe->gt;
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
/*
|
||||
* RING_CMD_CCTL specifies the default MOCS entry that will be
|
||||
* used by the command streamer when executing commands that
|
||||
* don't have a way to explicitly specify a MOCS setting.
|
||||
* The default should usually reference whichever MOCS entry
|
||||
* corresponds to uncached behavior, although use of a WB cached
|
||||
* entry is recommended by the spec in certain circumstances on
|
||||
* specific platforms.
|
||||
* Bspec: 72161
|
||||
*/
|
||||
const u8 mocs_write_idx = gt->mocs.uc_index;
|
||||
const u8 mocs_read_idx = hwe->class == XE_ENGINE_CLASS_COMPUTE && IS_DGFX(xe) &&
|
||||
(GRAPHICS_VER(xe) >= 20 || xe->info.platform == XE_PVC) ?
|
||||
gt->mocs.wb_index : gt->mocs.uc_index;
|
||||
u32 ring_cmd_cctl_val = REG_FIELD_PREP(CMD_CCTL_WRITE_OVERRIDE_MASK, mocs_write_idx) |
|
||||
REG_FIELD_PREP(CMD_CCTL_READ_OVERRIDE_MASK, mocs_read_idx);
|
||||
struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe);
|
||||
const struct xe_rtp_table_sr engine_sr = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("RING_CMD_CCTL_default_MOCS"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_always)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(RING_CMD_CCTL(0),
|
||||
CMD_CCTL_WRITE_OVERRIDE_MASK |
|
||||
CMD_CCTL_READ_OVERRIDE_MASK,
|
||||
ring_cmd_cctl_val,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("Disable HW status page updates for interrupts"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_always)),
|
||||
XE_RTP_ACTIONS(SET(RING_HWSTAM(0), ~0x0,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("Disable engine 'legacy' mode"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_always)),
|
||||
XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_DISABLE_LEGACY_MODE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
/*
|
||||
* To allow the GSC engine to go idle on MTL we need to enable
|
||||
* idle messaging and set the hysteresis value (we use 0xA=5us
|
||||
* as recommended in spec). On platforms after MTL this is
|
||||
* enabled by default.
|
||||
*/
|
||||
{ XE_RTP_NAME("MTL GSCCS IDLE MSG enable"),
|
||||
XE_RTP_RULES(MEDIA_VERSION(1300), ENGINE_CLASS(OTHER)),
|
||||
XE_RTP_ACTIONS(CLR(RING_PSMI_CTL(0),
|
||||
IDLE_MSG_DISABLE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)),
|
||||
FIELD_SET(RING_PWRCTX_MAXCNT(0),
|
||||
IDLE_WAIT_TIME,
|
||||
0xA,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
/* Enable Priority Mem Read */
|
||||
{ XE_RTP_NAME("Priority_Mem_Read"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
|
||||
XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0), CS_PRIORITY_MEM_READ,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("Enable CCS Engine(s)"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1255, XE_RTP_END_VERSION_UNDEFINED),
|
||||
FUNC(xe_rtp_match_first_render_or_compute)),
|
||||
XE_RTP_ACTIONS(SET(RCU_MODE, RCU_MODE_CCS_ENABLE))
|
||||
},
|
||||
/* Use Fixed slice CCS mode */
|
||||
{ XE_RTP_NAME("RCU_MODE_FIXED_SLICE_CCS_MODE"),
|
||||
XE_RTP_RULES(FUNC(xe_hw_engine_match_fixed_cslice_mode)),
|
||||
XE_RTP_ACTIONS(FIELD_SET(RCU_MODE, RCU_MODE_FIXED_SLICE_CCS_MODE,
|
||||
RCU_MODE_FIXED_SLICE_CCS_MODE))
|
||||
},
|
||||
{ XE_RTP_NAME("Enable MSI-X interrupt support"),
|
||||
XE_RTP_RULES(FUNC(xe_rtp_match_has_msix)),
|
||||
XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_MSIX_INTERRUPT_ENABLE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
);
|
||||
|
||||
xe_rtp_process_to_sr(&ctx, &engine_sr, &hwe->reg_sr, false);
|
||||
}
|
||||
|
||||
@@ -437,6 +437,16 @@ static void hw_error_source_handler(struct xe_tile *tile, const enum hardware_er
|
||||
if (!IS_DGFX(xe))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Hardware errors are reported through System Controller on the platforms that
|
||||
* support it, and never routed as direct IRQ to SGUnit. So we should never be
|
||||
* here for those platforms.
|
||||
*/
|
||||
if (xe->info.has_sysctrl) {
|
||||
drm_err_ratelimited(&xe->drm, HW_ERR "Invalid error routing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&xe->irq.lock, flags);
|
||||
err_src = xe_mmio_read32(&tile->mmio, DEV_ERR_STAT_REG(hw_err));
|
||||
if (!err_src) {
|
||||
@@ -516,14 +526,6 @@ void xe_hw_error_irq_handler(struct xe_tile *tile, const u32 master_ctl)
|
||||
}
|
||||
}
|
||||
|
||||
static int hw_error_info_init(struct xe_device *xe)
|
||||
{
|
||||
if (xe->info.platform != XE_PVC)
|
||||
return 0;
|
||||
|
||||
return xe_drm_ras_init(xe);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process hardware errors during boot
|
||||
*/
|
||||
@@ -550,16 +552,11 @@ static void process_hw_errors(struct xe_device *xe)
|
||||
void xe_hw_error_init(struct xe_device *xe)
|
||||
{
|
||||
struct xe_tile *tile = xe_device_get_root_tile(xe);
|
||||
int ret;
|
||||
|
||||
if (!IS_DGFX(xe) || IS_SRIOV_VF(xe))
|
||||
return;
|
||||
|
||||
INIT_WORK(&tile->csc_hw_error_work, csc_hw_error_work);
|
||||
|
||||
ret = hw_error_info_init(xe);
|
||||
if (ret)
|
||||
drm_err(&xe->drm, "Failed to initialize XE DRM RAS (%pe)\n", ERR_PTR(ret));
|
||||
|
||||
process_hw_errors(xe);
|
||||
}
|
||||
|
||||
@@ -334,9 +334,6 @@ int xe_i2c_probe(struct xe_device *xe)
|
||||
if (!xe->info.has_i2c)
|
||||
return 0;
|
||||
|
||||
if (IS_SRIOV_VF(xe))
|
||||
return 0;
|
||||
|
||||
xe_i2c_read_endpoint(xe_root_tile_mmio(xe), &ep);
|
||||
if (ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
|
||||
return 0;
|
||||
|
||||
@@ -2618,13 +2618,19 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot)
|
||||
kfree(snapshot);
|
||||
}
|
||||
|
||||
static bool engine_valid_for_utilization(struct xe_gt *gt, struct xe_hw_engine *hwe)
|
||||
{
|
||||
/* The USM-reserved copy engine runs kernel migrate contexts queried here */
|
||||
return hwe && (!xe_hw_engine_is_reserved(hwe) || xe_gt_is_usm_hwe(gt, hwe));
|
||||
}
|
||||
|
||||
static struct xe_hw_engine *engine_id_to_hwe(struct xe_gt *gt, u32 engine_id)
|
||||
{
|
||||
u16 class = REG_FIELD_GET(ENGINE_CLASS_ID, engine_id);
|
||||
u16 instance = REG_FIELD_GET(ENGINE_INSTANCE_ID, engine_id);
|
||||
struct xe_hw_engine *hwe = xe_gt_hw_engine(gt, class, instance, false);
|
||||
|
||||
if (xe_gt_WARN_ONCE(gt, !hwe || xe_hw_engine_is_reserved(hwe),
|
||||
if (xe_gt_WARN_ONCE(gt, !engine_valid_for_utilization(gt, hwe),
|
||||
"Unexpected engine class:instance %d:%d for utilization\n",
|
||||
class, instance))
|
||||
return NULL;
|
||||
|
||||
@@ -11,27 +11,18 @@
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
#include "regs/xe_bars.h"
|
||||
#include "xe_device.h"
|
||||
#include "xe_gt_sriov_vf.h"
|
||||
#include "xe_printk.h"
|
||||
#include "xe_sriov.h"
|
||||
#include "xe_tile_printk.h"
|
||||
#include "xe_trace.h"
|
||||
#include "xe_wa.h"
|
||||
|
||||
#include "generated/xe_device_wa_oob.h"
|
||||
|
||||
static void tiles_fini(void *arg)
|
||||
{
|
||||
struct xe_device *xe = arg;
|
||||
struct xe_tile *tile;
|
||||
int id;
|
||||
|
||||
for_each_remote_tile(tile, xe, id)
|
||||
tile->mmio.regs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* On multi-tile devices, partition the BAR space for MMIO on each tile,
|
||||
* possibly accounting for register override on the number of tiles available.
|
||||
@@ -56,50 +47,71 @@ static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
|
||||
struct xe_tile *tile;
|
||||
u8 id;
|
||||
|
||||
for_each_remote_tile(tile, xe, id)
|
||||
xe_mmio_init(&tile->mmio, tile, xe->mmio.regs + id * tile_mmio_size, SZ_4M);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_mmio_probe_tiles() - Initialize all tiles' MMIO
|
||||
* @xe: the &xe_device
|
||||
*
|
||||
* Initialize the remaining tiles' MMIO instances.
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int xe_mmio_probe_tiles(struct xe_device *xe)
|
||||
{
|
||||
size_t tile_mmio_size = SZ_16M;
|
||||
|
||||
/*
|
||||
* Nothing to be done as tile 0 has already been setup earlier with the
|
||||
* entire BAR mapped - see xe_mmio_probe_early()
|
||||
*/
|
||||
if (xe->info.tile_count == 1)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
for_each_remote_tile(tile, xe, id)
|
||||
xe_mmio_init(&tile->mmio, tile, xe->mmio.regs + id * tile_mmio_size, SZ_4M);
|
||||
}
|
||||
|
||||
int xe_mmio_probe_tiles(struct xe_device *xe)
|
||||
{
|
||||
size_t tile_mmio_size = SZ_16M;
|
||||
if (xe->mmio.size < xe->info.tile_count * tile_mmio_size) {
|
||||
xe_err(xe, "GTTMMADR_BAR is too small for %d tiles: %zu\n",
|
||||
xe->info.tile_count, xe->mmio.size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mmio_multi_tile_setup(xe, tile_mmio_size);
|
||||
|
||||
return devm_add_action_or_reset(xe->drm.dev, tiles_fini, xe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmio_fini(void *arg)
|
||||
{
|
||||
struct xe_device *xe = arg;
|
||||
struct xe_tile *root_tile = xe_device_get_root_tile(xe);
|
||||
|
||||
pci_iounmap(to_pci_dev(xe->drm.dev), xe->mmio.regs);
|
||||
xe->mmio.regs = NULL;
|
||||
root_tile->mmio.regs = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_mmio_probe_early() - Probe and initialize device's MMIO
|
||||
* @xe: the &xe_device
|
||||
*
|
||||
* Map the entire GTTMMADR_BAR and initialize the first tile's MMIO instance.
|
||||
*
|
||||
* The first 16MB of the GTTMMADR_BAR always belongs to the root tile, and
|
||||
* includes: registers (0-4MB), reserved space (4MB-8MB) and GGTT (8MB-16MB).
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int xe_mmio_probe_early(struct xe_device *xe)
|
||||
{
|
||||
struct xe_tile *root_tile = xe_device_get_root_tile(xe);
|
||||
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
|
||||
|
||||
/*
|
||||
* Map the entire BAR.
|
||||
* The first 16MB of the BAR, belong to the root tile, and include:
|
||||
* registers (0-4MB), reserved space (4MB-8MB) and GGTT (8MB-16MB).
|
||||
*/
|
||||
xe->mmio.size = pci_resource_len(pdev, GTTMMADR_BAR);
|
||||
xe->mmio.regs = pci_iomap(pdev, GTTMMADR_BAR, 0);
|
||||
xe->mmio.regs = pcim_iomap(pdev, GTTMMADR_BAR, 0);
|
||||
if (!xe->mmio.regs) {
|
||||
drm_err(&xe->drm, "failed to map registers\n");
|
||||
xe_err(xe, "Failed to map GTTMMADR_BAR\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
xe->mmio.size = pci_resource_len(pdev, GTTMMADR_BAR);
|
||||
if (xe->mmio.size < SZ_16M) {
|
||||
xe_err(xe, "GTTMMADR_BAR is too small: %zu\n", xe->mmio.size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -128,6 +140,11 @@ void xe_mmio_init(struct xe_mmio *mmio, struct xe_tile *tile, void __iomem *ptr,
|
||||
mmio->tile = tile;
|
||||
}
|
||||
|
||||
static bool mmio_available(struct xe_mmio *mmio)
|
||||
{
|
||||
return !xe_tile_WARN_ON_ONCE(mmio->tile, !mmio->tile->xe->mmio.regs);
|
||||
}
|
||||
|
||||
static void mmio_flush_pending_writes(struct xe_mmio *mmio)
|
||||
{
|
||||
#define DUMMY_REG_OFFSET 0x130030
|
||||
@@ -146,6 +163,9 @@ u8 xe_mmio_read8(struct xe_mmio *mmio, struct xe_reg reg)
|
||||
u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr);
|
||||
u8 val;
|
||||
|
||||
if (!mmio_available(mmio))
|
||||
return 0;
|
||||
|
||||
mmio_flush_pending_writes(mmio);
|
||||
|
||||
val = readb(mmio->regs + addr);
|
||||
@@ -158,6 +178,9 @@ void xe_mmio_write8(struct xe_mmio *mmio, struct xe_reg reg, u8 val)
|
||||
{
|
||||
u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr);
|
||||
|
||||
if (!mmio_available(mmio))
|
||||
return;
|
||||
|
||||
trace_xe_reg_rw(mmio, true, addr, val, sizeof(val));
|
||||
|
||||
writeb(val, mmio->regs + addr);
|
||||
@@ -168,6 +191,9 @@ u16 xe_mmio_read16(struct xe_mmio *mmio, struct xe_reg reg)
|
||||
u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr);
|
||||
u16 val;
|
||||
|
||||
if (!mmio_available(mmio))
|
||||
return 0;
|
||||
|
||||
mmio_flush_pending_writes(mmio);
|
||||
|
||||
val = readw(mmio->regs + addr);
|
||||
@@ -180,6 +206,9 @@ void xe_mmio_write32(struct xe_mmio *mmio, struct xe_reg reg, u32 val)
|
||||
{
|
||||
u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr);
|
||||
|
||||
if (!mmio_available(mmio))
|
||||
return;
|
||||
|
||||
trace_xe_reg_rw(mmio, true, addr, val, sizeof(val));
|
||||
|
||||
if (!reg.vf && IS_SRIOV_VF(mmio->tile->xe))
|
||||
@@ -194,6 +223,9 @@ u32 xe_mmio_read32(struct xe_mmio *mmio, struct xe_reg reg)
|
||||
u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr);
|
||||
u32 val;
|
||||
|
||||
if (!mmio_available(mmio))
|
||||
return 0;
|
||||
|
||||
mmio_flush_pending_writes(mmio);
|
||||
|
||||
if (!reg.vf && IS_SRIOV_VF(mmio->tile->xe))
|
||||
@@ -282,8 +314,8 @@ u64 xe_mmio_read64_2x32(struct xe_mmio *mmio, struct xe_reg reg)
|
||||
oldudw = udw;
|
||||
}
|
||||
|
||||
drm_WARN(&mmio->tile->xe->drm, retries == 0,
|
||||
"64-bit read of %#x did not stabilize\n", reg.addr);
|
||||
xe_tile_WARN(mmio->tile, retries == 0,
|
||||
"MMIO: 64-bit read of %#x did not stabilize\n", reg.addr);
|
||||
|
||||
return (u64)udw << 32 | ldw;
|
||||
}
|
||||
|
||||
@@ -36,9 +36,6 @@ module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600)
|
||||
MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 "
|
||||
"[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]");
|
||||
|
||||
module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444);
|
||||
MODULE_PARM_DESC(force_execlist, "Force Execlist submission");
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
|
||||
module_param_named(probe_display, xe_modparam.probe_display, bool, 0444);
|
||||
MODULE_PARM_DESC(probe_display, "Probe display HW, otherwise it's left untouched "
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
/* Module modprobe variables */
|
||||
struct xe_modparam {
|
||||
bool force_execlist;
|
||||
bool probe_display;
|
||||
int force_vram_bar_size;
|
||||
int guc_log_level;
|
||||
|
||||
@@ -1702,11 +1702,12 @@ static int xe_oa_release(struct inode *inode, struct file *file)
|
||||
static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct xe_oa_stream *stream = file->private_data;
|
||||
int ret = xe_observation_paranoid_check();
|
||||
struct xe_bo *bo = stream->oa_buffer.bo;
|
||||
|
||||
if (xe_observation_paranoid && !perfmon_capable()) {
|
||||
if (ret) {
|
||||
drm_dbg(&stream->oa->xe->drm, "Insufficient privilege to map OA buffer\n");
|
||||
return -EACCES;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */
|
||||
@@ -2080,10 +2081,12 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
|
||||
privileged_op = true;
|
||||
}
|
||||
|
||||
if (privileged_op && xe_observation_paranoid && !perfmon_capable()) {
|
||||
drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
|
||||
ret = -EACCES;
|
||||
goto err_exec_q;
|
||||
if (privileged_op) {
|
||||
ret = xe_observation_paranoid_check();
|
||||
if (ret) {
|
||||
drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n");
|
||||
goto err_exec_q;
|
||||
}
|
||||
}
|
||||
|
||||
if (!param.exec_q && !param.sample) {
|
||||
@@ -2365,9 +2368,10 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (xe_observation_paranoid && !perfmon_capable()) {
|
||||
err = xe_observation_paranoid_check();
|
||||
if (err) {
|
||||
drm_dbg(&oa->xe->drm, "Insufficient privileges to add xe OA config\n");
|
||||
return -EACCES;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = copy_from_user(¶m, u64_to_user_ptr(data), sizeof(param));
|
||||
@@ -2467,9 +2471,10 @@ int xe_oa_remove_config_ioctl(struct drm_device *dev, u64 data, struct drm_file
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (xe_observation_paranoid && !perfmon_capable()) {
|
||||
ret = xe_observation_paranoid_check();
|
||||
if (ret) {
|
||||
drm_dbg(&oa->xe->drm, "Insufficient privileges to remove xe OA config\n");
|
||||
return -EACCES;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = get_user(arg, ptr);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
#include <uapi/drm/xe_drm.h>
|
||||
@@ -12,9 +13,28 @@
|
||||
#include "xe_oa.h"
|
||||
#include "xe_observation.h"
|
||||
|
||||
u32 xe_observation_paranoid = true;
|
||||
static u32 xe_observation_paranoid = true;
|
||||
static struct ctl_table_header *sysctl_header;
|
||||
|
||||
/**
|
||||
* xe_observation_paranoid_check - Gate access to xe observation streams.
|
||||
*
|
||||
* When the xe-specific observation_paranoid sysctl is enabled (the
|
||||
* default), defer to perf_allow_cpu() so that access is governed by the
|
||||
* same policy as system-wide perf CPU events: kernel.perf_event_paranoid
|
||||
* plus the security_perf_event_open() LSM hook. When the sysctl has been
|
||||
* cleared by a privileged user, observation is open to all callers.
|
||||
*
|
||||
* Return: 0 if access is permitted, a negative errno otherwise.
|
||||
*/
|
||||
int xe_observation_paranoid_check(void)
|
||||
{
|
||||
if (!xe_observation_paranoid)
|
||||
return 0;
|
||||
|
||||
return perf_allow_cpu();
|
||||
}
|
||||
|
||||
static int xe_oa_ioctl(struct drm_device *dev, struct drm_xe_observation_param *arg,
|
||||
struct drm_file *file)
|
||||
{
|
||||
@@ -83,11 +103,13 @@ static const struct ctl_table observation_ctl_table[] = {
|
||||
};
|
||||
|
||||
/**
|
||||
* xe_observation_sysctl_register - Register xe_observation_paranoid sysctl
|
||||
* xe_observation_sysctl_register - Register the observation_paranoid sysctl
|
||||
*
|
||||
* Normally only superuser/root can access observation stream
|
||||
* data. However, superuser can set xe_observation_paranoid sysctl to 0 to
|
||||
* allow non-privileged users to also access observation data.
|
||||
* When dev.xe.observation_paranoid is set (the default), access to
|
||||
* observation streams follows the system-wide perf_allow_cpu() policy:
|
||||
* kernel.perf_event_paranoid plus the security_perf_event_open() LSM
|
||||
* hook. A privileged user can clear the sysctl to bypass that gate and
|
||||
* allow unprivileged access to observation data.
|
||||
*
|
||||
* Return: always returns 0
|
||||
*/
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
struct drm_device;
|
||||
struct drm_file;
|
||||
|
||||
extern u32 xe_observation_paranoid;
|
||||
|
||||
int xe_observation_paranoid_check(void);
|
||||
int xe_observation_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
|
||||
int xe_observation_sysctl_register(void);
|
||||
void xe_observation_sysctl_unregister(void);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "xe_guc.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_module.h"
|
||||
#include "xe_pci_error.h"
|
||||
#include "xe_pci_rebar.h"
|
||||
#include "xe_pci_sriov.h"
|
||||
#include "xe_pci_types.h"
|
||||
@@ -355,6 +356,7 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
|
||||
PLATFORM(PVC),
|
||||
.dma_mask_size = 52,
|
||||
.has_display = false,
|
||||
.has_drm_ras = true,
|
||||
.has_gsc_nvm = 1,
|
||||
.has_heci_gscfi = 1,
|
||||
.max_gt_per_tile = 1,
|
||||
@@ -457,6 +459,7 @@ static const struct xe_device_desc cri_desc = {
|
||||
PLATFORM(CRESCENTISLAND),
|
||||
.dma_mask_size = 52,
|
||||
.has_display = false,
|
||||
.has_drm_ras = true,
|
||||
.has_flat_ccs = false,
|
||||
.has_gsc_nvm = 1,
|
||||
.has_i2c = true,
|
||||
@@ -599,8 +602,6 @@ static int read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver, u
|
||||
struct xe_reg gmdid_reg = GMD_ID;
|
||||
u32 val;
|
||||
|
||||
KUNIT_STATIC_STUB_REDIRECT(read_gmdid, xe, type, ver, revid);
|
||||
|
||||
if (IS_SRIOV_VF(xe)) {
|
||||
/*
|
||||
* To get the value of the GMDID register, VFs must obtain it
|
||||
@@ -726,14 +727,30 @@ static int handle_gmdid(struct xe_device *xe,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_devid(struct xe_device *xe)
|
||||
struct xe_probed_info {
|
||||
u16 devid;
|
||||
u8 revid;
|
||||
u8 tile_count;
|
||||
struct xe_step_info step;
|
||||
const struct xe_ip *graphics_ip;
|
||||
const struct xe_ip *media_ip;
|
||||
};
|
||||
|
||||
/*
|
||||
* Probe from the hardware the info required by xe_info_init_early().
|
||||
*/
|
||||
static int xe_probe_info_early(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
|
||||
|
||||
KUNIT_STATIC_STUB_REDIRECT(init_devid, xe);
|
||||
probed_info->devid = pdev->device;
|
||||
probed_info->revid = pdev->revision;
|
||||
|
||||
xe->info.devid = pdev->device;
|
||||
xe->info.revid = pdev->revision;
|
||||
xe_step_platform_get(desc->platform, probed_info->revid, &probed_info->step);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -742,17 +759,20 @@ static void init_devid(struct xe_device *xe)
|
||||
*/
|
||||
static int xe_info_init_early(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
const struct xe_subplatform_desc *subplatform_desc)
|
||||
const struct xe_subplatform_desc *subplatform_desc,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
int err;
|
||||
|
||||
xe->info.devid = probed_info->devid;
|
||||
xe->info.revid = probed_info->revid;
|
||||
xe->info.step.platform = probed_info->step.platform;
|
||||
|
||||
xe->info.platform_name = desc->platform_name;
|
||||
xe->info.platform = desc->platform;
|
||||
xe->info.subplatform = subplatform_desc ?
|
||||
subplatform_desc->subplatform : XE_SUBPLATFORM_NONE;
|
||||
|
||||
init_devid(xe);
|
||||
|
||||
xe->info.dma_mask_size = desc->dma_mask_size;
|
||||
xe->info.va_bits = desc->va_bits;
|
||||
xe->info.vm_max_level = desc->vm_max_level;
|
||||
@@ -760,6 +780,7 @@ static int xe_info_init_early(struct xe_device *xe,
|
||||
|
||||
xe->info.is_dgfx = desc->is_dgfx;
|
||||
xe->info.has_cached_pt = desc->has_cached_pt;
|
||||
xe->info.has_drm_ras = desc->has_drm_ras;
|
||||
xe->info.has_fan_control = desc->has_fan_control;
|
||||
/* runtime fusing may force flat_ccs to disabled later */
|
||||
xe->info.has_flat_ccs = desc->has_flat_ccs;
|
||||
@@ -789,14 +810,10 @@ static int xe_info_init_early(struct xe_device *xe,
|
||||
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
|
||||
xe_modparam.probe_display &&
|
||||
desc->has_display;
|
||||
xe->info.force_execlist = xe_modparam.force_execlist;
|
||||
|
||||
xe_assert(xe, desc->max_gt_per_tile > 0);
|
||||
xe_assert(xe, desc->max_gt_per_tile <= XE_MAX_GT_PER_TILE);
|
||||
xe->info.max_gt_per_tile = desc->max_gt_per_tile;
|
||||
xe->info.tile_count = 1 + desc->max_remote_tiles;
|
||||
|
||||
xe_step_platform_get(xe);
|
||||
|
||||
err = xe_tile_init_early(xe_device_get_root_tile(xe), xe, 0);
|
||||
if (err)
|
||||
@@ -805,22 +822,21 @@ static int xe_info_init_early(struct xe_device *xe,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Possibly override number of tile based on configuration register.
|
||||
*/
|
||||
static void xe_info_probe_tile_count(struct xe_device *xe)
|
||||
static void xe_probe_tile_count(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
struct xe_mmio *mmio;
|
||||
u8 tile_count;
|
||||
u32 mtcfg;
|
||||
|
||||
KUNIT_STATIC_STUB_REDIRECT(xe_info_probe_tile_count, xe);
|
||||
probed_info->tile_count = 1 + desc->max_remote_tiles;
|
||||
|
||||
/*
|
||||
* Probe for tile count only for platforms that support multiple
|
||||
* tiles.
|
||||
*/
|
||||
if (xe->info.tile_count == 1)
|
||||
if (probed_info->tile_count == 1)
|
||||
return;
|
||||
|
||||
mmio = xe_root_tile_mmio(xe);
|
||||
@@ -833,10 +849,10 @@ static void xe_info_probe_tile_count(struct xe_device *xe)
|
||||
mtcfg = xe_mmio_read32(mmio, XEHP_MTCFG_ADDR);
|
||||
tile_count = REG_FIELD_GET(TILE_COUNT, mtcfg) + 1;
|
||||
|
||||
if (tile_count < xe->info.tile_count) {
|
||||
if (tile_count < probed_info->tile_count) {
|
||||
drm_info(&xe->drm, "tile_count: %d, reduced_tile_count %d\n",
|
||||
xe->info.tile_count, tile_count);
|
||||
xe->info.tile_count = tile_count;
|
||||
probed_info->tile_count, tile_count);
|
||||
probed_info->tile_count = tile_count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,25 +925,10 @@ static struct xe_gt *alloc_media_gt(struct xe_tile *tile,
|
||||
return gt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize device info content that does require knowledge about
|
||||
* graphics / media IP version.
|
||||
* Make sure that GT / tile structures allocated by the driver match the data
|
||||
* present in device info.
|
||||
*/
|
||||
static int xe_info_init(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc)
|
||||
static int xe_probe_ips(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
u32 graphics_gmdid_revid = 0, media_gmdid_revid = 0;
|
||||
const struct xe_ip *graphics_ip;
|
||||
const struct xe_ip *media_ip;
|
||||
const struct xe_graphics_desc *graphics_desc;
|
||||
const struct xe_media_desc *media_desc;
|
||||
struct xe_tile *tile;
|
||||
struct xe_gt *gt;
|
||||
int ret;
|
||||
u8 id;
|
||||
|
||||
/*
|
||||
* If this platform supports GMD_ID, we'll detect the proper IP
|
||||
* descriptor to use from hardware registers.
|
||||
@@ -936,17 +937,21 @@ static int xe_info_init(struct xe_device *xe,
|
||||
* versions are simply derived from that.
|
||||
*/
|
||||
if (desc->pre_gmdid_graphics_ip) {
|
||||
graphics_ip = desc->pre_gmdid_graphics_ip;
|
||||
media_ip = desc->pre_gmdid_media_ip;
|
||||
xe_step_pre_gmdid_get(xe);
|
||||
probed_info->graphics_ip = desc->pre_gmdid_graphics_ip;
|
||||
probed_info->media_ip = desc->pre_gmdid_media_ip;
|
||||
xe_step_pre_gmdid_get(xe, &probed_info->step);
|
||||
} else {
|
||||
xe_assert(xe, !desc->pre_gmdid_media_ip);
|
||||
ret = handle_gmdid(xe, &graphics_ip, &media_ip,
|
||||
&graphics_gmdid_revid, &media_gmdid_revid);
|
||||
if (ret)
|
||||
return ret;
|
||||
int err;
|
||||
u32 graphics_revid, media_revid;
|
||||
|
||||
xe_step_gmdid_get(xe, graphics_gmdid_revid, media_gmdid_revid);
|
||||
xe_assert(xe, !desc->pre_gmdid_media_ip);
|
||||
|
||||
err = handle_gmdid(xe, &probed_info->graphics_ip, &probed_info->media_ip,
|
||||
&graphics_revid, &media_revid);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xe_step_gmdid_get(xe, graphics_revid, media_revid, &probed_info->step);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -954,9 +959,56 @@ static int xe_info_init(struct xe_device *xe,
|
||||
* error and we should abort driver load. Failing to detect media
|
||||
* IP is non-fatal; we'll just proceed without enabling media support.
|
||||
*/
|
||||
if (!graphics_ip)
|
||||
if (!probed_info->graphics_ip)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe from the hardware the info required by xe_info_init().
|
||||
*/
|
||||
static int xe_probe_info(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
int err;
|
||||
|
||||
xe_probe_tile_count(xe, desc, probed_info);
|
||||
|
||||
err = xe_probe_ips(xe, desc, probed_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize device info content that does require knowledge about
|
||||
* graphics / media IP version.
|
||||
* Make sure that GT / tile structures allocated by the driver match the data
|
||||
* present in device info.
|
||||
*/
|
||||
static int xe_info_init(struct xe_device *xe,
|
||||
const struct xe_device_desc *desc,
|
||||
struct xe_probed_info *probed_info)
|
||||
{
|
||||
const struct xe_ip *graphics_ip;
|
||||
const struct xe_ip *media_ip;
|
||||
const struct xe_graphics_desc *graphics_desc;
|
||||
const struct xe_media_desc *media_desc;
|
||||
struct xe_tile *tile;
|
||||
struct xe_gt *gt;
|
||||
u8 id;
|
||||
|
||||
graphics_ip = probed_info->graphics_ip;
|
||||
media_ip = probed_info->media_ip;
|
||||
|
||||
xe->info.tile_count = probed_info->tile_count;
|
||||
xe->info.step.basedie = probed_info->step.basedie;
|
||||
xe->info.step.graphics = probed_info->step.graphics;
|
||||
xe->info.step.media = probed_info->step.media;
|
||||
|
||||
xe->info.graphics_verx100 = graphics_ip->verx100;
|
||||
xe->info.graphics_name = graphics_ip->name;
|
||||
graphics_desc = graphics_ip->desc;
|
||||
@@ -988,8 +1040,6 @@ static int xe_info_init(struct xe_device *xe,
|
||||
xe->info.has_soc_remapper_telem = 0;
|
||||
}
|
||||
|
||||
xe_info_probe_tile_count(xe);
|
||||
|
||||
for_each_remote_tile(tile, xe, id) {
|
||||
int err;
|
||||
|
||||
@@ -1072,9 +1122,11 @@ static void xe_pci_remove(struct pci_dev *pdev)
|
||||
*/
|
||||
static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct xe_probed_info probed_info = {};
|
||||
const struct xe_device_desc *desc = (const void *)ent->driver_data;
|
||||
const struct xe_subplatform_desc *subplatform_desc;
|
||||
struct xe_device *xe;
|
||||
void *group;
|
||||
int err;
|
||||
|
||||
subplatform_desc = find_subplatform(desc, pdev->device);
|
||||
@@ -1102,6 +1154,11 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
if (xe_display_driver_probe_defer(pdev))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/* Group all devres so xe_pci_error_slot_reset() can release them as a unit. */
|
||||
group = devres_open_group(&pdev->dev, NULL, GFP_KERNEL);
|
||||
if (!group)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -1110,13 +1167,19 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
if (IS_ERR(xe))
|
||||
return PTR_ERR(xe);
|
||||
|
||||
xe->devres_group = group;
|
||||
|
||||
pci_set_drvdata(pdev, &xe->drm);
|
||||
|
||||
xe_pm_assert_unbounded_bridge(xe);
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
err = xe_info_init_early(xe, desc, subplatform_desc);
|
||||
err = xe_probe_info_early(xe, desc, &probed_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_info_init_early(xe, desc, subplatform_desc, &probed_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1135,7 +1198,11 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_info_init(xe, desc);
|
||||
err = xe_probe_info(xe, desc, &probed_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_info_init(xe, desc, &probed_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -1348,6 +1415,7 @@ static struct pci_driver xe_pci_driver = {
|
||||
.remove = xe_pci_remove,
|
||||
.shutdown = xe_pci_shutdown,
|
||||
.sriov_configure = xe_pci_sriov_configure,
|
||||
.err_handler = &xe_pci_error_handlers,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.driver.pm = &xe_pm_ops,
|
||||
#endif
|
||||
|
||||
122
drivers/gpu/drm/xe/xe_pci_error.c
Normal file
122
drivers/gpu/drm/xe/xe_pci_error.c
Normal file
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright © 2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "xe_device.h"
|
||||
#include "xe_gt.h"
|
||||
#include "xe_pci.h"
|
||||
#include "xe_pm.h"
|
||||
#include "xe_printk.h"
|
||||
#include "xe_survivability_mode.h"
|
||||
|
||||
static void prepare_device_for_reset(struct pci_dev *pdev)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(pdev);
|
||||
struct xe_gt *gt;
|
||||
u8 id;
|
||||
|
||||
/*
|
||||
* Wedge the device to prevent userspace access but do not send the uevent.
|
||||
* xe_device_wedged_fini() releases runtime pm if wedged flag is set, so acquire a runtime
|
||||
* pm reference to avoid underflow.
|
||||
*/
|
||||
if (!atomic_xchg(&xe->wedged.flag, 1))
|
||||
xe_pm_runtime_get_noresume(xe);
|
||||
|
||||
xe_device_set_in_reset(xe);
|
||||
|
||||
for_each_gt(gt, xe, id)
|
||||
xe_gt_declare_wedged(gt);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static pci_ers_result_t xe_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(pdev);
|
||||
|
||||
xe_info(xe, "PCI error: detected state = %d\n", state);
|
||||
|
||||
if (state == pci_channel_io_perm_failure)
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
|
||||
/* If the device is already wedged or in survivability mode, do not attempt recovery */
|
||||
if (xe_survivability_mode_is_boot_enabled(xe) || xe_device_wedged(xe))
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
|
||||
switch (state) {
|
||||
case pci_channel_io_normal:
|
||||
return PCI_ERS_RESULT_CAN_RECOVER;
|
||||
case pci_channel_io_frozen:
|
||||
prepare_device_for_reset(pdev);
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
default:
|
||||
xe_info(xe, "PCI error: unknown state %d\n", state);
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
}
|
||||
}
|
||||
|
||||
static pci_ers_result_t xe_pci_error_mmio_enabled(struct pci_dev *pdev)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(pdev);
|
||||
|
||||
xe_info(xe, "PCI error: MMIO enabled\n");
|
||||
|
||||
/* TODO: Query system controller for the type of error and take appropriate action */
|
||||
return PCI_ERS_RESULT_RECOVERED;
|
||||
}
|
||||
|
||||
static pci_ers_result_t xe_pci_error_slot_reset(struct pci_dev *pdev)
|
||||
{
|
||||
const struct pci_device_id *ent = pci_match_id(pdev->driver->id_table, pdev);
|
||||
struct xe_device *xe = pdev_to_xe_device(pdev);
|
||||
|
||||
xe_info(xe, "PCI error: slot reset\n");
|
||||
|
||||
pci_restore_state(pdev);
|
||||
|
||||
if (pci_enable_device(pdev)) {
|
||||
xe_err(xe, "Cannot re-enable PCI device after reset\n");
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secondary Bus Reset causes all VRAM state to be lost along with
|
||||
* hardware state. As an initial step, re-probe the device to
|
||||
* re-initialize the driver and hardware.
|
||||
* TODO: optimize by re-initializing only the hardware state and re-creating
|
||||
* kernel BOs.
|
||||
*/
|
||||
xe_device_clear_in_reset(xe);
|
||||
pdev->driver->remove(pdev);
|
||||
devres_release_group(&pdev->dev, xe->devres_group);
|
||||
|
||||
if (pdev->driver->probe(pdev, ent))
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
|
||||
xe = pdev_to_xe_device(pdev);
|
||||
|
||||
/* Wedge the device to prevent I/O operations till the resume callback */
|
||||
atomic_set(&xe->wedged.flag, 1);
|
||||
|
||||
return PCI_ERS_RESULT_RECOVERED;
|
||||
}
|
||||
|
||||
static void xe_pci_error_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(pdev);
|
||||
|
||||
xe_info(xe, "PCI error: resume\n");
|
||||
|
||||
atomic_set(&xe->wedged.flag, 0);
|
||||
}
|
||||
|
||||
const struct pci_error_handlers xe_pci_error_handlers = {
|
||||
.error_detected = xe_pci_error_detected,
|
||||
.mmio_enabled = xe_pci_error_mmio_enabled,
|
||||
.slot_reset = xe_pci_error_slot_reset,
|
||||
.resume = xe_pci_error_resume,
|
||||
};
|
||||
13
drivers/gpu/drm/xe/xe_pci_error.h
Normal file
13
drivers/gpu/drm/xe/xe_pci_error.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _XE_PCI_ERROR_H_
|
||||
#define _XE_PCI_ERROR_H_
|
||||
|
||||
struct pci_error_handlers;
|
||||
|
||||
extern const struct pci_error_handlers xe_pci_error_handlers;
|
||||
|
||||
#endif
|
||||
@@ -40,6 +40,7 @@ struct xe_device_desc {
|
||||
|
||||
u8 has_cached_pt:1;
|
||||
u8 has_display:1;
|
||||
u8 has_drm_ras:1;
|
||||
u8 has_fan_control:1;
|
||||
u8 has_flat_ccs:1;
|
||||
u8 has_gsc_nvm:1;
|
||||
|
||||
@@ -323,15 +323,17 @@ int xe_pcode_ready(struct xe_device *xe, bool locked)
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_pcode_init: initialize components of PCODE
|
||||
* xe_pcode_init_early() - Initialize components of PCODE
|
||||
* @tile: tile instance
|
||||
*
|
||||
* This function initializes the xe_pcode component.
|
||||
* To be called once only during probe.
|
||||
*
|
||||
* Return: 0 on success or a negative error code on failure.
|
||||
*/
|
||||
void xe_pcode_init(struct xe_tile *tile)
|
||||
int xe_pcode_init_early(struct xe_tile *tile)
|
||||
{
|
||||
drmm_mutex_init(&tile_to_xe(tile)->drm, &tile->pcode.lock);
|
||||
return drmm_mutex_init(&tile_to_xe(tile)->drm, &tile->pcode.lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ struct drm_device;
|
||||
struct xe_device;
|
||||
struct xe_tile;
|
||||
|
||||
void xe_pcode_init(struct xe_tile *tile);
|
||||
int xe_pcode_init_early(struct xe_tile *tile);
|
||||
int xe_pcode_probe_early(struct xe_device *xe);
|
||||
int xe_pcode_ready(struct xe_device *xe, bool locked);
|
||||
int xe_pcode_init_min_freq_table(struct xe_tile *tile, u32 min_gt_freq,
|
||||
|
||||
@@ -1026,12 +1026,22 @@ xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *t
|
||||
u64 *ptr = data;
|
||||
u32 i;
|
||||
|
||||
/*
|
||||
* @qword_ofs is the absolute entry offset within the page table, while
|
||||
* @ptes is indexed relative to @update->ofs (its first entry). The GPU
|
||||
* path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized
|
||||
* chunks, calling this with an advancing @qword_ofs but a fresh @data
|
||||
* pointer per chunk, so translate back into a @ptes index rather than
|
||||
* assuming the chunk starts at ptes[0].
|
||||
*/
|
||||
for (i = 0; i < num_qwords; i++) {
|
||||
u32 idx = qword_ofs - update->ofs + i;
|
||||
|
||||
if (map)
|
||||
xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
|
||||
sizeof(u64), u64, ptes[i].pte);
|
||||
sizeof(u64), u64, ptes[idx].pte);
|
||||
else
|
||||
ptr[i] = ptes[i].pte;
|
||||
ptr[i] = ptes[idx].pte;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2070,6 +2080,9 @@ static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile,
|
||||
* automatically when the context is re-enabled by the rebind worker,
|
||||
* or in fault mode it was invalidated on PTE zapping.
|
||||
*
|
||||
* If rebind, we have to invalidate TLB on context based TLB invalidation
|
||||
* LR vms, as they cannot be relied on context re-enable.
|
||||
*
|
||||
* If !rebind, and scratch enabled VMs, there is a chance the scratch
|
||||
* PTE is already cached in the TLB so it needs to be invalidated.
|
||||
* On !LR VMs this is done in the ring ops preceding a batch, but on
|
||||
@@ -2079,6 +2092,9 @@ static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile,
|
||||
if ((!pt_op->rebind && xe_vm_has_scratch(vm) &&
|
||||
xe_vm_in_lr_mode(vm)))
|
||||
pt_update_ops->needs_invalidation = true;
|
||||
else if (pt_op->rebind && xe_vm_in_preempt_fence_mode(vm) &&
|
||||
vm->xe->info.has_ctx_tlb_inval)
|
||||
pt_update_ops->needs_invalidation = true;
|
||||
else if (pt_op->rebind && !xe_vm_in_lr_mode(vm))
|
||||
/* We bump also if batch_invalidate_tlb is true */
|
||||
vm->tlb_flush_seqno++;
|
||||
|
||||
@@ -59,6 +59,7 @@ bool xe_pxp_is_enabled(const struct xe_pxp *pxp)
|
||||
static bool pxp_prerequisites_done(const struct xe_pxp *pxp)
|
||||
{
|
||||
struct xe_gt *gt = pxp->gt;
|
||||
bool huc_ok;
|
||||
bool ready;
|
||||
|
||||
CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
@@ -73,9 +74,14 @@ static bool pxp_prerequisites_done(const struct xe_pxp *pxp)
|
||||
*/
|
||||
XE_WARN_ON(!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL));
|
||||
|
||||
/* PXP requires both HuC authentication via GSC and GSC proxy initialized */
|
||||
ready = xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GSC) &&
|
||||
xe_gsc_proxy_init_done(>->uc.gsc);
|
||||
/*
|
||||
* PXP requires GSC proxy to be initialized. On platforms where the HuC
|
||||
* is loaded by the kernel driver (i.e., pre media 35) PXP also requires
|
||||
* the HuC to be authenticated by GSC.
|
||||
*/
|
||||
huc_ok = MEDIA_VER(gt_to_xe(gt)) >= 35 ||
|
||||
xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GSC);
|
||||
ready = huc_ok && xe_gsc_proxy_init_done(>->uc.gsc);
|
||||
|
||||
return ready;
|
||||
}
|
||||
@@ -97,9 +103,13 @@ int xe_pxp_get_readiness_status(struct xe_pxp *pxp)
|
||||
if (!xe_pxp_is_enabled(pxp))
|
||||
return -ENODEV;
|
||||
|
||||
/* if the GSC or HuC FW are in an error state, PXP will never work */
|
||||
if (xe_uc_fw_status_to_error(pxp->gt->uc.huc.fw.status) ||
|
||||
xe_uc_fw_status_to_error(pxp->gt->uc.gsc.fw.status))
|
||||
/* If the GSC FW is in an error state, PXP will never work */
|
||||
if (xe_uc_fw_status_to_error(pxp->gt->uc.gsc.fw.status))
|
||||
return -EIO;
|
||||
|
||||
/* Same for HuC FW, but only if the kernel owns HuC-loading (i.e. pre-NVL) */
|
||||
if (MEDIA_VER(gt_to_xe(pxp->gt)) < 35 &&
|
||||
xe_uc_fw_status_to_error(pxp->gt->uc.huc.fw.status))
|
||||
return -EIO;
|
||||
|
||||
guard(xe_pm_runtime)(pxp->xe);
|
||||
@@ -361,6 +371,7 @@ static void pxp_fini(void *arg)
|
||||
int xe_pxp_init(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt = xe->tiles[0].media_gt;
|
||||
bool gsc_ok, huc_ok;
|
||||
struct xe_pxp *pxp;
|
||||
int err;
|
||||
|
||||
@@ -375,10 +386,14 @@ int xe_pxp_init(struct xe_device *xe)
|
||||
if (!(gt->info.engine_mask & BIT(XE_HW_ENGINE_GSCCS0)))
|
||||
return 0;
|
||||
|
||||
/* PXP requires both GSC and HuC firmwares to be available */
|
||||
if (!xe_uc_fw_is_loadable(>->uc.gsc.fw) ||
|
||||
!xe_uc_fw_is_loadable(>->uc.huc.fw)) {
|
||||
drm_info(&xe->drm, "skipping PXP init due to missing FW dependencies");
|
||||
/* PXP requires GSC FW to be available. Pre-NVL it also requires HuC FW */
|
||||
gsc_ok = xe_uc_fw_is_loadable(>->uc.gsc.fw);
|
||||
huc_ok = MEDIA_VER(xe) >= 35 || xe_uc_fw_is_loadable(>->uc.huc.fw);
|
||||
|
||||
if (!gsc_ok || !huc_ok) {
|
||||
drm_info(&xe->drm, "Skipping PXP due to unsatisfied FW deps - GSC=%s, HuC=%s\n",
|
||||
str_yes_no(gsc_ok),
|
||||
MEDIA_VER(xe) >= 35 ? "not needed" : str_yes_no(huc_ok));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ query_engine_cycles(struct xe_device *xe,
|
||||
struct drm_xe_engine_class_instance *eci;
|
||||
struct drm_xe_query_engine_cycles resp;
|
||||
size_t size = sizeof(resp);
|
||||
enum xe_force_wake_domains fw_domain;
|
||||
__ktime_func_t cpu_clock;
|
||||
struct xe_hw_engine *hwe;
|
||||
struct xe_gt *gt;
|
||||
@@ -154,8 +155,10 @@ query_engine_cycles(struct xe_device *xe,
|
||||
if (!hwe)
|
||||
return -EINVAL;
|
||||
|
||||
xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL) {
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL))
|
||||
fw_domain = xe_hw_engine_to_fw_domain(hwe);
|
||||
|
||||
xe_with_force_wake(fw_ref, gt_to_fw(gt), fw_domain) {
|
||||
if (!xe_force_wake_ref_has_domain(fw_ref.domains, fw_domain))
|
||||
return -EIO;
|
||||
|
||||
hwe_read_timestamp(hwe, &resp.engine_cycles, &resp.cpu_timestamp,
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
*/
|
||||
|
||||
#include "xe_device.h"
|
||||
#include "xe_drm_ras.h"
|
||||
#include "xe_pm.h"
|
||||
#include "xe_printk.h"
|
||||
#include "xe_ras.h"
|
||||
#include "xe_ras_types.h"
|
||||
#include "xe_sysctrl.h"
|
||||
#include "xe_sysctrl_event_types.h"
|
||||
#include "xe_sysctrl_mailbox.h"
|
||||
#include "xe_sysctrl_mailbox_types.h"
|
||||
|
||||
/* Severity of detected errors */
|
||||
enum xe_ras_severity {
|
||||
@@ -31,6 +35,17 @@ enum xe_ras_component {
|
||||
XE_RAS_COMP_MAX
|
||||
};
|
||||
|
||||
/* RAS response status codes */
|
||||
enum xe_ras_response_status {
|
||||
XE_RAS_STATUS_SUCCESS = 0,
|
||||
XE_RAS_STATUS_INVALID_PARAM,
|
||||
XE_RAS_STATUS_OP_NOT_SUPPORTED,
|
||||
XE_RAS_STATUS_TIMEOUT,
|
||||
XE_RAS_STATUS_HARDWARE_FAILURE,
|
||||
XE_RAS_STATUS_INSUFFICIENT_RESOURCES,
|
||||
XE_RAS_STATUS_MAX
|
||||
};
|
||||
|
||||
static const char *const xe_ras_severities[] = {
|
||||
[XE_RAS_SEV_NOT_SUPPORTED] = "Not Supported",
|
||||
[XE_RAS_SEV_CORRECTABLE] = "Correctable Error",
|
||||
@@ -50,6 +65,56 @@ static const char *const xe_ras_components[] = {
|
||||
};
|
||||
static_assert(ARRAY_SIZE(xe_ras_components) == XE_RAS_COMP_MAX);
|
||||
|
||||
static u8 drm_to_xe_ras_severity(u8 severity)
|
||||
{
|
||||
switch (severity) {
|
||||
case DRM_XE_RAS_ERR_SEV_CORRECTABLE:
|
||||
return XE_RAS_SEV_CORRECTABLE;
|
||||
case DRM_XE_RAS_ERR_SEV_UNCORRECTABLE:
|
||||
return XE_RAS_SEV_UNCORRECTABLE;
|
||||
default:
|
||||
return XE_RAS_SEV_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 drm_to_xe_ras_component(u8 component)
|
||||
{
|
||||
switch (component) {
|
||||
case DRM_XE_RAS_ERR_COMP_CORE_COMPUTE:
|
||||
return XE_RAS_COMP_CORE_COMPUTE;
|
||||
case DRM_XE_RAS_ERR_COMP_SOC_INTERNAL:
|
||||
return XE_RAS_COMP_SOC_INTERNAL;
|
||||
case DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY:
|
||||
return XE_RAS_COMP_DEVICE_MEMORY;
|
||||
case DRM_XE_RAS_ERR_COMP_PCIE:
|
||||
return XE_RAS_COMP_PCIE;
|
||||
case DRM_XE_RAS_ERR_COMP_FABRIC:
|
||||
return XE_RAS_COMP_FABRIC;
|
||||
default:
|
||||
return XE_RAS_COMP_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static int ras_status_to_errno(u32 status)
|
||||
{
|
||||
switch (status) {
|
||||
case XE_RAS_STATUS_SUCCESS:
|
||||
return 0;
|
||||
case XE_RAS_STATUS_INVALID_PARAM:
|
||||
return -EINVAL;
|
||||
case XE_RAS_STATUS_OP_NOT_SUPPORTED:
|
||||
return -EOPNOTSUPP;
|
||||
case XE_RAS_STATUS_TIMEOUT:
|
||||
return -ETIMEDOUT;
|
||||
case XE_RAS_STATUS_HARDWARE_FAILURE:
|
||||
return -EIO;
|
||||
case XE_RAS_STATUS_INSUFFICIENT_RESOURCES:
|
||||
return -ENOSPC;
|
||||
default:
|
||||
return -EPROTO;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *sev_to_str(u8 severity)
|
||||
{
|
||||
if (severity >= XE_RAS_SEV_MAX)
|
||||
@@ -66,6 +131,68 @@ static inline const char *comp_to_str(u8 component)
|
||||
return xe_ras_components[component];
|
||||
}
|
||||
|
||||
static struct pci_dev *find_usp_dev(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *vsp;
|
||||
|
||||
/*
|
||||
* Device Hierarchy:
|
||||
*
|
||||
* Upstream Switch Port (USP) --> Virtual Switch Port (VSP) --> SGunit (GPU endpoint)
|
||||
*/
|
||||
vsp = pci_upstream_bridge(pdev);
|
||||
if (!vsp)
|
||||
return NULL;
|
||||
|
||||
return pci_upstream_bridge(vsp);
|
||||
}
|
||||
|
||||
static void ras_usp_aer_init(struct xe_device *xe)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
|
||||
struct pci_dev *usp;
|
||||
u16 aer_cap;
|
||||
u32 status;
|
||||
|
||||
usp = find_usp_dev(pdev);
|
||||
if (!usp)
|
||||
return;
|
||||
|
||||
aer_cap = pci_find_ext_capability(usp, PCI_EXT_CAP_ID_ERR);
|
||||
if (!aer_cap) {
|
||||
dev_warn(&usp->dev, "AER capability unavailable\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear any stale Uncorrectable Internal Error Status event in Uncorrectable Error
|
||||
* Status Register.
|
||||
*/
|
||||
pci_read_config_dword(usp, aer_cap + PCI_ERR_UNCOR_STATUS, &status);
|
||||
if (status & PCI_ERR_UNC_INTN)
|
||||
pci_write_config_dword(usp, aer_cap + PCI_ERR_UNCOR_STATUS, PCI_ERR_UNC_INTN);
|
||||
|
||||
/*
|
||||
* All errors are steered to USP which is a PCIe AER Compliant device.
|
||||
* Downgrade all the errors to non-fatal to prevent PCIe bus driver
|
||||
* from triggering a Secondary Bus Reset (SBR). This allows error
|
||||
* detection, containment and recovery in the driver.
|
||||
*
|
||||
* The Uncorrectable Error Severity Register has the 'Uncorrectable
|
||||
* Internal Error Severity' set to fatal by default. Set this to
|
||||
* non-fatal and unmask the error.
|
||||
*/
|
||||
|
||||
/* Downgrade Uncorrectable Internal Error to non-fatal */
|
||||
pci_clear_and_set_config_dword(usp, aer_cap + PCI_ERR_UNCOR_SEVER, PCI_ERR_UNC_INTN, 0);
|
||||
|
||||
/* Unmask Uncorrectable Internal Error */
|
||||
pci_clear_and_set_config_dword(usp, aer_cap + PCI_ERR_UNCOR_MASK, PCI_ERR_UNC_INTN, 0);
|
||||
|
||||
pci_save_state(usp);
|
||||
dev_dbg(&usp->dev, "Uncorrectable Internal Errors downgraded and unmasked\n");
|
||||
}
|
||||
|
||||
void xe_ras_counter_threshold_crossed(struct xe_device *xe,
|
||||
struct xe_sysctrl_event_response *response)
|
||||
{
|
||||
@@ -91,3 +218,136 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe,
|
||||
comp_to_str(component), sev_to_str(severity));
|
||||
}
|
||||
}
|
||||
|
||||
static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter, u32 *value)
|
||||
{
|
||||
struct xe_ras_get_counter_response response = {0};
|
||||
struct xe_ras_get_counter_request request = {0};
|
||||
struct xe_sysctrl_mailbox_command command = {0};
|
||||
struct xe_ras_error_common *common;
|
||||
size_t rlen;
|
||||
int ret;
|
||||
|
||||
request.counter = *counter;
|
||||
|
||||
xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_GET_COUNTER,
|
||||
&request, sizeof(request), &response, sizeof(response));
|
||||
|
||||
ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen);
|
||||
if (ret) {
|
||||
xe_err(xe, "sysctrl: failed to get counter %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rlen != sizeof(response)) {
|
||||
xe_err(xe, "sysctrl: unexpected get counter response length %zu (expected %zu)\n",
|
||||
rlen, sizeof(response));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
common = &response.counter.common;
|
||||
*value = response.value;
|
||||
|
||||
xe_dbg(xe, "[RAS]: get counter %u for %s %s\n", *value, comp_to_str(common->component),
|
||||
sev_to_str(common->severity));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_ras_get_counter() - Get error counter value
|
||||
* @xe: Xe device instance
|
||||
* @severity: Error severity to be queried (&enum drm_xe_ras_error_severity)
|
||||
* @component: Error component to be queried (&enum drm_xe_ras_error_component)
|
||||
* @value: Counter value
|
||||
*
|
||||
* This function retrieves the value of a specific error counter based on
|
||||
* the error severity and component.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *value)
|
||||
{
|
||||
struct xe_ras_error_class counter = {0};
|
||||
|
||||
counter.common.severity = drm_to_xe_ras_severity(severity);
|
||||
counter.common.component = drm_to_xe_ras_component(component);
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
return get_counter(xe, &counter, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_ras_clear_counter() - Clear error counter value
|
||||
* @xe: Xe device instance
|
||||
* @severity: Error severity to be cleared (&enum drm_xe_ras_error_severity)
|
||||
* @component: Error component to be cleared (&enum drm_xe_ras_error_component)
|
||||
*
|
||||
* This function clears the value of a specific error counter based on
|
||||
* the error severity and component.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int xe_ras_clear_counter(struct xe_device *xe, u8 severity, u8 component)
|
||||
{
|
||||
struct xe_ras_clear_counter_response response = {0};
|
||||
struct xe_ras_clear_counter_request request = {0};
|
||||
struct xe_sysctrl_mailbox_command command = {0};
|
||||
struct xe_ras_error_class *counter;
|
||||
size_t rlen;
|
||||
int ret;
|
||||
|
||||
counter = &request.counter;
|
||||
counter->common.severity = drm_to_xe_ras_severity(severity);
|
||||
counter->common.component = drm_to_xe_ras_component(component);
|
||||
|
||||
xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_CLEAR_COUNTER,
|
||||
&request, sizeof(request), &response, sizeof(response));
|
||||
|
||||
guard(xe_pm_runtime)(xe);
|
||||
ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen);
|
||||
if (ret) {
|
||||
xe_err(xe, "sysctrl: failed to clear counter %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (rlen != sizeof(response)) {
|
||||
xe_err(xe, "sysctrl: unexpected clear counter response length %zu (expected %zu)\n",
|
||||
rlen, sizeof(response));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = ras_status_to_errno(response.status);
|
||||
if (ret) {
|
||||
xe_err(xe, "sysctrl: clear counter command failed with status %#x\n",
|
||||
response.status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
counter = &response.counter;
|
||||
|
||||
xe_dbg(xe, "[RAS]: clear counter for %s %s\n", comp_to_str(counter->common.component),
|
||||
sev_to_str(counter->common.severity));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_ras_init - Initialize Xe RAS
|
||||
* @xe: xe device instance
|
||||
*
|
||||
* Initialize Xe RAS
|
||||
*/
|
||||
void xe_ras_init(struct xe_device *xe)
|
||||
{
|
||||
if (!xe->info.has_drm_ras)
|
||||
return;
|
||||
|
||||
xe_drm_ras_init(xe);
|
||||
|
||||
if (!xe->info.has_sysctrl)
|
||||
return;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCIEAER))
|
||||
ras_usp_aer_init(xe);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,15 @@
|
||||
#ifndef _XE_RAS_H_
|
||||
#define _XE_RAS_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct xe_device;
|
||||
struct xe_sysctrl_event_response;
|
||||
|
||||
void xe_ras_counter_threshold_crossed(struct xe_device *xe,
|
||||
struct xe_sysctrl_event_response *response);
|
||||
int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *value);
|
||||
int xe_ras_clear_counter(struct xe_device *xe, u8 severity, u8 component);
|
||||
void xe_ras_init(struct xe_device *xe);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -70,4 +70,55 @@ struct xe_ras_threshold_crossed {
|
||||
struct xe_ras_error_class counters[XE_RAS_NUM_COUNTERS];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct xe_ras_get_counter_request - Request structure for get counter
|
||||
*/
|
||||
struct xe_ras_get_counter_request {
|
||||
/** @counter: Error counter to be queried */
|
||||
struct xe_ras_error_class counter;
|
||||
/** @reserved: Reserved for future use */
|
||||
u32 reserved;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct xe_ras_get_counter_response - Response structure for get counter
|
||||
*/
|
||||
struct xe_ras_get_counter_response {
|
||||
/** @counter: Error counter that was queried */
|
||||
struct xe_ras_error_class counter;
|
||||
/** @value: Current counter value */
|
||||
u32 value;
|
||||
/** @timestamp: Timestamp when counter was last updated */
|
||||
u64 timestamp;
|
||||
/** @threshold: Threshold value for the counter */
|
||||
u32 threshold;
|
||||
/** @reserved: Reserved */
|
||||
u32 reserved[57];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct xe_ras_clear_counter_request - Request structure for clear counter
|
||||
*/
|
||||
struct xe_ras_clear_counter_request {
|
||||
/** @counter: Counter class to be cleared */
|
||||
struct xe_ras_error_class counter;
|
||||
/** @reserved: Reserved for future use */
|
||||
u32 reserved;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct xe_ras_clear_counter_response - Response structure for clear counter
|
||||
*/
|
||||
struct xe_ras_clear_counter_response {
|
||||
/** @counter: Counter class that was cleared */
|
||||
struct xe_ras_error_class counter;
|
||||
/** @reserved: Reserved */
|
||||
u32 reserved;
|
||||
/** @timestamp: Timestamp when the counter was cleared */
|
||||
u64 timestamp;
|
||||
/** @status: Status of the clear operation */
|
||||
u32 status;
|
||||
/** @reserved1: Reserved for future use */
|
||||
u32 reserved1[3];
|
||||
} __packed;
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "xe_reg_whitelist.h"
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
#include "regs/xe_engine_regs.h"
|
||||
#include "regs/xe_gt_regs.h"
|
||||
#include "regs/xe_oa_regs.h"
|
||||
@@ -41,7 +43,7 @@ static bool match_multi_queue_class(const struct xe_device *xe,
|
||||
return xe_gt_supports_multi_queue(gt, hwe->class);
|
||||
}
|
||||
|
||||
static const struct xe_rtp_table_sr register_whitelist = XE_RTP_TABLE_SR(
|
||||
VISIBLE_IF_KUNIT const struct xe_rtp_table_sr register_whitelist = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("WaAllowPMDepthAndInvocationCountAccessFromUMD, 1408556865"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)),
|
||||
XE_RTP_ACTIONS(WHITELIST(PS_INVOCATION_COUNT,
|
||||
@@ -104,6 +106,7 @@ static const struct xe_rtp_table_sr register_whitelist = XE_RTP_TABLE_SR(
|
||||
RING_FORCE_TO_NONPRIV_ACCESS_RW))
|
||||
},
|
||||
);
|
||||
EXPORT_SYMBOL_IF_KUNIT(register_whitelist);
|
||||
|
||||
static const struct xe_rtp_table_sr oa_whitelist = XE_RTP_TABLE_SR(
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ struct xe_hw_engine;
|
||||
struct xe_reg_sr;
|
||||
struct xe_reg_sr_entry;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
extern const struct xe_rtp_table_sr register_whitelist;
|
||||
#endif
|
||||
|
||||
void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe);
|
||||
|
||||
void xe_reg_whitelist_oa_regs(struct xe_gt *gt);
|
||||
|
||||
@@ -227,17 +227,23 @@ static bool rule_matches(const struct xe_device *xe,
|
||||
|
||||
static void rtp_add_sr_entry(const struct xe_rtp_action *action,
|
||||
struct xe_gt *gt,
|
||||
struct xe_hw_engine *hwe,
|
||||
u32 mmio_base,
|
||||
struct xe_reg_sr *sr)
|
||||
{
|
||||
struct xe_reg_sr_entry sr_entry = {
|
||||
.reg = action->reg,
|
||||
.clr_bits = action->clr_bits,
|
||||
.set_bits = action->set_bits,
|
||||
.read_mask = action->read_mask,
|
||||
};
|
||||
|
||||
if (action->use_func)
|
||||
sr_entry.set_bits = action->set_func(gt, hwe);
|
||||
else
|
||||
sr_entry.set_bits = action->set_bits;
|
||||
|
||||
sr_entry.reg.addr += mmio_base;
|
||||
|
||||
xe_reg_sr_add(sr, &sr_entry, gt);
|
||||
}
|
||||
|
||||
@@ -259,7 +265,7 @@ static bool rtp_process_one_sr(const struct xe_rtp_entry_sr *entry,
|
||||
else
|
||||
mmio_base = 0;
|
||||
|
||||
rtp_add_sr_entry(action, gt, mmio_base, sr);
|
||||
rtp_add_sr_entry(action, gt, hwe, mmio_base, sr);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -322,6 +322,25 @@ struct xe_reg_sr;
|
||||
.clr_bits = (mask_bits_), .set_bits = (val_), \
|
||||
.read_mask = 0, ##__VA_ARGS__ }
|
||||
|
||||
/**
|
||||
* XE_RTP_ACTION_FIELD_SET_FUNC: Set a bit range to the value returned by a function
|
||||
* @reg_: Register
|
||||
* @mask_bits_: Mask of bits to be changed in the register, forming a field
|
||||
* @func_: Function that returns value to set in the field denoted by @mask_bits_
|
||||
* @...: Additional fields to override in the struct xe_rtp_action entry
|
||||
*
|
||||
* This macro works like XE_RTP_ACTION_FIELD_SET(), except that the
|
||||
* field value is evaluated at the time the RTP table is processed.
|
||||
*
|
||||
* @func_ will only be called a single time, when the RTP table is being
|
||||
* processed. After processing, the value in the reg_sr entry is fixed and
|
||||
* will not be re-evaluated.
|
||||
*/
|
||||
#define XE_RTP_ACTION_FIELD_SET_FUNC(reg_, mask_bits_, func_, ...) \
|
||||
{ .reg = XE_RTP_DROP_CAST(reg_), \
|
||||
.clr_bits = mask_bits_, .set_func = func_, .use_func = 1, \
|
||||
.read_mask = mask_bits_, ##__VA_ARGS__ }
|
||||
|
||||
/**
|
||||
* XE_RTP_ACTION_WHITELIST - Add register to userspace whitelist
|
||||
* @reg_: Register
|
||||
|
||||
@@ -22,20 +22,37 @@ struct xe_gt;
|
||||
*/
|
||||
struct xe_rtp_action {
|
||||
/** @reg: Register */
|
||||
struct xe_reg reg;
|
||||
struct xe_reg reg;
|
||||
|
||||
/**
|
||||
* @clr_bits: bits to clear when updating register. It's always a
|
||||
* superset of bits being modified
|
||||
*/
|
||||
u32 clr_bits;
|
||||
/** @set_bits: bits to set when updating register */
|
||||
u32 set_bits;
|
||||
u32 clr_bits;
|
||||
|
||||
union {
|
||||
/** @set_bits: bits to set when updating register */
|
||||
u32 set_bits;
|
||||
|
||||
/** @set_func: function to provide bits to set when updating register */
|
||||
u32 (*set_func)(struct xe_gt *gt,
|
||||
struct xe_hw_engine *hwe);
|
||||
};
|
||||
|
||||
#define XE_RTP_NOCHECK .read_mask = 0
|
||||
/** @read_mask: mask for bits to consider when reading value back */
|
||||
u32 read_mask;
|
||||
u32 read_mask;
|
||||
|
||||
#define XE_RTP_ACTION_FLAG_ENGINE_BASE BIT(0)
|
||||
/** @flags: flags to apply on rule evaluation or action */
|
||||
u8 flags;
|
||||
u8 flags;
|
||||
|
||||
/**
|
||||
* @use_func:
|
||||
* Internal flag indicating @set_func should be called instead of
|
||||
* using @set_bits.
|
||||
*/
|
||||
u8 use_func:1;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -69,6 +86,7 @@ struct xe_rtp_rule {
|
||||
u8 platform;
|
||||
u8 subplatform;
|
||||
};
|
||||
|
||||
/*
|
||||
* MATCH_GRAPHICS_VERSION / XE_RTP_MATCH_GRAPHICS_VERSION_RANGE /
|
||||
* MATCH_MEDIA_VERSION / XE_RTP_MATCH_MEDIA_VERSION_RANGE
|
||||
@@ -78,15 +96,18 @@ struct xe_rtp_rule {
|
||||
#define XE_RTP_END_VERSION_UNDEFINED U32_MAX
|
||||
u32 ver_end;
|
||||
};
|
||||
|
||||
/* MATCH_STEP */
|
||||
struct {
|
||||
u8 step_start;
|
||||
u8 step_end;
|
||||
};
|
||||
|
||||
/* MATCH_ENGINE_CLASS / MATCH_NOT_ENGINE_CLASS */
|
||||
struct {
|
||||
u8 engine_class;
|
||||
};
|
||||
|
||||
/* MATCH_FUNC */
|
||||
bool (*match_func)(const struct xe_device *xe,
|
||||
const struct xe_gt *gt,
|
||||
|
||||
@@ -110,12 +110,14 @@ __diag_pop();
|
||||
|
||||
/**
|
||||
* xe_step_platform_get - Determine platform-level stepping from PCI revid
|
||||
* @xe: Xe device
|
||||
* @platform: The Xe platform
|
||||
* @revid: The PCI revid
|
||||
* @step: Pointer to the step struct to update
|
||||
*
|
||||
* Convert the PCI revid into a platform-level stepping value and store that
|
||||
* in the device info.
|
||||
* in @step->platform.
|
||||
*/
|
||||
void xe_step_platform_get(struct xe_device *xe)
|
||||
void xe_step_platform_get(enum xe_platform platform, u8 revid, struct xe_step_info *step)
|
||||
{
|
||||
/*
|
||||
* Not all platforms map PCI revid directly into our symbolic stepping
|
||||
@@ -126,18 +128,21 @@ void xe_step_platform_get(struct xe_device *xe)
|
||||
* checks.
|
||||
*/
|
||||
|
||||
if (xe->info.platform == XE_NOVALAKE_P)
|
||||
xe->info.step.platform = STEP_A0 + xe->info.revid;
|
||||
if (platform == XE_NOVALAKE_P)
|
||||
step->platform = STEP_A0 + revid;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_step_pre_gmdid_get - Determine IP steppings from PCI revid
|
||||
* @xe: Xe device
|
||||
* @step: Pointer to the step struct to update
|
||||
*
|
||||
* Convert the PCI revid into proper IP steppings. This should only be
|
||||
* used on platforms that do not have GMD_ID support.
|
||||
* Convert the PCI revid into proper IP steppings and update @step->basedie,
|
||||
* @step->graphics and @step->media accordingly.
|
||||
*
|
||||
* This should only be used on platforms that do not have GMD_ID support.
|
||||
*/
|
||||
void xe_step_pre_gmdid_get(struct xe_device *xe)
|
||||
void xe_step_pre_gmdid_get(struct xe_device *xe, struct xe_step_info *step)
|
||||
{
|
||||
const struct xe_step_info *revids = NULL;
|
||||
u16 revid = xe->info.revid;
|
||||
@@ -234,9 +239,9 @@ void xe_step_pre_gmdid_get(struct xe_device *xe)
|
||||
}
|
||||
|
||||
done:
|
||||
xe->info.step.graphics = graphics;
|
||||
xe->info.step.media = media;
|
||||
xe->info.step.basedie = basedie;
|
||||
step->graphics = graphics;
|
||||
step->media = media;
|
||||
step->basedie = basedie;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,8 +249,10 @@ void xe_step_pre_gmdid_get(struct xe_device *xe)
|
||||
* @xe: Xe device
|
||||
* @graphics_gmdid_revid: value of graphics GMD_ID register's revid field
|
||||
* @media_gmdid_revid: value of media GMD_ID register's revid field
|
||||
* @step: Poninter to the step struct to update.
|
||||
*
|
||||
* Convert the revid fields of the GMD_ID registers into proper IP steppings.
|
||||
* Convert the revid fields of the GMD_ID registers into proper IP steppings
|
||||
* and update @step->graphics and @step->media accordingly.
|
||||
*
|
||||
* GMD_ID revid values are currently expected to have consistent meanings on
|
||||
* all platforms: major steppings (A0, B0, etc.) are 4 apart, with minor
|
||||
@@ -253,7 +260,8 @@ void xe_step_pre_gmdid_get(struct xe_device *xe)
|
||||
*/
|
||||
void xe_step_gmdid_get(struct xe_device *xe,
|
||||
u32 graphics_gmdid_revid,
|
||||
u32 media_gmdid_revid)
|
||||
u32 media_gmdid_revid,
|
||||
struct xe_step_info *step)
|
||||
{
|
||||
u8 graphics = STEP_A0 + graphics_gmdid_revid;
|
||||
u8 media = STEP_A0 + media_gmdid_revid;
|
||||
@@ -270,8 +278,8 @@ void xe_step_gmdid_get(struct xe_device *xe,
|
||||
media_gmdid_revid);
|
||||
}
|
||||
|
||||
xe->info.step.graphics = graphics;
|
||||
xe->info.step.media = media;
|
||||
step->graphics = graphics;
|
||||
step->media = media;
|
||||
}
|
||||
|
||||
#define STEP_NAME_CASE(name) \
|
||||
|
||||
@@ -10,14 +10,16 @@
|
||||
|
||||
#include "xe_step_types.h"
|
||||
|
||||
enum xe_platform;
|
||||
struct xe_device;
|
||||
|
||||
void xe_step_platform_get(struct xe_device *xe);
|
||||
void xe_step_platform_get(enum xe_platform platform, u8 revid, struct xe_step_info *step);
|
||||
|
||||
void xe_step_pre_gmdid_get(struct xe_device *xe);
|
||||
void xe_step_pre_gmdid_get(struct xe_device *xe, struct xe_step_info *step);
|
||||
void xe_step_gmdid_get(struct xe_device *xe,
|
||||
u32 graphics_gmdid_revid,
|
||||
u32 media_gmdid_revid);
|
||||
u32 media_gmdid_revid,
|
||||
struct xe_step_info *step);
|
||||
static inline u32 xe_step_to_gmdid(enum intel_step step) { return step - STEP_A0; }
|
||||
|
||||
const char *xe_step_name(enum intel_step step);
|
||||
|
||||
@@ -293,6 +293,34 @@ static int sysctrl_send_command(struct xe_sysctrl *sc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_sysctrl_create_command() - Create system controller command
|
||||
* @command: Sysctrl command structure
|
||||
* @group_id: Command group ID
|
||||
* @cmd_id: Command ID
|
||||
* @request: Pointer to request buffer (can be NULL)
|
||||
* @request_len: Size of request buffer
|
||||
* @response: Pointer to response buffer
|
||||
* @response_len: Size of response buffer
|
||||
*
|
||||
* Helper function to create sysctrl command to be sent via %xe_sysctrl_send_command()
|
||||
*/
|
||||
void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 group_id, u8 cmd_id,
|
||||
void *request, size_t request_len, void *response,
|
||||
size_t response_len)
|
||||
{
|
||||
struct xe_sysctrl_app_msg_hdr header = {0};
|
||||
|
||||
header.data = FIELD_PREP(APP_HDR_GROUP_ID_MASK, group_id) |
|
||||
FIELD_PREP(APP_HDR_COMMAND_MASK, cmd_id);
|
||||
|
||||
command->header = header;
|
||||
command->data_in = request;
|
||||
command->data_in_len = request_len;
|
||||
command->data_out = response;
|
||||
command->data_out_len = response_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_sysctrl_mailbox_init - Initialize System Controller mailbox interface
|
||||
* @sc: System controller structure
|
||||
|
||||
@@ -23,6 +23,9 @@ struct xe_sysctrl_mailbox_command;
|
||||
#define XE_SYSCTRL_APP_HDR_VERSION(hdr) \
|
||||
FIELD_GET(APP_HDR_VERSION_MASK, (hdr)->data)
|
||||
|
||||
void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 group_id, u8 cmd_id,
|
||||
void *request, size_t request_len, void *response,
|
||||
size_t response_len);
|
||||
void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc);
|
||||
int xe_sysctrl_send_command(struct xe_sysctrl *sc,
|
||||
struct xe_sysctrl_mailbox_command *cmd,
|
||||
|
||||
@@ -22,9 +22,13 @@ enum xe_sysctrl_group {
|
||||
/**
|
||||
* enum xe_sysctrl_gfsp_cmd - Commands supported by GFSP group
|
||||
*
|
||||
* @XE_SYSCTRL_CMD_GET_COUNTER: Get error counter value
|
||||
* @XE_SYSCTRL_CMD_CLEAR_COUNTER: Clear error counter value
|
||||
* @XE_SYSCTRL_CMD_GET_PENDING_EVENT: Retrieve pending event
|
||||
*/
|
||||
enum xe_sysctrl_gfsp_cmd {
|
||||
XE_SYSCTRL_CMD_GET_COUNTER = 0x03,
|
||||
XE_SYSCTRL_CMD_CLEAR_COUNTER = 0x04,
|
||||
XE_SYSCTRL_CMD_GET_PENDING_EVENT = 0x07,
|
||||
};
|
||||
|
||||
|
||||
@@ -157,7 +157,9 @@ int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe, u8 id)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xe_pcode_init(tile);
|
||||
err = xe_pcode_init_early(tile);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#undef XE_REG_MCR
|
||||
#define XE_REG_MCR(...) XE_REG(__VA_ARGS__, .mcr = 1)
|
||||
|
||||
static const struct xe_rtp_table_sr gt_tunings = XE_RTP_TABLE_SR(
|
||||
VISIBLE_IF_KUNIT const struct xe_rtp_table_sr gt_tunings = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("Tuning: Blend Fill Caching Optimization Disable"),
|
||||
XE_RTP_RULES(PLATFORM(DG2)),
|
||||
XE_RTP_ACTIONS(SET(XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS))
|
||||
@@ -101,6 +101,7 @@ static const struct xe_rtp_table_sr gt_tunings = XE_RTP_TABLE_SR(
|
||||
BANK_HASH_4KB_MODE))
|
||||
},
|
||||
);
|
||||
EXPORT_SYMBOL_IF_KUNIT(gt_tunings);
|
||||
|
||||
static const struct xe_rtp_table_sr engine_tunings = XE_RTP_TABLE_SR(
|
||||
{ XE_RTP_NAME("Tuning: L3 Hashing Mask"),
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#ifndef _XE_TUNING_H_
|
||||
#define _XE_TUNING_H_
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
|
||||
struct drm_printer;
|
||||
struct xe_gt;
|
||||
struct xe_hw_engine;
|
||||
@@ -16,4 +18,8 @@ void xe_tuning_process_engine(struct xe_hw_engine *hwe);
|
||||
void xe_tuning_process_lrc(struct xe_hw_engine *hwe);
|
||||
int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
extern const struct xe_rtp_table_sr gt_tunings;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end)
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* madvise_range_needs_invalidation() - Check whether madvise needs invalidation
|
||||
* @args: madvise ioctl arguments
|
||||
*
|
||||
* Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are
|
||||
* zapped only if the BO is later purged.
|
||||
*
|
||||
* Return: true when the update needs PTE invalidation.
|
||||
*/
|
||||
static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args)
|
||||
{
|
||||
return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE;
|
||||
}
|
||||
|
||||
static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args)
|
||||
{
|
||||
if (XE_IOCTL_DBG(xe, !args))
|
||||
@@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
|
||||
madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args,
|
||||
&details);
|
||||
|
||||
err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr,
|
||||
madvise_range.addr + args->range);
|
||||
if (madvise_range_needs_invalidation(args))
|
||||
err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr,
|
||||
madvise_range.addr + args->range);
|
||||
|
||||
if (madvise_range.has_svm_userptr_vmas)
|
||||
xe_svm_notifier_unlock(vm);
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
__diag_push();
|
||||
__diag_ignore_all("-Woverride-init", "Allow field overrides in table");
|
||||
|
||||
static const struct xe_rtp_table_sr gt_was = XE_RTP_TABLE_SR(
|
||||
VISIBLE_IF_KUNIT const struct xe_rtp_table_sr gt_was = XE_RTP_TABLE_SR(
|
||||
/* Workarounds applying over a range of IPs */
|
||||
|
||||
{ XE_RTP_NAME("14011060649"),
|
||||
@@ -293,7 +293,7 @@ static const struct xe_rtp_table_sr gt_was = XE_RTP_TABLE_SR(
|
||||
XE_RTP_ACTIONS(SET(MMIOATSREQLIMIT_GAM_WALK_3D,
|
||||
DIS_ATS_WRONLY_PG))
|
||||
},
|
||||
{ XE_RTP_NAME("14026144927, 16029437861"),
|
||||
{ XE_RTP_NAME("14026144927, 16029437861, 14026127056"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)),
|
||||
XE_RTP_ACTIONS(SET(L3SQCREG2, L3_SQ_DISABLE_COAMA_2WAY_COH |
|
||||
L3_SQ_DISABLE_COAMA))
|
||||
@@ -307,6 +307,7 @@ static const struct xe_rtp_table_sr gt_was = XE_RTP_TABLE_SR(
|
||||
XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES))
|
||||
},
|
||||
);
|
||||
EXPORT_SYMBOL_IF_KUNIT(gt_was);
|
||||
|
||||
static const struct xe_rtp_table_sr engine_was = XE_RTP_TABLE_SR(
|
||||
/* Workarounds applying over a range of IPs */
|
||||
@@ -586,12 +587,12 @@ static const struct xe_rtp_table_sr engine_was = XE_RTP_TABLE_SR(
|
||||
|
||||
/* Xe3p_LPG*/
|
||||
|
||||
{ XE_RTP_NAME("22021149932"),
|
||||
{ XE_RTP_NAME("22021149932, 14026290593"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0),
|
||||
FUNC(xe_rtp_match_first_render_or_compute)),
|
||||
XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, SAMPLER_LD_LSC_DISABLE))
|
||||
},
|
||||
{ XE_RTP_NAME("14025676848"),
|
||||
{ XE_RTP_NAME("14025676848, 14026270459"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0),
|
||||
FUNC(xe_rtp_match_first_render_or_compute)),
|
||||
XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, LSCFE_SAME_ADDRESS_ATOMICS_COALESCING_DISABLE))
|
||||
@@ -802,10 +803,11 @@ static const struct xe_rtp_entry oob_was_entries[] = {
|
||||
|
||||
static_assert(ARRAY_SIZE(oob_was_entries) == _XE_WA_OOB_COUNT);
|
||||
|
||||
static __maybe_unused const struct xe_rtp_table oob_was = {
|
||||
VISIBLE_IF_KUNIT __maybe_unused const struct xe_rtp_table oob_was = {
|
||||
.entries = oob_was_entries,
|
||||
.n_entries = ARRAY_SIZE(oob_was_entries),
|
||||
};
|
||||
EXPORT_SYMBOL_IF_KUNIT(oob_was);
|
||||
|
||||
static const struct xe_rtp_entry device_oob_was_entries[] = {
|
||||
#include <generated/xe_device_wa_oob.c>
|
||||
@@ -813,10 +815,11 @@ static const struct xe_rtp_entry device_oob_was_entries[] = {
|
||||
|
||||
static_assert(ARRAY_SIZE(device_oob_was_entries) == _XE_DEVICE_WA_OOB_COUNT);
|
||||
|
||||
static __maybe_unused const struct xe_rtp_table device_oob_was = {
|
||||
VISIBLE_IF_KUNIT __maybe_unused const struct xe_rtp_table device_oob_was = {
|
||||
.entries = device_oob_was_entries,
|
||||
.n_entries = ARRAY_SIZE(device_oob_was_entries),
|
||||
};
|
||||
EXPORT_SYMBOL_IF_KUNIT(device_oob_was);
|
||||
|
||||
__diag_pop();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef _XE_WA_H_
|
||||
#define _XE_WA_H_
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
#include "xe_assert.h"
|
||||
|
||||
struct drm_printer;
|
||||
@@ -24,6 +25,12 @@ void xe_wa_apply_tile_workarounds(struct xe_tile *tile);
|
||||
void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p);
|
||||
int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
|
||||
extern const struct xe_rtp_table_sr gt_was;
|
||||
extern __maybe_unused const struct xe_rtp_table oob_was;
|
||||
extern __maybe_unused const struct xe_rtp_table device_oob_was;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* XE_GT_WA - Out-of-band GT workarounds, to be queried and called as needed.
|
||||
* @gt__: gt instance
|
||||
|
||||
@@ -65,3 +65,4 @@
|
||||
|
||||
14025883347 MEDIA_VERSION_RANGE(1301, 3503)
|
||||
GRAPHICS_VERSION_RANGE(2004, 3005)
|
||||
16029380221 MEDIA_VERSION(3500)
|
||||
|
||||
@@ -893,8 +893,9 @@
|
||||
MACRO__(0xD741, ## __VA_ARGS__), \
|
||||
MACRO__(0xD742, ## __VA_ARGS__), \
|
||||
MACRO__(0xD743, ## __VA_ARGS__), \
|
||||
MACRO__(0xD744, ## __VA_ARGS__), \
|
||||
MACRO__(0xD745, ## __VA_ARGS__)
|
||||
MACRO__(0xD745, ## __VA_ARGS__), \
|
||||
MACRO__(0xD74A, ## __VA_ARGS__), \
|
||||
MACRO__(0xD74B, ## __VA_ARGS__)
|
||||
|
||||
/* CRI */
|
||||
#define INTEL_CRI_IDS(MACRO__, ...) \
|
||||
|
||||
@@ -1791,22 +1791,8 @@ static inline int perf_is_paranoid(void)
|
||||
}
|
||||
|
||||
extern int perf_allow_kernel(void);
|
||||
|
||||
static inline int perf_allow_cpu(void)
|
||||
{
|
||||
if (sysctl_perf_event_paranoid > 0 && !perfmon_capable())
|
||||
return -EACCES;
|
||||
|
||||
return security_perf_event_open(PERF_SECURITY_CPU);
|
||||
}
|
||||
|
||||
static inline int perf_allow_tracepoint(void)
|
||||
{
|
||||
if (sysctl_perf_event_paranoid > -1 && !perfmon_capable())
|
||||
return -EPERM;
|
||||
|
||||
return security_perf_event_open(PERF_SECURITY_TRACEPOINT);
|
||||
}
|
||||
extern int perf_allow_cpu(void);
|
||||
extern int perf_allow_tracepoint(void);
|
||||
|
||||
extern int perf_exclude_event(struct perf_event *event, struct pt_regs *regs);
|
||||
|
||||
@@ -2023,6 +2009,19 @@ perf_event_pause(struct perf_event *event, bool reset) { return 0; }
|
||||
static inline int
|
||||
perf_exclude_event(struct perf_event *event, struct pt_regs *regs) { return 0; }
|
||||
|
||||
static inline int perf_allow_kernel(void)
|
||||
{
|
||||
return perfmon_capable() ? 0 : -EACCES;
|
||||
}
|
||||
static inline int perf_allow_cpu(void)
|
||||
{
|
||||
return perfmon_capable() ? 0 : -EACCES;
|
||||
}
|
||||
static inline int perf_allow_tracepoint(void)
|
||||
{
|
||||
return perfmon_capable() ? 0 : -EPERM;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_PERF_EVENTS */
|
||||
|
||||
#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_INTEL)
|
||||
|
||||
@@ -2589,6 +2589,12 @@ enum drm_xe_ras_error_component {
|
||||
DRM_XE_RAS_ERR_COMP_CORE_COMPUTE = 1,
|
||||
/** @DRM_XE_RAS_ERR_COMP_SOC_INTERNAL: SoC Internal Error */
|
||||
DRM_XE_RAS_ERR_COMP_SOC_INTERNAL,
|
||||
/** @DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY: Device Memory Error */
|
||||
DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY,
|
||||
/** @DRM_XE_RAS_ERR_COMP_PCIE: PCIe Subsystem Error */
|
||||
DRM_XE_RAS_ERR_COMP_PCIE,
|
||||
/** @DRM_XE_RAS_ERR_COMP_FABRIC: Fabric Subsystem Error */
|
||||
DRM_XE_RAS_ERR_COMP_FABRIC,
|
||||
/** @DRM_XE_RAS_ERR_COMP_MAX: Max Error */
|
||||
DRM_XE_RAS_ERR_COMP_MAX /* non-ABI */
|
||||
};
|
||||
@@ -2606,7 +2612,10 @@ enum drm_xe_ras_error_component {
|
||||
*/
|
||||
#define DRM_XE_RAS_ERROR_COMPONENT_NAMES { \
|
||||
[DRM_XE_RAS_ERR_COMP_CORE_COMPUTE] = "core-compute", \
|
||||
[DRM_XE_RAS_ERR_COMP_SOC_INTERNAL] = "soc-internal" \
|
||||
[DRM_XE_RAS_ERR_COMP_SOC_INTERNAL] = "soc-internal", \
|
||||
[DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY] = "device-memory", \
|
||||
[DRM_XE_RAS_ERR_COMP_PCIE] = "pcie", \
|
||||
[DRM_XE_RAS_ERR_COMP_FABRIC] = "fabric", \
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
@@ -14769,6 +14769,24 @@ int perf_allow_kernel(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_allow_kernel);
|
||||
|
||||
int perf_allow_cpu(void)
|
||||
{
|
||||
if (sysctl_perf_event_paranoid > 0 && !perfmon_capable())
|
||||
return -EACCES;
|
||||
|
||||
return security_perf_event_open(PERF_SECURITY_CPU);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_allow_cpu);
|
||||
|
||||
int perf_allow_tracepoint(void)
|
||||
{
|
||||
if (sysctl_perf_event_paranoid > -1 && !perfmon_capable())
|
||||
return -EPERM;
|
||||
|
||||
return security_perf_event_open(PERF_SECURITY_TRACEPOINT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_allow_tracepoint);
|
||||
|
||||
/*
|
||||
* Inherit an event from parent task to child task.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user