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 * Copyright 2009 Solarflare Communications Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/ddi.h> 29 #include <sys/sunddi.h> 30 #include <sys/stream.h> 31 #include <sys/dlpi.h> 32 33 #include "sfxge.h" 34 #include "efsys.h" 35 #include "efx.h" 36 #include "efx_mcdi.h" 37 #include "efx_regs_mcdi.h" 38 39 40 /* 41 * Notes on MCDI operation: 42 * ------------------------ 43 * MCDI requests can be made in arbitrary thread context, and as a synchronous 44 * API must therefore block until the response is available from the MC, or 45 * a watchdog timeout occurs. 46 * 47 * This interacts badly with the limited number of worker threads (2 per CPU) 48 * used by the Solaris callout subsystem to invoke timeout handlers. If both 49 * worker threads are blocked (e.g. waiting for a condvar or mutex) then timeout 50 * processing is deadlocked on that CPU, causing system failure. 51 * 52 * For this reason the driver does not use event based MCDI completion, as this 53 * leads to numerous paths involving timeouts and reentrant GLDv3 entrypoints 54 * that result in a deadlocked system. 55 */ 56 #define SFXGE_MCDI_POLL_INTERVAL 10 /* 10us in 1us units */ 57 #define SFXGE_MCDI_WATCHDOG_INTERVAL 10000000 /* 10s in 1us units */ 58 59 60 /* Acquire exclusive access to MCDI for the duration of a request */ 61 static void 62 sfxge_mcdi_acquire(sfxge_mcdi_t *smp) 63 { 64 mutex_enter(&(smp->sm_lock)); 65 ASSERT3U(smp->sm_state, !=, SFXGE_MCDI_UNINITIALIZED); 66 67 while (smp->sm_state != SFXGE_MCDI_INITIALIZED) { 68 (void) cv_wait_sig(&(smp->sm_kv), &(smp->sm_lock)); 69 } 70 smp->sm_state = SFXGE_MCDI_BUSY; 71 72 mutex_exit(&(smp->sm_lock)); 73 } 74 75 76 /* Release ownership of MCDI on request completion */ 77 static void 78 sfxge_mcdi_release(sfxge_mcdi_t *smp) 79 { 80 mutex_enter(&(smp->sm_lock)); 81 ASSERT((smp->sm_state == SFXGE_MCDI_BUSY) || 82 (smp->sm_state == SFXGE_MCDI_COMPLETED)); 83 84 smp->sm_state = SFXGE_MCDI_INITIALIZED; 85 cv_broadcast(&(smp->sm_kv)); 86 87 mutex_exit(&(smp->sm_lock)); 88 } 89 90 91 static void 92 sfxge_mcdi_timeout(sfxge_t *sp) 93 { 94 dev_info_t *dip = sp->s_dip; 95 96 cmn_err(CE_WARN, SFXGE_CMN_ERR "[%s%d] MC_TIMEOUT", 97 ddi_driver_name(dip), ddi_get_instance(dip)); 98 99 DTRACE_PROBE(mcdi_timeout); 100 (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR, 101 "MCDI timeout", 0); 102 } 103 104 105 static void 106 sfxge_mcdi_poll(sfxge_t *sp) 107 { 108 efx_nic_t *enp = sp->s_enp; 109 clock_t timeout; 110 boolean_t aborted; 111 112 /* Poll until request completes or timeout */ 113 timeout = ddi_get_lbolt() + drv_usectohz(SFXGE_MCDI_WATCHDOG_INTERVAL); 114 while (efx_mcdi_request_poll(enp) == B_FALSE) { 115 116 /* No response received yet */ 117 if (ddi_get_lbolt() > timeout) { 118 /* Timeout expired */ 119 goto fail; 120 } 121 122 /* Short delay to avoid excessive PCIe traffic */ 123 drv_usecwait(SFXGE_MCDI_POLL_INTERVAL); 124 } 125 126 /* Request completed (or polling failed) */ 127 return; 128 129 fail: 130 /* Timeout before request completion */ 131 DTRACE_PROBE(fail); 132 aborted = efx_mcdi_request_abort(enp); 133 ASSERT(aborted); 134 sfxge_mcdi_timeout(sp); 135 } 136 137 138 static void 139 sfxge_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 140 { 141 sfxge_t *sp = (sfxge_t *)arg; 142 sfxge_mcdi_t *smp = &(sp->s_mcdi); 143 144 sfxge_mcdi_acquire(smp); 145 146 /* Issue request and poll for completion */ 147 efx_mcdi_request_start(sp->s_enp, emrp, B_FALSE); 148 sfxge_mcdi_poll(sp); 149 150 sfxge_mcdi_release(smp); 151 } 152 153 154 static void 155 sfxge_mcdi_ev_cpl(void *arg) 156 { 157 sfxge_t *sp = (sfxge_t *)arg; 158 sfxge_mcdi_t *smp = &(sp->s_mcdi); 159 160 mutex_enter(&(smp->sm_lock)); 161 ASSERT(smp->sm_state == SFXGE_MCDI_BUSY); 162 smp->sm_state = SFXGE_MCDI_COMPLETED; 163 cv_broadcast(&(smp->sm_kv)); 164 mutex_exit(&(smp->sm_lock)); 165 } 166 167 168 static void 169 sfxge_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 170 { 171 sfxge_t *sp = (sfxge_t *)arg; 172 const char *reason; 173 174 if (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) 175 reason = "MC_REBOOT"; 176 else if (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) 177 reason = "MC_BADASSERT"; 178 else 179 reason = "MC_UNKNOWN"; 180 181 DTRACE_PROBE(mcdi_exception); 182 /* sfxge_evq_t->se_lock held */ 183 (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_ERR, reason, 0); 184 } 185 186 187 int 188 sfxge_mcdi_init(sfxge_t *sp) 189 { 190 efx_nic_t *enp = sp->s_enp; 191 sfxge_mcdi_t *smp = &(sp->s_mcdi); 192 efx_mcdi_transport_t *emtp = &(smp->sm_emt); 193 int rc; 194 195 ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_UNINITIALIZED); 196 197 mutex_init(&(smp->sm_lock), NULL, MUTEX_DRIVER, NULL); 198 199 smp->sm_state = SFXGE_MCDI_INITIALIZED; 200 201 emtp->emt_context = sp; 202 emtp->emt_execute = sfxge_mcdi_execute; 203 emtp->emt_ev_cpl = sfxge_mcdi_ev_cpl; 204 emtp->emt_exception = sfxge_mcdi_exception; 205 206 cv_init(&(smp->sm_kv), NULL, CV_DRIVER, NULL); 207 208 if ((rc = efx_mcdi_init(enp, emtp)) != 0) 209 goto fail1; 210 211 return (0); 212 213 fail1: 214 DTRACE_PROBE1(fail1, int, rc); 215 216 cv_destroy(&(smp->sm_kv)); 217 mutex_destroy(&(smp->sm_lock)); 218 219 smp->sm_state = SFXGE_MCDI_UNINITIALIZED; 220 smp->sm_sp = NULL; 221 SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t); 222 223 return (rc); 224 } 225 226 227 void 228 sfxge_mcdi_fini(sfxge_t *sp) 229 { 230 efx_nic_t *enp = sp->s_enp; 231 sfxge_mcdi_t *smp = &(sp->s_mcdi); 232 efx_mcdi_transport_t *emtp; 233 234 mutex_enter(&(smp->sm_lock)); 235 ASSERT3U(smp->sm_state, ==, SFXGE_MCDI_INITIALIZED); 236 237 efx_mcdi_fini(enp); 238 emtp = &(smp->sm_emt); 239 bzero(emtp, sizeof (*emtp)); 240 241 smp->sm_sp = NULL; 242 243 cv_destroy(&(smp->sm_kv)); 244 mutex_exit(&(smp->sm_lock)); 245 246 mutex_destroy(&(smp->sm_lock)); 247 248 smp->sm_state = SFXGE_MCDI_UNINITIALIZED; 249 SFXGE_OBJ_CHECK(smp, sfxge_mcdi_t); 250 } 251 252 253 int 254 sfxge_mcdi_ioctl(sfxge_t *sp, sfxge_mcdi_ioc_t *smip) 255 { 256 const efx_nic_cfg_t *encp = efx_nic_cfg_get(sp->s_enp); 257 sfxge_mcdi_t *smp = &(sp->s_mcdi); 258 efx_mcdi_req_t emr; 259 uint8_t *out; 260 int rc; 261 262 if (smp->sm_state == SFXGE_MCDI_UNINITIALIZED) { 263 rc = ENODEV; 264 goto fail1; 265 } 266 267 if (!(encp->enc_features & EFX_FEATURE_MCDI)) { 268 rc = ENOTSUP; 269 goto fail2; 270 } 271 272 if ((out = kmem_zalloc(sizeof (smip->smi_payload), KM_SLEEP)) == NULL) { 273 rc = ENOMEM; 274 goto fail3; 275 } 276 277 emr.emr_cmd = smip->smi_cmd; 278 emr.emr_in_buf = smip->smi_payload; 279 emr.emr_in_length = smip->smi_len; 280 281 emr.emr_out_buf = out; 282 emr.emr_out_length = sizeof (smip->smi_payload); 283 284 sfxge_mcdi_execute(sp, &emr); 285 286 smip->smi_rc = (uint8_t)emr.emr_rc; 287 smip->smi_cmd = (uint8_t)emr.emr_cmd; 288 smip->smi_len = (uint8_t)emr.emr_out_length_used; 289 memcpy(smip->smi_payload, out, smip->smi_len); 290 291 /* 292 * Helpfully trigger a device reset in response to an MCDI_CMD_REBOOT 293 * Both ports will see ->emt_exception callbacks on the next MCDI poll 294 */ 295 if (smip->smi_cmd == MC_CMD_REBOOT) { 296 297 DTRACE_PROBE(mcdi_ioctl_mc_reboot); 298 /* sfxge_t->s_state_lock held */ 299 (void) sfxge_restart_dispatch(sp, DDI_SLEEP, SFXGE_HW_OK, 300 "MC_REBOOT triggering restart", 0); 301 } 302 303 kmem_free(out, sizeof (smip->smi_payload)); 304 305 return (0); 306 307 fail3: 308 DTRACE_PROBE(fail3); 309 fail2: 310 DTRACE_PROBE(fail2); 311 fail1: 312 DTRACE_PROBE1(fail1, int, rc); 313 return (rc); 314 }