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
sfxge_mcdi_acquire(sfxge_mcdi_t * smp)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
sfxge_mcdi_release(sfxge_mcdi_t * smp)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
sfxge_mcdi_timeout(sfxge_t * sp)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
sfxge_mcdi_poll(sfxge_t * sp)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
sfxge_mcdi_execute(void * arg,efx_mcdi_req_t * emrp)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
sfxge_mcdi_ev_cpl(void * arg)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
sfxge_mcdi_exception(void * arg,efx_mcdi_exception_t eme)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
sfxge_mcdi_do_log(char * buffer,void * data,size_t data_size,size_t pfxsize,size_t position)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
sfxge_mcdi_logger(void * arg,efx_log_msg_t type,void * header,size_t header_size,void * data,size_t data_size)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
sfxge_mcdi_init(sfxge_t * sp)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
sfxge_mcdi_fini(sfxge_t * sp)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
sfxge_mcdi_ioctl(sfxge_t * sp,sfxge_mcdi_ioc_t * smip)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
sfxge_mcdi2_ioctl(sfxge_t * sp,sfxge_mcdi2_ioc_t * smip)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