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