summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/xe/xe_pci_error.c
blob: e41af2ac7f23b0c6c1a83bedf4621d88e1c878e2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// 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_ras.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 ras_action_to_pci_result(struct pci_dev *pdev, u8 action)
{
	switch (action) {
	case XE_RAS_RECOVERY_ACTION_RECOVERED:
		return PCI_ERS_RESULT_RECOVERED;
	case XE_RAS_RECOVERY_ACTION_RESET:
		prepare_device_for_reset(pdev);
		return PCI_ERS_RESULT_NEED_RESET;
	case XE_RAS_RECOVERY_ACTION_DISCONNECT:
		return PCI_ERS_RESULT_DISCONNECT;
	default:
		return PCI_ERS_RESULT_DISCONNECT;
	}
}

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);
	enum xe_ras_recovery_action action;

	xe_info(xe, "PCI error: MMIO enabled\n");
	action = xe_ras_process_errors(xe);

	return ras_action_to_pci_result(pdev, action);
}

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,
};