1 /* 2 * Copyright (c) 2009-2015 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 }