brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 3109077 Raw
279 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * This file implements the error recovery as a core part of PCIe error4 * reporting. When a PCIe error is delivered, an error message will be5 * collected and printed to console, then, an error recovery procedure6 * will be executed by following the PCI error recovery rules.7 *8 * Copyright (C) 2006 Intel Corp.9 *	Tom Long Nguyen (tom.l.nguyen@intel.com)10 *	Zhang Yanmin (yanmin.zhang@intel.com)11 */12 13#define dev_fmt(fmt) "AER: " fmt14 15#include <linux/pci.h>16#include <linux/pm_runtime.h>17#include <linux/module.h>18#include <linux/kernel.h>19#include <linux/errno.h>20#include <linux/aer.h>21#include "portdrv.h"22#include "../pci.h"23 24static pci_ers_result_t merge_result(enum pci_ers_result orig,25				  enum pci_ers_result new)26{27	if (new == PCI_ERS_RESULT_NO_AER_DRIVER)28		return PCI_ERS_RESULT_NO_AER_DRIVER;29 30	if (new == PCI_ERS_RESULT_NONE)31		return orig;32 33	switch (orig) {34	case PCI_ERS_RESULT_CAN_RECOVER:35	case PCI_ERS_RESULT_RECOVERED:36		orig = new;37		break;38	case PCI_ERS_RESULT_DISCONNECT:39		if (new == PCI_ERS_RESULT_NEED_RESET)40			orig = PCI_ERS_RESULT_NEED_RESET;41		break;42	default:43		break;44	}45 46	return orig;47}48 49static int report_error_detected(struct pci_dev *dev,50				 pci_channel_state_t state,51				 enum pci_ers_result *result)52{53	struct pci_driver *pdrv;54	pci_ers_result_t vote;55	const struct pci_error_handlers *err_handler;56 57	device_lock(&dev->dev);58	pdrv = dev->driver;59	if (pci_dev_is_disconnected(dev)) {60		vote = PCI_ERS_RESULT_DISCONNECT;61	} else if (!pci_dev_set_io_state(dev, state)) {62		pci_info(dev, "can't recover (state transition %u -> %u invalid)\n",63			dev->error_state, state);64		vote = PCI_ERS_RESULT_NONE;65	} else if (!pdrv || !pdrv->err_handler ||66		   !pdrv->err_handler->error_detected) {67		/*68		 * If any device in the subtree does not have an error_detected69		 * callback, PCI_ERS_RESULT_NO_AER_DRIVER prevents subsequent70		 * error callbacks of "any" device in the subtree, and will71		 * exit in the disconnected error state.72		 */73		if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {74			vote = PCI_ERS_RESULT_NO_AER_DRIVER;75			pci_info(dev, "can't recover (no error_detected callback)\n");76		} else {77			vote = PCI_ERS_RESULT_NONE;78		}79	} else {80		err_handler = pdrv->err_handler;81		vote = err_handler->error_detected(dev, state);82	}83	pci_uevent_ers(dev, vote);84	*result = merge_result(*result, vote);85	device_unlock(&dev->dev);86	return 0;87}88 89static int pci_pm_runtime_get_sync(struct pci_dev *pdev, void *data)90{91	pm_runtime_get_sync(&pdev->dev);92	return 0;93}94 95static int pci_pm_runtime_put(struct pci_dev *pdev, void *data)96{97	pm_runtime_put(&pdev->dev);98	return 0;99}100 101static int report_frozen_detected(struct pci_dev *dev, void *data)102{103	return report_error_detected(dev, pci_channel_io_frozen, data);104}105 106static int report_normal_detected(struct pci_dev *dev, void *data)107{108	return report_error_detected(dev, pci_channel_io_normal, data);109}110 111static int report_mmio_enabled(struct pci_dev *dev, void *data)112{113	struct pci_driver *pdrv;114	pci_ers_result_t vote, *result = data;115	const struct pci_error_handlers *err_handler;116 117	device_lock(&dev->dev);118	pdrv = dev->driver;119	if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->mmio_enabled)120		goto out;121 122	err_handler = pdrv->err_handler;123	vote = err_handler->mmio_enabled(dev);124	*result = merge_result(*result, vote);125out:126	device_unlock(&dev->dev);127	return 0;128}129 130static int report_slot_reset(struct pci_dev *dev, void *data)131{132	struct pci_driver *pdrv;133	pci_ers_result_t vote, *result = data;134	const struct pci_error_handlers *err_handler;135 136	device_lock(&dev->dev);137	pdrv = dev->driver;138	if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->slot_reset)139		goto out;140 141	err_handler = pdrv->err_handler;142	vote = err_handler->slot_reset(dev);143	*result = merge_result(*result, vote);144out:145	device_unlock(&dev->dev);146	return 0;147}148 149static int report_resume(struct pci_dev *dev, void *data)150{151	struct pci_driver *pdrv;152	const struct pci_error_handlers *err_handler;153 154	device_lock(&dev->dev);155	pdrv = dev->driver;156	if (!pci_dev_set_io_state(dev, pci_channel_io_normal) ||157	    !pdrv || !pdrv->err_handler || !pdrv->err_handler->resume)158		goto out;159 160	err_handler = pdrv->err_handler;161	err_handler->resume(dev);162out:163	pci_uevent_ers(dev, PCI_ERS_RESULT_RECOVERED);164	device_unlock(&dev->dev);165	return 0;166}167 168/**169 * pci_walk_bridge - walk bridges potentially AER affected170 * @bridge:	bridge which may be a Port, an RCEC, or an RCiEP171 * @cb:		callback to be called for each device found172 * @userdata:	arbitrary pointer to be passed to callback173 *174 * If the device provided is a bridge, walk the subordinate bus, including175 * any bridged devices on buses under this bus.  Call the provided callback176 * on each device found.177 *178 * If the device provided has no subordinate bus, e.g., an RCEC or RCiEP,179 * call the callback on the device itself.180 */181static void pci_walk_bridge(struct pci_dev *bridge,182			    int (*cb)(struct pci_dev *, void *),183			    void *userdata)184{185	if (bridge->subordinate)186		pci_walk_bus(bridge->subordinate, cb, userdata);187	else188		cb(bridge, userdata);189}190 191pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,192		pci_channel_state_t state,193		pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev))194{195	int type = pci_pcie_type(dev);196	struct pci_dev *bridge;197	pci_ers_result_t status = PCI_ERS_RESULT_CAN_RECOVER;198	struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);199 200	/*201	 * If the error was detected by a Root Port, Downstream Port, RCEC,202	 * or RCiEP, recovery runs on the device itself.  For Ports, that203	 * also includes any subordinate devices.204	 *205	 * If it was detected by another device (Endpoint, etc), recovery206	 * runs on the device and anything else under the same Port, i.e.,207	 * everything under "bridge".208	 */209	if (type == PCI_EXP_TYPE_ROOT_PORT ||210	    type == PCI_EXP_TYPE_DOWNSTREAM ||211	    type == PCI_EXP_TYPE_RC_EC ||212	    type == PCI_EXP_TYPE_RC_END)213		bridge = dev;214	else215		bridge = pci_upstream_bridge(dev);216 217	pci_walk_bridge(bridge, pci_pm_runtime_get_sync, NULL);218 219	pci_dbg(bridge, "broadcast error_detected message\n");220	if (state == pci_channel_io_frozen) {221		pci_walk_bridge(bridge, report_frozen_detected, &status);222		if (reset_subordinates(bridge) != PCI_ERS_RESULT_RECOVERED) {223			pci_warn(bridge, "subordinate device reset failed\n");224			goto failed;225		}226	} else {227		pci_walk_bridge(bridge, report_normal_detected, &status);228	}229 230	if (status == PCI_ERS_RESULT_CAN_RECOVER) {231		status = PCI_ERS_RESULT_RECOVERED;232		pci_dbg(bridge, "broadcast mmio_enabled message\n");233		pci_walk_bridge(bridge, report_mmio_enabled, &status);234	}235 236	if (status == PCI_ERS_RESULT_NEED_RESET) {237		/*238		 * TODO: Should call platform-specific239		 * functions to reset slot before calling240		 * drivers' slot_reset callbacks?241		 */242		status = PCI_ERS_RESULT_RECOVERED;243		pci_dbg(bridge, "broadcast slot_reset message\n");244		pci_walk_bridge(bridge, report_slot_reset, &status);245	}246 247	if (status != PCI_ERS_RESULT_RECOVERED)248		goto failed;249 250	pci_dbg(bridge, "broadcast resume message\n");251	pci_walk_bridge(bridge, report_resume, &status);252 253	/*254	 * If we have native control of AER, clear error status in the device255	 * that detected the error.  If the platform retained control of AER,256	 * it is responsible for clearing this status.  In that case, the257	 * signaling device may not even be visible to the OS.258	 */259	if (host->native_aer || pcie_ports_native) {260		pcie_clear_device_status(dev);261		pci_aer_clear_nonfatal_status(dev);262	}263 264	pci_walk_bridge(bridge, pci_pm_runtime_put, NULL);265 266	pci_info(bridge, "device recovery successful\n");267	return status;268 269failed:270	pci_walk_bridge(bridge, pci_pm_runtime_put, NULL);271 272	pci_uevent_ers(bridge, PCI_ERS_RESULT_DISCONNECT);273 274	/* TODO: Should kernel panic here? */275	pci_info(bridge, "device recovery failed\n");276 277	return status;278}279