xref: /illumos-gate/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_fm.c (revision 9d9f553dbf21dd5a42309551d1b69fdc837737d0)
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 	(void) ddi_fm_acc_err_clear(handle, DDI_FME_VERSION);
115 
116 	return (err.fme_status);
117 }
118 
119 /*ARGSUSED*/
120 int
qlc_fm_check_dma_handle(ql_adapter_state_t * ha,ddi_dma_handle_t handle)121 qlc_fm_check_dma_handle(ql_adapter_state_t *ha, ddi_dma_handle_t handle)
122 {
123 	ddi_fm_error_t err;
124 
125 	if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
126 		return (DDI_FM_OK);
127 	}
128 
129 	err.fme_status = DDI_FM_OK;
130 
131 	ddi_fm_dma_err_get(handle, &err, DDI_FME_VERSION);
132 
133 	return (err.fme_status);
134 
135 }
136 
137 
138 void
qlc_fm_check_pkt_dma_handle(ql_adapter_state_t * ha,ql_srb_t * sb)139 qlc_fm_check_pkt_dma_handle(ql_adapter_state_t *ha, ql_srb_t *sb)
140 {
141 	fc_packet_t	*pkt = sb->pkt;
142 	int		rval = DDI_FM_OK;
143 
144 
145 	if (!DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
146 		return;
147 	}
148 
149 	if (pkt->pkt_cmd_acc != NULL && pkt->pkt_cmdlen) {
150 		rval = qlc_fm_check_dma_handle(ha, pkt->pkt_cmd_dma);
151 	}
152 
153 	if (pkt->pkt_resp_acc != NULL && rval == DDI_FM_OK &&
154 	    pkt->pkt_rsplen != 0) {
155 		rval = qlc_fm_check_dma_handle(ha, pkt->pkt_resp_dma);
156 	}
157 
158 	if (((pkt->pkt_data_acc != NULL) & (rval == DDI_FM_OK) &
159 	    (pkt->pkt_datalen != 0)) != 0) {
160 		rval = qlc_fm_check_dma_handle(ha, pkt->pkt_data_dma);
161 	}
162 
163 	if (rval != DDI_FM_OK) {
164 		pkt->pkt_state = FC_PKT_TRAN_ERROR;
165 		pkt->pkt_reason = FC_REASON_DMA_ERROR;
166 		pkt->pkt_expln = FC_EXPLN_NONE;
167 		pkt->pkt_action = FC_ACTION_RETRYABLE;
168 
169 		(void) qlc_fm_report_err_impact(ha,
170 		    QL_FM_EREPORT_DMA_HANDLE_CHECK);
171 	}
172 
173 }
174 
175 /*
176  * The IO fault service error handling callback function
177  */
178 
179 /*ARGSUSED*/
180 int
qlc_fm_error_cb(dev_info_t * dip,ddi_fm_error_t * err,const void * impl_data)181 qlc_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
182 {
183 	pci_ereport_post(dip, err, NULL);
184 
185 	return (err->fme_status);
186 
187 }
188 
189 /*ARGSUSED*/
190 void
qlc_fm_service_impact(ql_adapter_state_t * ha,int impact)191 qlc_fm_service_impact(ql_adapter_state_t *ha, int impact)
192 {
193 	if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) {
194 		return;
195 	}
196 
197 	ddi_fm_service_impact(ha->dip, impact);
198 }
199 
200 
201 /*ARGSUSED*/
202 void
qlc_fm_init(ql_adapter_state_t * ha)203 qlc_fm_init(ql_adapter_state_t *ha)
204 {
205 	ddi_iblock_cookie_t iblk;
206 
207 	if (ha->fm_capabilities == DDI_FM_NOT_CAPABLE) {
208 		return;
209 	}
210 
211 	/*
212 	 * Register capabilities with IO Fault Services.
213 	 */
214 	if (ha->fm_capabilities) {
215 		ddi_fm_init(ha->dip, (int *)&ha->fm_capabilities, &iblk);
216 	}
217 
218 	/*
219 	 * Initialize pci ereport capabilities if ereport capable
220 	 * PCI-related errors are automatically detected and reported
221 	 */
222 	if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) ||
223 	    DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
224 		pci_ereport_setup(ha->dip);
225 	}
226 
227 	/*
228 	 * Register error callback if error callback capable.
229 	 */
230 	if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
231 		ddi_fm_handler_register(ha->dip,
232 		    qlc_fm_error_cb, (void*)ha);
233 	}
234 
235 	/*
236 	 * DDI_FLAGERR_ACC indicates:
237 	 * 1. Driver will check its access handle(s) for faults on
238 	 *    a regular basis by calling ddi_fm_acc_err_get
239 	 * 2. Driver is able to cope with incorrect results of I/O
240 	 *    operations resulted from an I/O fault.
241 	 */
242 	if (DDI_FM_ACC_ERR_CAP(ha->fm_capabilities)) {
243 		ql_dev_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC;
244 	} else {
245 		ql_dev_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC;
246 	}
247 
248 	/*
249 	 * per instance based setup only
250 	 */
251 	if (DDI_FM_DMA_ERR_CAP(ha->fm_capabilities)) {
252 		ha->bit32_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
253 		ha->bit64_io_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
254 
255 	} else {
256 		ha->bit32_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
257 		ha->bit64_io_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
258 	}
259 
260 }
261 
262 
263 void
qlc_fm_fini(ql_adapter_state_t * ha)264 qlc_fm_fini(ql_adapter_state_t *ha)
265 {
266 	if (ha->fm_capabilities) {
267 		/*
268 		 * Release any resources allocated by pci_ereport_setup()
269 		 */
270 		if (DDI_FM_EREPORT_CAP(ha->fm_capabilities) ||
271 		    DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
272 			pci_ereport_teardown(ha->dip);
273 		}
274 
275 		if (DDI_FM_ERRCB_CAP(ha->fm_capabilities)) {
276 			ddi_fm_handler_unregister(ha->dip);
277 		}
278 
279 		/* Unregister from IO Fault Services */
280 		ddi_fm_fini(ha->dip);
281 	}
282 
283 }
284 
285 
286 void
qlc_fm_report_err_impact(ql_adapter_state_t * ha,uint32_t fid)287 qlc_fm_report_err_impact(ql_adapter_state_t *ha, uint32_t fid)
288 {
289 	uint64_t ena;
290 	char eclass[QL_FM_MAX_CLASS];
291 	qlc_fm_ereport_t *ereport = NULL;
292 
293 	if (!DDI_FM_EREPORT_CAP(ha->fm_capabilities)) {
294 		return;
295 	}
296 
297 	if (fid > QL_FM_EREPORT_NONE) {
298 		cmn_err(CE_NOTE, "Not reported yet");
299 		return;
300 	}
301 
302 	ereport = &qlc_fm_ereport_tbl[fid];
303 
304 	/* We already have everything we need in ereport */
305 	(void) snprintf(eclass, QL_FM_MAX_CLASS, "%s.%s",
306 	    DDI_FM_DEVICE,
307 	    ereport->gen_eclass);
308 
309 	ena = fm_ena_generate(0, FM_ENA_FMT1);
310 
311 	switch (ereport->fid) {
312 	case QL_FM_EREPORT_DMA_ERR:
313 	case QL_FM_EREPORT_BAD_PAYLOAD:
314 	case QL_FM_EREPORT_CMD_FAILED:
315 	case QL_FM_EREPORT_CHIP_HANG:
316 	case QL_FM_EREPORT_UNKNOWN:
317 	case QL_FM_EREPORT_MBA_REQ_TRANSFER_ERR:
318 	case QL_FM_EREPORT_MBA_RSP_TRANSFER_ERR:
319 
320 		ddi_fm_ereport_post(ha->dip, eclass, ena,
321 		    DDI_NOSLEEP,
322 		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
323 		    "Detailed error desc", DATA_TYPE_STRING, ereport->desc,
324 		    "Instance number", DATA_TYPE_UINT8, ha->instance,
325 		    NULL);
326 
327 		break;
328 
329 	case QL_FM_EREPORT_ACC_HANDLE_CHECK:
330 	case QL_FM_EREPORT_DMA_HANDLE_CHECK:
331 	/*
332 	 * Adjust the impact code based on the state
333 	 * of the device: For example, if check failed
334 	 * during attach, then impact is DDI_SERVICE_LOST.
335 	 *
336 	 * driver's callback qlc_fm_error_cb() registerd will report error.
337 	 * We only need to report service impact here.
338 	 */
339 		ddi_fm_ereport_post(ha->dip, eclass, ena,
340 		    DDI_NOSLEEP,
341 		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
342 		    "Detailed error desc", DATA_TYPE_STRING, ereport->desc,
343 		    "Instance number", DATA_TYPE_UINT8, ha->instance,
344 		    NULL);
345 
346 		break;
347 
348 	default:
349 		ddi_fm_ereport_post(ha->dip, eclass, ena,
350 		    DDI_NOSLEEP,
351 		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
352 
353 		break;
354 	}
355 
356 	qlc_fm_service_impact(ha, ereport->impact_code);
357 }
358