📄 aerdrv_core.c
字号:
pci_ers_result_t status; struct find_aer_service_data data; if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) udev = dev; else udev= dev->bus->self; data.is_downstream = 0; data.aer_driver = NULL; find_aer_service(udev, &data); /* * Use the aer driver of the error agent firstly. * If it hasn't the aer driver, use the root port's */ if (!data.aer_driver || !data.aer_driver->reset_link) { if (data.is_downstream && aerdev->device.driver && to_service_driver(aerdev->device.driver)->reset_link) { data.aer_driver = to_service_driver(aerdev->device.driver); } else { printk(KERN_DEBUG "No link-reset support to Device ID" "[%s]\n", dev->dev.bus_id); return PCI_ERS_RESULT_DISCONNECT; } } status = data.aer_driver->reset_link(udev); if (status != PCI_ERS_RESULT_RECOVERED) { printk(KERN_DEBUG "Link reset at upstream Device ID" "[%s] failed\n", udev->dev.bus_id); return PCI_ERS_RESULT_DISCONNECT; } return status;}/** * do_recovery - handle nonfatal/fatal error recovery process * @aerdev: pointer to a pcie_device data structure of root port * @dev: pointer to a pci_dev data structure of agent detecting an error * @severity: error severity type * * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast * error detected message to all downstream drivers within a hierarchy in * question and return the returned code. */static pci_ers_result_t do_recovery(struct pcie_device *aerdev, struct pci_dev *dev, int severity){ pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED; enum pci_channel_state state; if (severity == AER_FATAL) state = pci_channel_io_frozen; else state = pci_channel_io_normal; status = broadcast_error_message(dev, state, "error_detected", report_error_detected); if (severity == AER_FATAL) { result = reset_link(aerdev, dev); if (result != PCI_ERS_RESULT_RECOVERED) { /* TODO: Should panic here? */ return result; } } if (status == PCI_ERS_RESULT_CAN_RECOVER) status = broadcast_error_message(dev, state, "mmio_enabled", report_mmio_enabled); if (status == PCI_ERS_RESULT_NEED_RESET) { /* * TODO: Should call platform-specific * functions to reset slot before calling * drivers' slot_reset callbacks? */ status = broadcast_error_message(dev, state, "slot_reset", report_slot_reset); } if (status == PCI_ERS_RESULT_RECOVERED) broadcast_error_message(dev, state, "resume", report_resume); return status;}/** * handle_error_source - handle logging error into an event log * @aerdev: pointer to pcie_device data structure of the root port * @dev: pointer to pci_dev data structure of error source device * @info: comprehensive error information * * Invoked when an error being detected by Root Port. */static void handle_error_source(struct pcie_device * aerdev, struct pci_dev *dev, struct aer_err_info info){ pci_ers_result_t status = 0; int pos; if (info.severity == AER_CORRECTABLE) { /* * Correctable error does not need software intevention. * No need to go through error recovery process. */ pos = pci_find_aer_capability(dev); if (pos) pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, info.status); } else { status = do_recovery(aerdev, dev, info.severity); if (status == PCI_ERS_RESULT_RECOVERED) { printk(KERN_DEBUG "AER driver successfully recovered\n"); } else { /* TODO: Should kernel panic here? */ printk(KERN_DEBUG "AER driver didn't recover\n"); } }}/** * aer_enable_rootport - enable Root Port's interrupts when receiving messages * @rpc: pointer to a Root Port data structure * * Invoked when PCIE bus loads AER service driver. */void aer_enable_rootport(struct aer_rpc *rpc){ struct pci_dev *pdev = rpc->rpd->port; int pos, aer_pos; u16 reg16; u32 reg32; pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); /* Clear PCIE Capability's Device Status */ pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); /* Disable system error generation in response to error messages */ pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); aer_pos = pci_find_aer_capability(pdev); /* Clear error status */ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); /* Enable Root Port device reporting error itself */ pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, ®16); reg16 = reg16 | PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE; pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL, reg16); /* Enable Root Port's interrupt in response to error messages */ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ROOT_PORT_INTR_ON_MESG_MASK);}/** * disable_root_aer - disable Root Port's interrupts when receiving messages * @rpc: pointer to a Root Port data structure * * Invoked when PCIE bus unloads AER service driver. */static void disable_root_aer(struct aer_rpc *rpc){ struct pci_dev *pdev = rpc->rpd->port; u32 reg32; int pos; pos = pci_find_aer_capability(pdev); /* Disable Root's interrupt in response to error messages */ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); /* Clear Root's error status reg */ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);}/** * get_e_source - retrieve an error source * @rpc: pointer to the root port which holds an error * * Invoked by DPC handler to consume an error. */static struct aer_err_source* get_e_source(struct aer_rpc *rpc){ struct aer_err_source *e_source; unsigned long flags; /* Lock access to Root error producer/consumer index */ spin_lock_irqsave(&rpc->e_lock, flags); if (rpc->prod_idx == rpc->cons_idx) { spin_unlock_irqrestore(&rpc->e_lock, flags); return NULL; } e_source = &rpc->e_sources[rpc->cons_idx]; rpc->cons_idx++; if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) rpc->cons_idx = 0; spin_unlock_irqrestore(&rpc->e_lock, flags); return e_source;}static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info){ int pos; pos = pci_find_aer_capability(dev); /* The device might not support AER */ if (!pos) return AER_SUCCESS; if (info->severity == AER_CORRECTABLE) { pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &info->status); if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) return AER_UNSUCCESS; } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || info->severity == AER_NONFATAL) { /* Link is still healthy for IO reads */ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &info->status); if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) return AER_UNSUCCESS; if (info->status & AER_LOG_TLP_MASKS) { info->flags |= AER_TLP_HEADER_VALID_FLAG; pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); } } return AER_SUCCESS;}/** * aer_isr_one_error - consume an error detected by root port * @p_device: pointer to error root port service device * @e_src: pointer to an error source */static void aer_isr_one_error(struct pcie_device *p_device, struct aer_err_source *e_src){ struct device *s_device; struct aer_err_info e_info = {0, 0, 0,}; int i; u16 id; /* * There is a possibility that both correctable error and * uncorrectable error being logged. Report correctable error first. */ for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) { if (i > 4) break; if (!(e_src->status & i)) continue; /* Init comprehensive error information */ if (i & PCI_ERR_ROOT_COR_RCV) { id = ERR_COR_ID(e_src->id); e_info.severity = AER_CORRECTABLE; } else { id = ERR_UNCOR_ID(e_src->id); e_info.severity = ((e_src->status >> 6) & 1); } if (e_src->status & (PCI_ERR_ROOT_MULTI_COR_RCV | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) e_info.flags |= AER_MULTI_ERROR_VALID_FLAG; if (!(s_device = find_source_device(p_device->port, id))) { printk(KERN_DEBUG "%s->can't find device of ID%04x\n", __FUNCTION__, id); continue; } if (get_device_error_info(to_pci_dev(s_device), &e_info) == AER_SUCCESS) { aer_print_error(to_pci_dev(s_device), &e_info); handle_error_source(p_device, to_pci_dev(s_device), e_info); } }}/** * aer_isr - consume errors detected by root port * @work: definition of this work item * * Invoked, as DPC, when root port records new detected error */void aer_isr(struct work_struct *work){ struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); struct pcie_device *p_device = rpc->rpd; struct aer_err_source *e_src; mutex_lock(&rpc->rpc_mutex); e_src = get_e_source(rpc); while (e_src) { aer_isr_one_error(p_device, e_src); e_src = get_e_source(rpc); } mutex_unlock(&rpc->rpc_mutex); wake_up(&rpc->wait_release);}/** * aer_delete_rootport - disable root port aer and delete service data * @rpc: pointer to a root port device being deleted * * Invoked when AER service unloaded on a specific Root Port */void aer_delete_rootport(struct aer_rpc *rpc){ /* Disable root port AER itself */ disable_root_aer(rpc); kfree(rpc);}/** * aer_init - provide AER initialization * @dev: pointer to AER pcie device * * Invoked when AER service driver is loaded. */int aer_init(struct pcie_device *dev){ if (aer_osc_setup(dev) && !forceload) return -ENXIO; return AER_SUCCESS;}EXPORT_SYMBOL_GPL(pci_find_aer_capability);EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);EXPORT_SYMBOL_GPL(pci_cleanup_aer_correct_error_status);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -