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
qlc_fm_check_acc_handle(ql_adapter_state_t * ha,ddi_acc_handle_t handle)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
qlc_fm_check_dma_handle(ql_adapter_state_t * ha,ddi_dma_handle_t handle)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
qlc_fm_check_pkt_dma_handle(ql_adapter_state_t * ha,ql_srb_t * sb)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
qlc_fm_error_cb(dev_info_t * dip,ddi_fm_error_t * err,const void * impl_data)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
qlc_fm_service_impact(ql_adapter_state_t * ha,int impact)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
qlc_fm_init(ql_adapter_state_t * ha)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
qlc_fm_fini(ql_adapter_state_t * ha)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
qlc_fm_report_err_impact(ql_adapter_state_t * ha,uint32_t fid)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