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 }