Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

GenWQE: Add support for EEH error recovery

This patch implements the callbacks and functions necessary to have EEH
recovery support.

It adds a config option to enable or disable explicit calls to trigger
platform specific mechanisms on error recovery paths. This option is
enabled by default only on PPC64 systems and can be overritten via
debugfs. If this option is enabled, on the error recovery path the
driver will call pci_channel_offline() to check for error condition and
issue non-raw MMIO reads to trigger early EEH detection in case of
hardware failures. This is necessary since the driver MMIO helper
funtions use raw accessors.

Signed-off-by: Kleber Sacilotto de Souza <klebers@linux.vnet.ibm.com>
Acked-by: Frank Haverkamp <haver@linux.vnet.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Kleber Sacilotto de Souza and committed by
Greg Kroah-Hartman
fb145456 c1f732ad

+115 -18
+6
drivers/misc/genwqe/Kconfig
··· 11 11 Enables PCIe card driver for IBM GenWQE accelerators. 12 12 The user-space interface is described in 13 13 include/linux/genwqe/genwqe_card.h. 14 + 15 + config GENWQE_PLATFORM_ERROR_RECOVERY 16 + int "Use platform recovery procedures (0=off, 1=on)" 17 + depends on GENWQE 18 + default 1 if PPC64 19 + default 0
+66 -13
drivers/misc/genwqe/card_base.c
··· 140 140 cd->class_genwqe = class_genwqe; 141 141 cd->debugfs_genwqe = debugfs_genwqe; 142 142 143 + /* 144 + * This comes from kernel config option and can be overritten via 145 + * debugfs. 146 + */ 147 + cd->use_platform_recovery = CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY; 148 + 143 149 init_waitqueue_head(&cd->queue_waitq); 144 150 145 151 spin_lock_init(&cd->file_lock); ··· 949 943 return 0; 950 944 951 945 fatal_error: 946 + if (cd->use_platform_recovery) { 947 + /* 948 + * Since we use raw accessors, EEH errors won't be detected 949 + * by the platform until we do a non-raw MMIO or config space 950 + * read 951 + */ 952 + readq(cd->mmio + IO_SLC_CFGREG_GFIR); 953 + 954 + /* We do nothing if the card is going over PCI recovery */ 955 + if (pci_channel_offline(pci_dev)) 956 + return -EIO; 957 + } 958 + 952 959 dev_err(&pci_dev->dev, 953 960 "[%s] card unusable. Please trigger unbind!\n", __func__); 954 961 ··· 1066 1047 1067 1048 pci_set_master(pci_dev); 1068 1049 pci_enable_pcie_error_reporting(pci_dev); 1050 + 1051 + /* EEH recovery requires PCIe fundamental reset */ 1052 + pci_dev->needs_freset = 1; 1069 1053 1070 1054 /* request complete BAR-0 space (length = 0) */ 1071 1055 cd->mmio_len = pci_resource_len(pci_dev, 0); ··· 1208 1186 1209 1187 dev_err(&pci_dev->dev, "[%s] state=%d\n", __func__, state); 1210 1188 1211 - if (pci_dev == NULL) 1212 - return PCI_ERS_RESULT_NEED_RESET; 1213 - 1214 1189 cd = dev_get_drvdata(&pci_dev->dev); 1215 1190 if (cd == NULL) 1216 - return PCI_ERS_RESULT_NEED_RESET; 1191 + return PCI_ERS_RESULT_DISCONNECT; 1217 1192 1218 - switch (state) { 1219 - case pci_channel_io_normal: 1220 - return PCI_ERS_RESULT_CAN_RECOVER; 1221 - case pci_channel_io_frozen: 1193 + /* Stop the card */ 1194 + genwqe_health_check_stop(cd); 1195 + genwqe_stop(cd); 1196 + 1197 + /* 1198 + * On permanent failure, the PCI code will call device remove 1199 + * after the return of this function. 1200 + * genwqe_stop() can be called twice. 1201 + */ 1202 + if (state == pci_channel_io_perm_failure) { 1203 + return PCI_ERS_RESULT_DISCONNECT; 1204 + } else { 1205 + genwqe_pci_remove(cd); 1222 1206 return PCI_ERS_RESULT_NEED_RESET; 1223 - case pci_channel_io_perm_failure: 1207 + } 1208 + } 1209 + 1210 + static pci_ers_result_t genwqe_err_slot_reset(struct pci_dev *pci_dev) 1211 + { 1212 + int rc; 1213 + struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev); 1214 + 1215 + rc = genwqe_pci_setup(cd); 1216 + if (!rc) { 1217 + return PCI_ERS_RESULT_RECOVERED; 1218 + } else { 1219 + dev_err(&pci_dev->dev, 1220 + "err: problems with PCI setup (err=%d)\n", rc); 1224 1221 return PCI_ERS_RESULT_DISCONNECT; 1225 1222 } 1226 - 1227 - return PCI_ERS_RESULT_NEED_RESET; 1228 1223 } 1229 1224 1230 1225 static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev) ··· 1249 1210 return PCI_ERS_RESULT_NONE; 1250 1211 } 1251 1212 1252 - static void genwqe_err_resume(struct pci_dev *dev) 1213 + static void genwqe_err_resume(struct pci_dev *pci_dev) 1253 1214 { 1215 + int rc; 1216 + struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev); 1217 + 1218 + rc = genwqe_start(cd); 1219 + if (!rc) { 1220 + rc = genwqe_health_check_start(cd); 1221 + if (rc) 1222 + dev_err(&pci_dev->dev, 1223 + "err: cannot start health checking! (err=%d)\n", 1224 + rc); 1225 + } else { 1226 + dev_err(&pci_dev->dev, 1227 + "err: cannot start card services! (err=%d)\n", rc); 1228 + } 1254 1229 } 1255 1230 1256 1231 static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) ··· 1287 1234 .error_detected = genwqe_err_error_detected, 1288 1235 .mmio_enabled = genwqe_err_result_none, 1289 1236 .link_reset = genwqe_err_result_none, 1290 - .slot_reset = genwqe_err_result_none, 1237 + .slot_reset = genwqe_err_slot_reset, 1291 1238 .resume = genwqe_err_resume, 1292 1239 }; 1293 1240
+2
drivers/misc/genwqe/card_base.h
··· 291 291 struct task_struct *health_thread; 292 292 wait_queue_head_t health_waitq; 293 293 294 + int use_platform_recovery; /* use platform recovery mechanisms */ 295 + 294 296 /* char device */ 295 297 dev_t devnum_genwqe; /* major/minor num card */ 296 298 struct class *class_genwqe; /* reference to class object */
+19 -5
drivers/misc/genwqe/card_ddcb.c
··· 1118 1118 * safer, but slower for the good-case ... See above. 1119 1119 */ 1120 1120 gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR); 1121 - if ((gfir & GFIR_ERR_TRIGGER) != 0x0) { 1121 + if (((gfir & GFIR_ERR_TRIGGER) != 0x0) && 1122 + !pci_channel_offline(pci_dev)) { 1123 + 1124 + if (cd->use_platform_recovery) { 1125 + /* 1126 + * Since we use raw accessors, EEH errors won't be 1127 + * detected by the platform until we do a non-raw 1128 + * MMIO or config space read 1129 + */ 1130 + readq(cd->mmio + IO_SLC_CFGREG_GFIR); 1131 + 1132 + /* Don't do anything if the PCI channel is frozen */ 1133 + if (pci_channel_offline(pci_dev)) 1134 + goto exit; 1135 + } 1122 1136 1123 1137 wake_up_interruptible(&cd->health_waitq); 1124 1138 ··· 1140 1126 * By default GFIRs causes recovery actions. This 1141 1127 * count is just for debug when recovery is masked. 1142 1128 */ 1143 - printk_ratelimited(KERN_ERR 1144 - "%s %s: [%s] GFIR=%016llx\n", 1145 - GENWQE_DEVNAME, dev_name(&pci_dev->dev), 1146 - __func__, gfir); 1129 + dev_err_ratelimited(&pci_dev->dev, 1130 + "[%s] GFIR=%016llx\n", 1131 + __func__, gfir); 1147 1132 } 1148 1133 1134 + exit: 1149 1135 return IRQ_HANDLED; 1150 1136 } 1151 1137
+7
drivers/misc/genwqe/card_debugfs.c
··· 485 485 goto err1; 486 486 } 487 487 488 + file = debugfs_create_u32("use_platform_recovery", 0666, root, 489 + &cd->use_platform_recovery); 490 + if (!file) { 491 + ret = -ENOMEM; 492 + goto err1; 493 + } 494 + 488 495 cd->debugfs_root = root; 489 496 return 0; 490 497 err1:
+5
drivers/misc/genwqe/card_dev.c
··· 1048 1048 int rc = 0; 1049 1049 struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data; 1050 1050 struct genwqe_dev *cd = cfile->cd; 1051 + struct pci_dev *pci_dev = cd->pci_dev; 1051 1052 struct genwqe_reg_io __user *io; 1052 1053 u64 val; 1053 1054 u32 reg_offs; 1055 + 1056 + /* Return -EIO if card hit EEH */ 1057 + if (pci_channel_offline(pci_dev)) 1058 + return -EIO; 1054 1059 1055 1060 if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE) 1056 1061 return -EINVAL;
+10
drivers/misc/genwqe/card_utils.c
··· 53 53 */ 54 54 int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val) 55 55 { 56 + struct pci_dev *pci_dev = cd->pci_dev; 57 + 56 58 if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) 57 59 return -EIO; 58 60 59 61 if (cd->mmio == NULL) 62 + return -EIO; 63 + 64 + if (pci_channel_offline(pci_dev)) 60 65 return -EIO; 61 66 62 67 __raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs); ··· 104 99 */ 105 100 int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val) 106 101 { 102 + struct pci_dev *pci_dev = cd->pci_dev; 103 + 107 104 if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) 108 105 return -EIO; 109 106 110 107 if (cd->mmio == NULL) 108 + return -EIO; 109 + 110 + if (pci_channel_offline(pci_dev)) 111 111 return -EIO; 112 112 113 113 __raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs);