📄 pcibr_error.c
字号:
/* We read the INT_STATUS register as a 64bit picreg_t for PIC and a * 32bit bridgereg_t for BRIDGE, but always process the result as a * 64bit value so the code can be "common" for both PIC and BRIDGE... */ if (IS_PIC_SOFT(pcibr_soft)) { int_status_64 = (bridge->p_int_status_64 & ~BRIDGE_ISR_INT_MSK); int_status = (uint64_t)int_status_64; number_bits = PCIBR_ISR_MAX_ERRS_PIC; } else { int_status_32 = (bridge->b_int_status & ~BRIDGE_ISR_INT_MSK); int_status = ((uint64_t)int_status_32) & 0xffffffff; number_bits = PCIBR_ISR_MAX_ERRS_BRIDGE; } PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_INTR_ERROR, pcibr_soft->bs_conn, "pcibr_error_intr_handler: int_status=0x%x\n", int_status)); /* int_status is which bits we have to clear; * err_status is the bits we haven't handled yet. */ err_status = int_status & ~BRIDGE_ISR_MULTI_ERR; if (!(int_status & ~BRIDGE_ISR_INT_MSK)) { /* * No error bit set!!. */ return; } /* * If we have a PCIBUS_PIOERR, hand it to the logger. */ if (int_status & BRIDGE_ISR_PCIBUS_PIOERR) { pcibr_pioerr_check(pcibr_soft); }#ifdef BRIDGE_B_DATACORR_WAR if ((pcibr_soft->bs_rev_num == BRIDGE_PART_REV_B) && (err_status & BRIDGE_IMR_LLP_REC_CBERR)) { if (bridge_rev_b_data_check_disable) printk(KERN_WARNING "\n%s%s: %s%s\n", rev_b_datacorr_warning, pcibr_soft->bs_name, rev_b_datacorr_mesg, rev_b_datacorr_warning); else { ql_bridge_rev_b_war(pcibr_soft->bs_vhdl); PRINT_PANIC( "\n%s%s: %s%s\n", rev_b_datacorr_warning, pcibr_soft->bs_name, rev_b_datacorr_mesg, rev_b_datacorr_warning); } err_status &= ~BRIDGE_IMR_LLP_REC_CBERR; }#endif /* BRIDGE_B_DATACORR_WAR */ if (err_status) { struct bs_errintr_stat_s *bs_estat = pcibr_soft->bs_errintr_stat; for (i = PCIBR_ISR_ERR_START; i < number_bits; i++, bs_estat++) { if (err_status & (1ull << i)) { uint32_t errrate = 0; uint32_t errcount = 0; uint32_t errinterval = 0, current_tick = 0; int llp_tx_retry_errors = 0; int is_llp_tx_retry_intr = 0; bs_estat->bs_errcount_total++; current_tick = jiffies; errinterval = (current_tick - bs_estat->bs_lasterr_timestamp); errcount = (bs_estat->bs_errcount_total - bs_estat->bs_lasterr_snapshot); /* LLP interrrupt errors are only valid on BUS0 of the PIC */ if (pcibr_soft->bs_busnum == 0) is_llp_tx_retry_intr = (BRIDGE_ISR_LLP_TX_RETRY==(1ull << i)); /* Check for the divide by zero condition while * calculating the error rates. */ if (errinterval) { errrate = errcount / errinterval; /* If able to calculate error rate * on a LLP transmitter retry interrupt, check * if the error rate is nonzero and we have seen * a certain minimum number of errors. * * NOTE : errcount is being compared to * PCIBR_ERRTIME_THRESHOLD to make sure that we are not * seeing cases like x error interrupts per y ticks for * very low x ,y (x > y ) which could result in a * rate > 100/tick. */ if (is_llp_tx_retry_intr && errrate && (errcount >= PCIBR_ERRTIME_THRESHOLD)) { llp_tx_retry_errors = 1; } } else { errrate = 0; /* Since we are not able to calculate the * error rate check if we exceeded a certain * minimum number of errors for LLP transmitter * retries. Note that this can only happen * within the first tick after the last snapshot. */ if (is_llp_tx_retry_intr && (errcount >= PCIBR_ERRINTR_DISABLE_LEVEL)) { llp_tx_retry_errors = 1; } } /* * If a non-zero error rate (which is equivalent to * to 100 errors/tick at least) for the LLP transmitter * retry interrupt was seen, check if we should print * a warning message. */ if (llp_tx_retry_errors) { static uint32_t last_printed_rate; if (errrate > last_printed_rate) { last_printed_rate = errrate; /* Print the warning only if the error rate * for the transmitter retry interrupt * exceeded the previously printed rate. */ printk(KERN_WARNING "%s: %s, Excessive error interrupts : %d/tick\n", pcibr_soft->bs_name, pcibr_isr_errs[i], errrate); } /* * Update snapshot, and time */ bs_estat->bs_lasterr_timestamp = current_tick; bs_estat->bs_lasterr_snapshot = bs_estat->bs_errcount_total; } /* * If the error rate is high enough, print the error rate. */ if (errinterval > PCIBR_ERRTIME_THRESHOLD) { if (errrate > PCIBR_ERRRATE_THRESHOLD) { printk(KERN_NOTICE "%s: %s, Error rate %d/tick", pcibr_soft->bs_name, pcibr_isr_errs[i], errrate); /* * Update snapshot, and time */ bs_estat->bs_lasterr_timestamp = current_tick; bs_estat->bs_lasterr_snapshot = bs_estat->bs_errcount_total; } } /* PIC BRINGUP WAR (PV# 856155): * Dont disable PCI_X_ARB_ERR interrupts, we need the * interrupt inorder to clear the DEV_BROKE bits in * b_arb register to re-enable the device. */ if (IS_PIC_SOFT(pcibr_soft) && !(err_status & PIC_ISR_PCIX_ARB_ERR) && PCIBR_WAR_ENABLED(PV856155, pcibr_soft)) { if (bs_estat->bs_errcount_total > PCIBR_ERRINTR_DISABLE_LEVEL) { /* * We have seen a fairly large number of errors of * this type. Let's disable the interrupt. But flash * a message about the interrupt being disabled. */ printk(KERN_NOTICE "%s Disabling error interrupt type %s. Error count %d", pcibr_soft->bs_name, pcibr_isr_errs[i], bs_estat->bs_errcount_total); disable_errintr_mask |= (1ull << i); } } /* PIC: WAR for PV 856155 end-of-if */ } } } if (disable_errintr_mask) { unsigned s; /* * Disable some high frequency errors as they * could eat up too much cpu time. */ s = pcibr_lock(pcibr_soft); if (IS_PIC_SOFT(pcibr_soft)) { bridge->p_int_enable_64 &= (picreg_t)(~disable_errintr_mask); } else { bridge->b_int_enable &= (bridgereg_t)(~disable_errintr_mask); } pcibr_unlock(pcibr_soft, s); } /* * If we leave the PROM cacheable, T5 might * try to do a cache line sized writeback to it, * which will cause a BRIDGE_ISR_INVLD_ADDR. */ if ((err_status & BRIDGE_ISR_INVLD_ADDR) && (0x00000000 == bridge->b_wid_err_upper) && (0x00C00000 == (0xFFC00000 & bridge->b_wid_err_lower)) && (0x00402000 == (0x00F07F00 & bridge->b_wid_err_cmdword))) { err_status &= ~BRIDGE_ISR_INVLD_ADDR; }#if defined (PCIBR_LLP_CONTROL_WAR) /* * The bridge bug, where the llp_config or control registers * need to be read back after being written, affects an MP * system since there could be small windows between writing * the register and reading it back on one cpu while another * cpu is fielding an interrupt. If we run into this scenario, * workaround the problem by ignoring the error. (bug 454474) * pcibr_llp_control_war_cnt keeps an approximate number of * times we saw this problem on a system. */ if ((err_status & BRIDGE_ISR_INVLD_ADDR) && ((((uint64_t) bridge->b_wid_err_upper << 32) | (bridge->b_wid_err_lower)) == (BRIDGE_INT_RST_STAT & 0xff0))) {#if 0 if (kdebug) printk(KERN_NOTICE "%s bridge: ignoring llp/control address interrupt", pcibr_soft->bs_name);#endif pcibr_llp_control_war_cnt++; err_status &= ~BRIDGE_ISR_INVLD_ADDR; }#endif /* PCIBR_LLP_CONTROL_WAR */#ifdef EHE_ENABLE /* Check if this is the RESP_XTALK_ERROR interrupt. * This can happen due to a failed DMA READ operation. */ if (err_status & BRIDGE_ISR_RESP_XTLK_ERR) { /* Phase 1 : Look at the error state in the bridge and further * down in the device layers. */ (void)error_state_set(pcibr_soft->bs_conn, ERROR_STATE_LOOKUP); IOERROR_SETVALUE(&ioe, widgetnum, pcibr_soft->bs_xid); (void)pcibr_error_handler((error_handler_arg_t)pcibr_soft, error_code, mode, &ioe); /* Phase 2 : Perform the action agreed upon in phase 1. */ (void)error_state_set(pcibr_soft->bs_conn, ERROR_STATE_ACTION); rv = pcibr_error_handler((error_handler_arg_t)pcibr_soft, error_code, mode, &ioe); } if (rv != IOERROR_HANDLED) {#endif /* EHE_ENABLE */ bridge_errors_to_dump |= BRIDGE_ISR_PCIBUS_PIOERR; /* Dump/Log Bridge error interrupt info */ if (err_status & bridge_errors_to_dump) { printk("BRIDGE ERR_STATUS 0x%lx\n", err_status); pcibr_error_dump(pcibr_soft); } /* PIC BRINGUP WAR (PV# 867308): * Make BRIDGE_ISR_LLP_REC_SNERR & BRIDGE_ISR_LLP_REC_CBERR fatal errors * so we know we've hit the problem defined in PV 867308 that we believe * has only been seen in simulation */ if (IS_PIC_SOFT(pcibr_soft) && PCIBR_WAR_ENABLED(PV867308, pcibr_soft) && (err_status & (BRIDGE_ISR_LLP_REC_SNERR | BRIDGE_ISR_LLP_REC_CBERR))) { printk("BRIDGE ERR_STATUS 0x%x\n", err_status); pcibr_error_dump(pcibr_soft);#ifdef LATER machine_error_dump("");#endif PRINT_PANIC("PCI Bridge Error interrupt killed the system"); } if (err_status & BRIDGE_ISR_ERROR_FATAL) {#ifdef LATER machine_error_dump("");#endif PRINT_PANIC("PCI Bridge Error interrupt killed the system"); /*NOTREACHED */ }#ifdef EHE_ENABLE }#endif /* * We can't return without re-enabling the interrupt, since * it would cause problems for devices like IOC3 (Lost * interrupts ?.). So, just cleanup the interrupt, and * use saved values later.. * * PIC doesn't require groups of interrupts to be cleared... */ if (IS_PIC_SOFT(pcibr_soft)) { bridge->p_int_rst_stat_64 = (picreg_t)(int_status | BRIDGE_IRR_MULTI_CLR); } else { bridge->b_int_rst_stat = (bridgereg_t)pcibr_errintr_group(int_status); } /* PIC BRINGUP WAR (PV# 856155): * On a PCI_X_ARB_ERR error interrupt clear the DEV_BROKE bits from * the b_arb register to re-enable the device. */ if (IS_PIC_SOFT(pcibr_soft) && (err_status & PIC_ISR_PCIX_ARB_ERR) && PCIBR_WAR_ENABLED(PV856155, pcibr_soft)) { bridge->b_arb |= (0xf << 20); } /* Zero out bserr_intstat field */ pcibr_soft->bs_errinfo.bserr_intstat = 0;}/* * pcibr_addr_toslot * Given the 'pciaddr' find out which slot this address is * allocated to, and return the slot number. * While we have the info handy, construct the * function number, space code and offset as well. * * NOTE: if this routine is called, we don't know whether * the address is in CFG, MEM, or I/O space. We have to guess. * This will be the case on PIO stores, where the only way * we have of getting the address is to check the Bridge, which * stores the PCI address but not the space and not the xtalk * address (from which we could get it). */intpcibr_addr_toslot(pcibr_soft_t pcibr_soft, iopaddr_t pciaddr, pciio_space_t *spacep, iopaddr_t *offsetp, pciio_function_t *funcp){ int s, f = 0, w; iopaddr_t base; size_t size; pciio_piospace_t piosp; /* * Check if the address is in config space */ if ((pciaddr >= BRIDGE_CONFIG_BASE) && (pciaddr < BRIDGE_CONFIG_END)) { if (pciaddr >= BRIDGE_CONFIG1_BASE) pciaddr -= BRIDGE_CONFIG1_BASE; else pciaddr -= BRIDGE_CONFIG_BASE; s = pciaddr / BRIDGE_CONFIG_SLOT_SIZE; pciaddr %= BRIDGE_CONFIG_SLOT_SIZE; if (funcp) { f = pciaddr / 0x100; pciaddr %= 0x100; } if (spacep) *spacep = PCIIO_SPACE_CFG; if (offsetp) *offsetp = pciaddr; if (funcp) *funcp = f; return s; } for (s = pcibr_soft->bs_min_slot; s < PCIBR_NUM_SLOTS(pcibr_soft); ++s) { int nf = pcibr_soft->bs_slot[s].bss_ninfo; pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[s].bss_infos; for (f = 0; f < nf; f++) { pcibr_info_t pcibr_info = pcibr_infoh[f]; if (!pcibr_info) continue; for (w = 0; w < 6; w++) { if (pcibr_info->f_window[w].w_space == PCIIO_SPACE_NONE) { continue; } base = pcibr_info->f_window[w].w_base; size = pcibr_info->f_window[w].w_size; if ((pciaddr >= base) && (pciaddr < (base + size))) { if (spacep) *spacep = PCIIO_SPACE_WIN(w); if (offsetp) *offsetp = pciaddr - base; if (funcp) *funcp = f; return s; } /* endif match */ } /* next window */ } /* next func */ } /* next slot */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -