xref: /illumos-gate/usr/src/uts/common/io/sfxge/sfxge_mcdi.c (revision 2a6e99a0f1f7d22c0396e8b2ce9b9babbd1056cf)
1 /*
2  * Copyright (c) 2009-2016 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/stream.h>
35 #include <sys/dlpi.h>
36 #include <sys/pci.h>
37 
38 #include "sfxge.h"
39 #include "efsys.h"
40 #include "efx.h"
41 #include "efx_mcdi.h"
42 #include "efx_regs_mcdi.h"
43 
44 /* MAC DMA attributes */
45 static ddi_device_acc_attr_t sfxge_mcdi_devacc = {
46 
47 	DDI_DEVICE_ATTR_V0,	/* devacc_attr_version */
48 	DDI_NEVERSWAP_ACC,	/* devacc_attr_endian_flags */
49 	DDI_STRICTORDER_ACC	/* devacc_attr_dataorder */
50 };
51 
52 static ddi_dma_attr_t sfxge_mcdi_dma_attr = {
53 	DMA_ATTR_V0,		/* dma_attr_version	*/
54 	0,			/* dma_attr_addr_lo	*/
55 	0xffffffffffffffffull,	/* dma_attr_addr_hi	*/
56 	0xffffffffffffffffull,	/* dma_attr_count_max	*/
57 	0x1000,			/* dma_attr_align	*/
58 	0xffffffff,		/* dma_attr_burstsizes	*/
59 	1,			/* dma_attr_minxfer	*/
60 	0xffffffffffffffffull,	/* dma_attr_maxxfer	*/
61 	0xffffffffffffffffull,	/* dma_attr_seg		*/
62 	1,			/* dma_attr_sgllen	*/
63 	1,			/* dma_attr_granular	*/
64 	0			/* dma_attr_flags	*/
65 };
66 
67 /*
68  * Notes on MCDI operation:
69  * ------------------------
70  * MCDI requests can be made in arbitrary thread context, and as a synchronous
71  * API must therefore block until the response is available from the MC, or
72  * a watchdog timeout occurs.
73  *
74  * This interacts badly with the limited number of worker threads (2 per CPU)
75  * used by the Solaris callout subsystem to invoke timeout handlers. If both
76  * worker threads are blocked (e.g. waiting for a condvar or mutex) then timeout
77  * processing is deadlocked on that CPU, causing system failure.
78  *
79  * For this reason the driver does not use event based MCDI completion, as this
80  * leads to numerous paths involving timeouts and reentrant GLDv3 entrypoints
81  * that result in a deadlocked system.
82  */
83 #define	SFXGE_MCDI_POLL_INTERVAL	10		/* 10us in 1us units */
84 #define	SFXGE_MCDI_WATCHDOG_INTERVAL	10000000	/* 10s in 1us units */
85 
86 
87 /* Acquire exclusive access to MCDI for the duration of a request */
88 static void
89 sfxge_mcdi_acquire(sfxge_mcdi_t *smp)
90 {
91 	mutex_enter(&(smp->sm_lock));
92 	ASSERT3U(smp->sm_state, !=, SFXGE_MCDI_UNINITIALIZED);
93 
94 	while (smp->sm_state != SFXGE_MCDI_INITIALIZED) {
95 		(void) cv_wait(&(smp->sm_kv), &(smp->sm_lock));
96 	}
97 	smp->sm_state = SFXGE_MCDI_BUSY;
98 
99 	mutex_exit(&(smp->sm_lock));
100 }
101 
102 
103 /* Release ownership of MCDI on request completion */
104 static void
105 sfxge_mcdi_release(sfxge_mcdi_t *smp)
106 {
107 	mutex_enter(&(smp->sm_lock));
108 	ASSERT((smp->sm_state == SFXGE_MCDI_BUSY) ||
109 	    (smp->sm_state == SFXGE_MCDI_COMPLETED));
110 
111 	smp->sm_state = SFXGE_MCDI_INITIALIZED;
112 	cv_broadcast(&(smp->sm_kv));
113 
114 	mutex_exit(&(smp->sm_lock));
115 }
116 
117 
118 static void
119 sfxge_mcdi_timeout(sfxge_t *sp)
120 {
121 	dev_info_t *dip = sp->s_dip;
122 
123 	dev_err(dip, CE_WARN, SFXGE_CMN_ERR "MC_TIMEOUT");
124 
125 	DTRACE_PROBE(mcdi_timeout);
126 	(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR,
127 	    "MCDI timeout", 0);
128 }
129 
130 
131 static void
132 sfxge_mcdi_poll(sfxge_t *sp)
133 {
134 	efx_nic_t *enp = sp->s_enp;
135 	clock_t timeout;
136 	boolean_t aborted;
137 
138 	/* Poll until request completes or timeout */
139 	timeout = ddi_get_lbolt() + drv_usectohz(SFXGE_MCDI_WATCHDOG_INTERVAL);
140 	while (efx_mcdi_request_poll(enp) == B_FALSE) {
141 
142 		/* No response received yet */
143 		if (ddi_get_lbolt() > timeout) {
144 			/* Timeout expired */
145 			goto fail;
146 		}
147 
148 		/* Short delay to avoid excessive PCIe traffic */
149 		drv_usecwait(SFXGE_MCDI_POLL_INTERVAL);
150 	}
151 
152 	/* Request completed (or polling failed) */
153 	return;
154 
155 fail:
156 	/* Timeout before request completion */
157 	DTRACE_PROBE(fail);
158 	aborted = efx_mcdi_request_abort(enp);
159 	ASSERT(aborted);
160 	sfxge_mcdi_timeout(sp);
161 }
162 
163 
164 static void
165 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
166 {
167 	sfxge_t *sp = (sfxge_t *)arg;
168 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
169 
170 	sfxge_mcdi_acquire(smp);
171 
172 	/* Issue request and poll for completion */
173 	efx_mcdi_request_start(sp->s_enp, emrp, B_FALSE);
174 	sfxge_mcdi_poll(sp);
175 
176 	sfxge_mcdi_release(smp);
177 }
178 
179 
180 static void
181 sfxge_mcdi_ev_cpl(void *arg)
182 {
183 	sfxge_t *sp = (sfxge_t *)arg;
184 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
185 
186 	mutex_enter(&(smp->sm_lock));
187 	ASSERT(smp->sm_state == SFXGE_MCDI_BUSY);
188 	smp->sm_state = SFXGE_MCDI_COMPLETED;
189 	cv_broadcast(&(smp->sm_kv));
190 	mutex_exit(&(smp->sm_lock));
191 }
192 
193 
194 static void
195 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
196 {
197 	sfxge_t *sp = (sfxge_t *)arg;
198 	const char *reason;
199 
200 	if (eme == EFX_MCDI_EXCEPTION_MC_REBOOT)
201 		reason = "MC_REBOOT";
202 	else if (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT)
203 		reason = "MC_BADASSERT";
204 	else
205 		reason = "MC_UNKNOWN";
206 
207 	DTRACE_PROBE(mcdi_exception);
208 	/* sfxge_evq_t->se_lock held */
209 	(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR, reason, 0);
210 }
211 
212 #if EFSYS_OPT_MCDI_LOGGING
213 #define	SFXGE_MCDI_LOG_BUF_SIZE	128
214 
215 static size_t
216 sfxge_mcdi_do_log(char *buffer, void *data, size_t data_size,
217     size_t pfxsize, size_t position)
218 {
219 	uint32_t *words = data;
220 	size_t i;
221 
222 	for (i = 0; i < data_size; i += sizeof (*words)) {
223 		if (position + 2 * sizeof (*words) + 1 >=
224 		    SFXGE_MCDI_LOG_BUF_SIZE) {
225 			buffer[position] = '\0';
226 			cmn_err(CE_NOTE, "%s \\", buffer);
227 			position = pfxsize;
228 		}
229 		snprintf(buffer + position, SFXGE_MCDI_LOG_BUF_SIZE - position,
230 		    " %08x", *words);
231 		words++;
232 		position += 2 * sizeof (uint32_t) + 1;
233 	}
234 	return (position);
235 }
236 
237 
238 static void
239 sfxge_mcdi_logger(void *arg, efx_log_msg_t type,
240     void *header, size_t header_size, void *data, size_t data_size)
241 {
242 	sfxge_t *sp = (sfxge_t *)arg;
243 	char buffer[SFXGE_MCDI_LOG_BUF_SIZE];
244 	size_t pfxsize;
245 	size_t start;
246 
247 	if (!sp->s_mcdi_logging)
248 		return;
249 
250 	pfxsize = snprintf(buffer, sizeof (buffer),
251 	    "sfc %04x:%02x:%02x.%02x %s%d MCDI RPC %s:",
252 	    0,
253 	    PCI_REG_BUS_G(sp->s_bus_addr),
254 	    PCI_REG_DEV_G(sp->s_bus_addr),
255 	    PCI_REG_FUNC_G(sp->s_bus_addr),
256 	    ddi_driver_name(sp->s_dip),
257 	    ddi_get_instance(sp->s_dip),
258 	    type == EFX_LOG_MCDI_REQUEST ? "REQ" :
259 	    type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
260 	start = sfxge_mcdi_do_log(buffer, header, header_size,
261 	    pfxsize, pfxsize);
262 	start = sfxge_mcdi_do_log(buffer, data, data_size, pfxsize, start);
263 	if (start != pfxsize) {
264 		buffer[start] = '\0';
265 		cmn_err(CE_NOTE, "%s", buffer);
266 	}
267 }
268 #endif /* EFSYS_OPT_MCDI_LOGGING */
269 
270 int
271 sfxge_mcdi_init(sfxge_t *sp)
272 {
273 	efx_nic_t *enp = sp->s_enp;
274 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
275 	efsys_mem_t *esmp = &(smp->sm_mem);
276 	efx_mcdi_transport_t *emtp = &(smp->sm_emt);
277 	sfxge_dma_buffer_attr_t dma_attr;
278 	int msg_buf_size;
279 	int rc;
280 
281 	ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_UNINITIALIZED);
282 
283 	msg_buf_size = sizeof (uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
284 
285 	/* Allocate host DMA buffer for MCDI commands */
286 	dma_attr.sdba_dip	 = sp->s_dip;
287 	dma_attr.sdba_dattrp	 = &sfxge_mcdi_dma_attr;
288 	dma_attr.sdba_callback	 = DDI_DMA_SLEEP;
289 	dma_attr.sdba_length	 = msg_buf_size;
290 	dma_attr.sdba_memflags	 = DDI_DMA_CONSISTENT;
291 	dma_attr.sdba_devaccp	 = &sfxge_mcdi_devacc;
292 	dma_attr.sdba_bindflags	 = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
293 	dma_attr.sdba_maxcookies = 1;
294 	dma_attr.sdba_zeroinit	 = B_TRUE;
295 
296 	if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0)
297 		goto fail1;
298 
299 	mutex_init(&(smp->sm_lock), NULL, MUTEX_DRIVER, NULL);
300 
301 	smp->sm_state = SFXGE_MCDI_INITIALIZED;
302 
303 	emtp->emt_context   = sp;
304 	emtp->emt_dma_mem   = esmp;
305 	emtp->emt_execute   = sfxge_mcdi_execute;
306 	emtp->emt_ev_cpl    = sfxge_mcdi_ev_cpl;
307 	emtp->emt_exception = sfxge_mcdi_exception;
308 #if EFSYS_OPT_MCDI_LOGGING
309 	emtp->emt_logger    = sfxge_mcdi_logger;
310 #endif
311 
312 	cv_init(&(smp->sm_kv), NULL, CV_DRIVER, NULL);
313 
314 	if ((rc = efx_mcdi_init(enp, emtp)) != 0)
315 		goto fail2;
316 
317 	return (0);
318 
319 fail2:
320 	DTRACE_PROBE(fail2);
321 
322 	cv_destroy(&(smp->sm_kv));
323 	mutex_destroy(&(smp->sm_lock));
324 
325 	sfxge_dma_buffer_destroy(esmp);
326 
327 	smp->sm_state = SFXGE_MCDI_UNINITIALIZED;
328 	smp->sm_sp = NULL;
329 	SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t);
330 
331 fail1:
332 	DTRACE_PROBE1(fail1, int, rc);
333 
334 	return (rc);
335 }
336 
337 
338 void
339 sfxge_mcdi_fini(sfxge_t *sp)
340 {
341 	efx_nic_t *enp = sp->s_enp;
342 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
343 	efsys_mem_t *esmp = &(smp->sm_mem);
344 	efx_mcdi_transport_t *emtp;
345 
346 	mutex_enter(&(smp->sm_lock));
347 	ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_INITIALIZED);
348 
349 	efx_mcdi_fini(enp);
350 	emtp = &(smp->sm_emt);
351 	bzero(emtp, sizeof (*emtp));
352 
353 	smp->sm_sp = NULL;
354 
355 	cv_destroy(&(smp->sm_kv));
356 	mutex_exit(&(smp->sm_lock));
357 
358 	sfxge_dma_buffer_destroy(esmp);
359 
360 	mutex_destroy(&(smp->sm_lock));
361 
362 	smp->sm_state = SFXGE_MCDI_UNINITIALIZED;
363 	SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t);
364 }
365 
366 
367 int
368 sfxge_mcdi_ioctl(sfxge_t *sp, sfxge_mcdi_ioc_t *smip)
369 {
370 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp);
371 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
372 	efx_mcdi_req_t emr;
373 	uint8_t *out;
374 	int rc;
375 
376 	if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) {
377 		rc = ENODEV;
378 		goto fail1;
379 	}
380 
381 	if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
382 		rc = ENOTSUP;
383 		goto fail2;
384 	}
385 
386 	out = kmem_zalloc(sizeof (smip->smi_payload), KM_NOSLEEP);
387 	if (out == NULL) {
388 		rc = ENOMEM;
389 		goto fail3;
390 	}
391 
392 	emr.emr_cmd = smip->smi_cmd;
393 	emr.emr_in_buf = smip->smi_payload;
394 	emr.emr_in_length = smip->smi_len;
395 
396 	emr.emr_out_buf = out;
397 	emr.emr_out_length = sizeof (smip->smi_payload);
398 
399 	sfxge_mcdi_execute(sp, &emr);
400 
401 	smip->smi_rc = (uint8_t)emr.emr_rc;
402 	smip->smi_cmd = (uint8_t)emr.emr_cmd;
403 	smip->smi_len = (uint8_t)emr.emr_out_length_used;
404 	bcopy(out, smip->smi_payload, smip->smi_len);
405 
406 	/*
407 	 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
408 	 * Both ports will see ->emt_exception callbacks on the next MCDI poll
409 	 */
410 	if (smip->smi_cmd == MC_CMD_REBOOT) {
411 
412 		DTRACE_PROBE(mcdi_ioctl_mc_reboot);
413 		/* sfxge_t->s_state_lock held */
414 		(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK,
415 		    "MC_REBOOT triggering restart", 0);
416 	}
417 
418 	kmem_free(out, sizeof (smip->smi_payload));
419 
420 	return (0);
421 
422 fail3:
423 	DTRACE_PROBE(fail3);
424 fail2:
425 	DTRACE_PROBE(fail2);
426 fail1:
427 	DTRACE_PROBE1(fail1, int, rc);
428 	return (rc);
429 }
430 
431 int
432 sfxge_mcdi2_ioctl(sfxge_t *sp, sfxge_mcdi2_ioc_t *smip)
433 {
434 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp);
435 	sfxge_mcdi_t *smp = &(sp->s_mcdi);
436 	efx_mcdi_req_t emr;
437 	uint8_t *out;
438 	int rc;
439 
440 	if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) {
441 		rc = ENODEV;
442 		goto fail1;
443 	}
444 
445 	if (!(encp->enc_features & EFX_FEATURE_MCDI)) {
446 		rc = ENOTSUP;
447 		goto fail2;
448 	}
449 
450 	out = kmem_zalloc(sizeof (smip->smi_payload), KM_NOSLEEP);
451 	if (out == NULL) {
452 		rc = ENOMEM;
453 		goto fail3;
454 	}
455 
456 	emr.emr_cmd = smip->smi_cmd;
457 	emr.emr_in_buf = smip->smi_payload;
458 	emr.emr_in_length = smip->smi_len;
459 
460 	emr.emr_out_buf = out;
461 	emr.emr_out_length = sizeof (smip->smi_payload);
462 
463 	sfxge_mcdi_execute(sp, &emr);
464 
465 	smip->smi_rc = emr.emr_rc;
466 	smip->smi_cmd = emr.emr_cmd;
467 	smip->smi_len = (uint32_t)emr.emr_out_length_used;
468 	bcopy(out, smip->smi_payload, smip->smi_len);
469 
470 	/*
471 	 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT
472 	 * Both ports will see ->emt_exception callbacks on the next MCDI poll
473 	 */
474 	if (smip->smi_cmd == MC_CMD_REBOOT) {
475 
476 		DTRACE_PROBE(mcdi_ioctl_mc_reboot);
477 		/* sfxge_t->s_state_lock held */
478 		(void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK,
479 		    "MC_REBOOT triggering restart", 0);
480 	}
481 
482 	kmem_free(out, sizeof (smip->smi_payload));
483 
484 	return (0);
485 
486 fail3:
487 	DTRACE_PROBE(fail3);
488 fail2:
489 	DTRACE_PROBE(fail2);
490 fail1:
491 	DTRACE_PROBE1(fail1, int, rc);
492 	return (rc);
493 }
494