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