1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * *********************************************************************** 24 * * ** 25 * * NOTICE ** 26 * * COPYRIGHT (C) 1996-2015 QLOGIC CORPORATION ** 27 * * ALL RIGHTS RESERVED ** 28 * * ** 29 * *********************************************************************** 30 * 31 */ 32 33 #include <ql_apps.h> 34 #include <ql_api.h> 35 #include <ql_fm.h> 36 37 /* Define default impact code */ 38 qlc_fm_ereport_t qlc_fm_ereport_tbl[] = { 39 40 {QL_FM_EREPORT_DMA_ERR, 41 "A DMA direction error", 42 QL_FM_DEVICE_DMA_ERR, 43 DDI_FM_DEVICE_INTERN_CORR, 44 DDI_SERVICE_UNAFFECTED}, 45 46 {QL_FM_EREPORT_BAD_PAYLOAD, 47 "A bad payload detected", 48 QL_FM_DEVICE_BAD_PAYLOAD, 49 DDI_FM_DEVICE_INTERN_CORR, 50 DDI_SERVICE_UNAFFECTED}, 51 52 {QL_FM_EREPORT_CMD_FAILED, 53 "A command failed", 54 QL_FM_DEVICE_CMD_FAILED, 55 DDI_FM_DEVICE_INTERN_CORR, 56 DDI_SERVICE_UNAFFECTED}, 57 58 {QL_FM_EREPORT_CHIP_HANG, 59 "fw is not responding", 60 QL_FM_DEVICE_CHIP_HANG, 61 DDI_FM_DEVICE_INTERN_CORR, 62 DDI_SERVICE_UNAFFECTED}, 63 64 {QL_FM_EREPORT_UNKNOWN, 65 "Unknown error reported", 66 QL_FM_DEVICE_UNKNOWN, 67 DDI_FM_DEVICE_INTERN_CORR, 68 DDI_SERVICE_UNAFFECTED}, 69 70 {QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR, 71 "Async event request transfer error", 72 QL_FM_DEVICE_MBA_REQ_TRANSFER_ERR, 73 DDI_FM_DEVICE_INVAL_STATE, 74 DDI_SERVICE_LOST}, 75 76 {QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR, 77 "Async event response transfer error", 78 QL_FM_DEVICE_MBA_RSP_TRANSFER_ERR, 79 DDI_FM_DEVICE_INVAL_STATE, 80 DDI_SERVICE_LOST}, 81 82 {QL_FM_EREPORT_ACC_HANDLE_CHECK, 83 "ACC handle check return failed", 84 QL_FM_DEVICE_ACC_HANDLE_ERR, 85 DDI_FM_DEVICE_INTERN_UNCORR, 86 DDI_SERVICE_LOST}, 87 88 {QL_FM_EREPORT_DMA_HANDLE_CHECK, 89 "DMA handle check return failed", 90 QL_FM_DEVICE_DMA_HANDLE_ERR, 91 DDI_FM_DEVICE_INTERN_CORR, 92 DDI_SERVICE_UNAFFECTED}, 93 94 /* Reporting Standard I/O controller Errors */ 95 96 /* End of table */ 97 {0, NULL, NULL, NULL, 0}, 98 }; 99 100 101 int 102 qlc_fm_check_acc_handle(ql_adapter_state_t *ha, ddi_acc_handle_t handle) 103 { 104 105 ddi_fm_error_t err; 106 107 if (!DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) { 108 return (DDI_FM_OK); 109 } 110 err.fme_status = DDI_FM_OK; 111 112 ddi_fm_acc_err_get(handle, &err, DDI_FME_VERSION); 113 114 if ((void *)&ddi_fm_acc_err_clear != NULL) 115 (void) ddi_fm_acc_err_clear(handle, DDI_FME_VERSION); 116 117 return (err.fme_status); 118 } 119 120 /*ARGSUSED*/ 121 int 122 qlc_fm_check_dma_handle(ql_adapter_state_t *ha, ddi_dma_handle_t handle) 123 { 124 ddi_fm_error_t err; 125 126 if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) { 127 return (DDI_FM_OK); 128 } 129 130 err.fme_status = DDI_FM_OK; 131 132 ddi_fm_dma_err_get(handle, &err, DDI_FME_VERSION); 133 134 return (err.fme_status); 135 136 } 137 138 139 void 140 qlc_fm_check_pkt_dma_handle(ql_adapter_state_t *ha, ql_srb_t *sb) 141 { 142 fc_packet_t *pkt = sb->pkt; 143 int rval = DDI_FM_OK; 144 145 146 if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) { 147 return; 148 } 149 150 if (pkt->pkt_cmd_acc != NULL && pkt->pkt_cmdlen) { 151 rval = qlc_fm_check_dma_handle(ha, pkt->pkt_cmd_dma); 152 } 153 154 if (pkt->pkt_resp_acc != NULL && rval == DDI_FM_OK && 155 pkt->pkt_rsplen != 0) { 156 rval = qlc_fm_check_dma_handle(ha, pkt->pkt_resp_dma); 157 } 158 159 if (((pkt->pkt_data_acc != NULL) & (rval == DDI_FM_OK) & 160 (pkt->pkt_datalen != 0)) != 0) { 161 rval = qlc_fm_check_dma_handle(ha, pkt->pkt_data_dma); 162 } 163 164 if (rval != DDI_FM_OK) { 165 pkt->pkt_state = FC_PKT_TRAN_ERROR; 166 pkt->pkt_reason = FC_REASON_DMA_ERROR; 167 pkt->pkt_expln = FC_EXPLN_NONE; 168 pkt->pkt_action = FC_ACTION_RETRYABLE; 169 170 (void) qlc_fm_report_err_impact(ha, 171 QL_FM_EREPORT_DMA_HANDLE_CHECK); 172 } 173 174 } 175 176 /* 177 * The IO fault service error handling callback function 178 */ 179 180 /*ARGSUSED*/ 181 int 182 qlc_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) 183 { 184 pci_ereport_post(dip, err, NULL); 185 186 return (err->fme_status); 187 188 } 189 190 /*ARGSUSED*/ 191 void 192 qlc_fm_service_impact(ql_adapter_state_t *ha, int impact) 193 { 194 if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) { 195 return; 196 } 197 198 ddi_fm_service_impact(ha->dip, impact); 199 } 200 201 202 /*ARGSUSED*/ 203 void 204 qlc_fm_init(ql_adapter_state_t *ha) 205 { 206 ddi_iblock_cookie_t iblk; 207 208 if (ha->fm_capabilities == DDI_FM_NOT_CAPABLE) { 209 return; 210 } 211 212 /* 213 * Register capabilities with IO Fault Services. 214 */ 215 if (ha->fm_capabilities) { 216 ddi_fm_init(ha->dip, (int *)&ha->fm_capabilities, &iblk); 217 } 218 219 /* 220 * Initialize pci ereport capabilities if ereport capable 221 * PCI-related errors are automatically detected and reported 222 */ 223 if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) || 224 DDI_FM_ERRCB_CAP(ha->fm_capabilities)) { 225 pci_ereport_setup(ha->dip); 226 } 227 228 /* 229 * Register error callback if error callback capable. 230 */ 231 if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) { 232 ddi_fm_handler_register(ha->dip, 233 qlc_fm_error_cb, (void*)ha); 234 } 235 236 /* 237 * DDI_FLAGERR_ACC indicates: 238 * 1. Driver will check its access handle(s) for faults on 239 * a regular basis by calling ddi_fm_acc_err_get 240 * 2. Driver is able to cope with incorrect results of I/O 241 * operations resulted from an I/O fault. 242 */ 243 if (DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) { 244 ql_dev_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC; 245 } else { 246 ql_dev_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC; 247 } 248 249 /* 250 * per instance based setup only 251 */ 252 if (DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) { 253 ha->bit32_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 254 ha->bit64_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 255 256 } else { 257 ha->bit32_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 258 ha->bit64_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 259 } 260 261 } 262 263 264 void 265 qlc_fm_fini(ql_adapter_state_t *ha) 266 { 267 if (ha->fm_capabilities) { 268 /* 269 * Release any resources allocated by pci_ereport_setup() 270 */ 271 if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) || 272 DDI_FM_ERRCB_CAP(ha->fm_capabilities)) { 273 pci_ereport_teardown(ha->dip); 274 } 275 276 if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) { 277 ddi_fm_handler_unregister(ha->dip); 278 } 279 280 /* Unregister from IO Fault Services */ 281 ddi_fm_fini(ha->dip); 282 } 283 284 } 285 286 287 void 288 qlc_fm_report_err_impact(ql_adapter_state_t *ha, uint32_t fid) 289 { 290 uint64_t ena; 291 char eclass[QL_FM_MAX_CLASS]; 292 qlc_fm_ereport_t *ereport = NULL; 293 294 if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) { 295 return; 296 } 297 298 if (fid > QL_FM_EREPORT_NONE) { 299 cmn_err(CE_NOTE, "Not reported yet"); 300 return; 301 } 302 303 ereport = &qlc_fm_ereport_tbl[fid]; 304 305 /* We already have everything we need in ereport */ 306 (void) snprintf(eclass, QL_FM_MAX_CLASS, "%s.%s", 307 DDI_FM_DEVICE, 308 ereport->gen_eclass); 309 310 ena = fm_ena_generate(0, FM_ENA_FMT1); 311 312 switch (ereport->fid) { 313 case QL_FM_EREPORT_DMA_ERR: 314 case QL_FM_EREPORT_BAD_PAYLOAD: 315 case QL_FM_EREPORT_CMD_FAILED: 316 case QL_FM_EREPORT_CHIP_HANG: 317 case QL_FM_EREPORT_UNKNOWN: 318 case QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR: 319 case QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR: 320 321 ddi_fm_ereport_post(ha->dip, eclass, ena, 322 DDI_NOSLEEP, 323 FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, 324 "Detailed error desc", DATA_TYPE_STRING, ereport->desc, 325 "Instance number", DATA_TYPE_UINT8, ha->instance, 326 NULL); 327 328 break; 329 330 case QL_FM_EREPORT_ACC_HANDLE_CHECK: 331 case QL_FM_EREPORT_DMA_HANDLE_CHECK: 332 /* 333 * Adjust the impact code based on the state 334 * of the device: For example, if check failed 335 * during attach, then impact is DDI_SERVICE_LOST. 336 * 337 * driver's callback qlc_fm_error_cb() registerd will report error. 338 * We only need to report service impact here. 339 */ 340 ddi_fm_ereport_post(ha->dip, eclass, ena, 341 DDI_NOSLEEP, 342 FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, 343 "Detailed error desc", DATA_TYPE_STRING, ereport->desc, 344 "Instance number", DATA_TYPE_UINT8, ha->instance, 345 NULL); 346 347 break; 348 349 default: 350 ddi_fm_ereport_post(ha->dip, eclass, ena, 351 DDI_NOSLEEP, 352 FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL); 353 354 break; 355 } 356 357 qlc_fm_service_impact(ha, ereport->impact_code); 358 } 359