1 | /* $NetBSD: isp_netbsd.c,v 1.88 2014/12/31 17:10:45 christos Exp $ */ |
2 | /* |
3 | * Platform (NetBSD) dependent common attachment code for Qlogic adapters. |
4 | */ |
5 | /* |
6 | * Copyright (C) 1997, 1998, 1999 National Aeronautics & Space Administration |
7 | * All rights reserved. |
8 | * |
9 | * Additional Copyright (C) 2000-2007 by Matthew Jacob |
10 | * All rights reserved. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * 3. The name of the author may not be used to endorse or promote products |
21 | * derived from this software without specific prior written permission |
22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
24 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
25 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
26 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
28 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <sys/cdefs.h> |
36 | __KERNEL_RCSID(0, "$NetBSD: isp_netbsd.c,v 1.88 2014/12/31 17:10:45 christos Exp $" ); |
37 | |
38 | #include <dev/ic/isp_netbsd.h> |
39 | #include <dev/ic/isp_ioctl.h> |
40 | #include <sys/scsiio.h> |
41 | |
42 | #include <sys/timevar.h> |
43 | |
44 | /* |
45 | * Set a timeout for the watchdogging of a command. |
46 | * |
47 | * The dimensional analysis is |
48 | * |
49 | * milliseconds * (seconds/millisecond) * (ticks/second) = ticks |
50 | * |
51 | * = |
52 | * |
53 | * (milliseconds / 1000) * hz = ticks |
54 | * |
55 | * |
56 | * For timeouts less than 1 second, we'll get zero. Because of this, and |
57 | * because we want to establish *our* timeout to be longer than what the |
58 | * firmware might do, we just add 3 seconds at the back end. |
59 | */ |
60 | #define _XT(xs) ((((xs)->timeout/1000) * hz) + (3 * hz)) |
61 | |
62 | static void isp_config_interrupts(device_t); |
63 | static void ispminphys_1020(struct buf *); |
64 | static void ispminphys(struct buf *); |
65 | static void ispcmd(struct ispsoftc *, XS_T *); |
66 | static void isprequest(struct scsipi_channel *, scsipi_adapter_req_t, void *); |
67 | static int |
68 | ispioctl(struct scsipi_channel *, u_long, void *, int, struct proc *); |
69 | |
70 | static void isp_polled_cmd_wait(struct ispsoftc *, XS_T *); |
71 | static void isp_dog(void *); |
72 | static void isp_gdt(void *); |
73 | static void isp_ldt(void *); |
74 | static void isp_make_here(ispsoftc_t *, int); |
75 | static void isp_make_gone(ispsoftc_t *, int); |
76 | static void isp_fc_worker(void *); |
77 | |
78 | static const char *roles[4] = { |
79 | "(none)" , "Target" , "Initiator" , "Target/Initiator" |
80 | }; |
81 | static const char prom3[] = |
82 | "PortID 0x%06x Departed from Target %u because of %s" ; |
83 | int isp_change_is_bad = 0; /* "changed" devices are bad */ |
84 | int isp_quickboot_time = 15; /* don't wait more than N secs for loop up */ |
85 | static int isp_fabric_hysteresis = 5; |
86 | #define isp_change_is_bad 0 |
87 | |
88 | /* |
89 | * Complete attachment of hardware, include subdevices. |
90 | */ |
91 | |
92 | void |
93 | isp_attach(struct ispsoftc *isp) |
94 | { |
95 | device_t self = isp->isp_osinfo.dev; |
96 | int i; |
97 | |
98 | isp->isp_state = ISP_RUNSTATE; |
99 | |
100 | isp->isp_osinfo.adapter.adapt_dev = self; |
101 | isp->isp_osinfo.adapter.adapt_openings = isp->isp_maxcmds; |
102 | isp->isp_osinfo.loop_down_limit = 300; |
103 | |
104 | /* |
105 | * It's not stated whether max_periph is limited by SPI |
106 | * tag uage, but let's assume that it is. |
107 | */ |
108 | isp->isp_osinfo.adapter.adapt_max_periph = min(isp->isp_maxcmds, 255); |
109 | isp->isp_osinfo.adapter.adapt_ioctl = ispioctl; |
110 | isp->isp_osinfo.adapter.adapt_request = isprequest; |
111 | if (isp->isp_type <= ISP_HA_SCSI_1020A) { |
112 | isp->isp_osinfo.adapter.adapt_minphys = ispminphys_1020; |
113 | } else { |
114 | isp->isp_osinfo.adapter.adapt_minphys = ispminphys; |
115 | } |
116 | |
117 | callout_init(&isp->isp_osinfo.gdt, 0); |
118 | callout_setfunc(&isp->isp_osinfo.gdt, isp_gdt, isp); |
119 | callout_init(&isp->isp_osinfo.ldt, 0); |
120 | callout_setfunc(&isp->isp_osinfo.ldt, isp_ldt, isp); |
121 | if (IS_FC(isp)) { |
122 | if (kthread_create(PRI_NONE, 0, NULL, isp_fc_worker, isp, |
123 | &isp->isp_osinfo.thread, "%s:fc_thrd" , |
124 | device_xname(self))) { |
125 | isp_prt(isp, ISP_LOGERR, |
126 | "unable to create FC worker thread" ); |
127 | return; |
128 | } |
129 | } |
130 | |
131 | for (i = 0; i != isp->isp_osinfo.adapter.adapt_nchannels; i++) { |
132 | isp->isp_osinfo.chan[i].chan_adapter = |
133 | &isp->isp_osinfo.adapter; |
134 | isp->isp_osinfo.chan[i].chan_bustype = &scsi_bustype; |
135 | isp->isp_osinfo.chan[i].chan_channel = i; |
136 | /* |
137 | * Until the midlayer is fixed to use REPORT LUNS, |
138 | * limit to 8 luns. |
139 | */ |
140 | isp->isp_osinfo.chan[i].chan_nluns = min(isp->isp_maxluns, 8); |
141 | if (IS_FC(isp)) { |
142 | isp->isp_osinfo.chan[i].chan_ntargets = MAX_FC_TARG; |
143 | if (ISP_CAP_2KLOGIN(isp) == 0 && MAX_FC_TARG > 256) { |
144 | isp->isp_osinfo.chan[i].chan_ntargets = 256; |
145 | } |
146 | isp->isp_osinfo.chan[i].chan_id = MAX_FC_TARG; |
147 | } else { |
148 | isp->isp_osinfo.chan[i].chan_ntargets = MAX_TARGETS; |
149 | isp->isp_osinfo.chan[i].chan_id = |
150 | SDPARAM(isp, i)->isp_initiator_id; |
151 | ISP_LOCK(isp); |
152 | (void) isp_control(isp, ISPCTL_RESET_BUS, i); |
153 | ISP_UNLOCK(isp); |
154 | } |
155 | } |
156 | |
157 | /* |
158 | * Defer enabling mailbox interrupts until later. |
159 | */ |
160 | config_interrupts(self, isp_config_interrupts); |
161 | } |
162 | |
163 | static void |
164 | isp_config_interrupts(device_t self) |
165 | { |
166 | int i; |
167 | struct ispsoftc *isp = device_private(self); |
168 | |
169 | isp->isp_osinfo.mbox_sleep_ok = 1; |
170 | |
171 | if (IS_FC(isp) && (FCPARAM(isp, 0)->isp_fwstate != FW_READY || |
172 | FCPARAM(isp, 0)->isp_loopstate != LOOP_READY)) { |
173 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
174 | "Starting Initial Loop Down Timer" ); |
175 | callout_schedule(&isp->isp_osinfo.ldt, isp_quickboot_time * hz); |
176 | } |
177 | |
178 | /* |
179 | * And attach children (if any). |
180 | */ |
181 | for (i = 0; i < isp->isp_osinfo.adapter.adapt_nchannels; i++) { |
182 | config_found(self, &isp->isp_osinfo.chan[i], scsiprint); |
183 | } |
184 | } |
185 | |
186 | /* |
187 | * minphys our xfers |
188 | */ |
189 | static void |
190 | ispminphys_1020(struct buf *bp) |
191 | { |
192 | if (bp->b_bcount >= (1 << 24)) { |
193 | bp->b_bcount = (1 << 24); |
194 | } |
195 | minphys(bp); |
196 | } |
197 | |
198 | static void |
199 | ispminphys(struct buf *bp) |
200 | { |
201 | if (bp->b_bcount >= (1 << 30)) { |
202 | bp->b_bcount = (1 << 30); |
203 | } |
204 | minphys(bp); |
205 | } |
206 | |
207 | static int |
208 | ispioctl(struct scsipi_channel *chan, u_long cmd, void *addr, int flag, |
209 | struct proc *p) |
210 | { |
211 | struct ispsoftc *isp = device_private(chan->chan_adapter->adapt_dev); |
212 | int nr, bus, retval = ENOTTY; |
213 | |
214 | switch (cmd) { |
215 | case ISP_SDBLEV: |
216 | { |
217 | int olddblev = isp->isp_dblev; |
218 | isp->isp_dblev = *(int *)addr; |
219 | *(int *)addr = olddblev; |
220 | retval = 0; |
221 | break; |
222 | } |
223 | case ISP_GETROLE: |
224 | bus = *(int *)addr; |
225 | if (bus < 0 || bus >= isp->isp_nchan) { |
226 | retval = -ENXIO; |
227 | break; |
228 | } |
229 | if (IS_FC(isp)) { |
230 | *(int *)addr = FCPARAM(isp, bus)->role; |
231 | } else { |
232 | *(int *)addr = SDPARAM(isp, bus)->role; |
233 | } |
234 | retval = 0; |
235 | break; |
236 | case ISP_SETROLE: |
237 | |
238 | nr = *(int *)addr; |
239 | bus = nr >> 8; |
240 | if (bus < 0 || bus >= isp->isp_nchan) { |
241 | retval = -ENXIO; |
242 | break; |
243 | } |
244 | nr &= 0xff; |
245 | if (nr & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) { |
246 | retval = EINVAL; |
247 | break; |
248 | } |
249 | if (IS_FC(isp)) { |
250 | *(int *)addr = FCPARAM(isp, bus)->role; |
251 | FCPARAM(isp, bus)->role = nr; |
252 | } else { |
253 | *(int *)addr = SDPARAM(isp, bus)->role; |
254 | SDPARAM(isp, bus)->role = nr; |
255 | } |
256 | retval = 0; |
257 | break; |
258 | |
259 | case ISP_RESETHBA: |
260 | ISP_LOCK(isp); |
261 | isp_reinit(isp, 0); |
262 | ISP_UNLOCK(isp); |
263 | retval = 0; |
264 | break; |
265 | |
266 | case ISP_RESCAN: |
267 | if (IS_FC(isp)) { |
268 | bus = *(int *)addr; |
269 | if (bus < 0 || bus >= isp->isp_nchan) { |
270 | retval = -ENXIO; |
271 | break; |
272 | } |
273 | ISP_LOCK(isp); |
274 | if (isp_fc_runstate(isp, bus, 5 * 1000000)) { |
275 | retval = EIO; |
276 | } else { |
277 | retval = 0; |
278 | } |
279 | ISP_UNLOCK(isp); |
280 | } |
281 | break; |
282 | |
283 | case ISP_FC_LIP: |
284 | if (IS_FC(isp)) { |
285 | bus = *(int *)addr; |
286 | if (bus < 0 || bus >= isp->isp_nchan) { |
287 | retval = -ENXIO; |
288 | break; |
289 | } |
290 | ISP_LOCK(isp); |
291 | if (isp_control(isp, ISPCTL_SEND_LIP, bus)) { |
292 | retval = EIO; |
293 | } else { |
294 | retval = 0; |
295 | } |
296 | ISP_UNLOCK(isp); |
297 | } |
298 | break; |
299 | case ISP_FC_GETDINFO: |
300 | { |
301 | struct isp_fc_device *ifc = (struct isp_fc_device *) addr; |
302 | fcportdb_t *lp; |
303 | |
304 | if (IS_SCSI(isp)) { |
305 | break; |
306 | } |
307 | if (ifc->loopid >= MAX_FC_TARG) { |
308 | retval = EINVAL; |
309 | break; |
310 | } |
311 | lp = &FCPARAM(isp, ifc->chan)->portdb[ifc->loopid]; |
312 | if (lp->state == FC_PORTDB_STATE_VALID) { |
313 | ifc->role = lp->roles; |
314 | ifc->loopid = lp->handle; |
315 | ifc->portid = lp->portid; |
316 | ifc->node_wwn = lp->node_wwn; |
317 | ifc->port_wwn = lp->port_wwn; |
318 | retval = 0; |
319 | } else { |
320 | retval = ENODEV; |
321 | } |
322 | break; |
323 | } |
324 | case ISP_GET_STATS: |
325 | { |
326 | isp_stats_t *sp = (isp_stats_t *) addr; |
327 | |
328 | ISP_MEMZERO(sp, sizeof (*sp)); |
329 | sp->isp_stat_version = ISP_STATS_VERSION; |
330 | sp->isp_type = isp->isp_type; |
331 | sp->isp_revision = isp->isp_revision; |
332 | ISP_LOCK(isp); |
333 | sp->isp_stats[ISP_INTCNT] = isp->isp_intcnt; |
334 | sp->isp_stats[ISP_INTBOGUS] = isp->isp_intbogus; |
335 | sp->isp_stats[ISP_INTMBOXC] = isp->isp_intmboxc; |
336 | sp->isp_stats[ISP_INGOASYNC] = isp->isp_intoasync; |
337 | sp->isp_stats[ISP_RSLTCCMPLT] = isp->isp_rsltccmplt; |
338 | sp->isp_stats[ISP_FPHCCMCPLT] = isp->isp_fphccmplt; |
339 | sp->isp_stats[ISP_RSCCHIWAT] = isp->isp_rscchiwater; |
340 | sp->isp_stats[ISP_FPCCHIWAT] = isp->isp_fpcchiwater; |
341 | ISP_UNLOCK(isp); |
342 | retval = 0; |
343 | break; |
344 | } |
345 | case ISP_CLR_STATS: |
346 | ISP_LOCK(isp); |
347 | isp->isp_intcnt = 0; |
348 | isp->isp_intbogus = 0; |
349 | isp->isp_intmboxc = 0; |
350 | isp->isp_intoasync = 0; |
351 | isp->isp_rsltccmplt = 0; |
352 | isp->isp_fphccmplt = 0; |
353 | isp->isp_rscchiwater = 0; |
354 | isp->isp_fpcchiwater = 0; |
355 | ISP_UNLOCK(isp); |
356 | retval = 0; |
357 | break; |
358 | case ISP_FC_GETHINFO: |
359 | { |
360 | struct isp_hba_device *hba = (struct isp_hba_device *) addr; |
361 | bus = hba->fc_channel; |
362 | |
363 | if (bus < 0 || bus >= isp->isp_nchan) { |
364 | retval = ENXIO; |
365 | break; |
366 | } |
367 | hba->fc_fw_major = ISP_FW_MAJORX(isp->isp_fwrev); |
368 | hba->fc_fw_minor = ISP_FW_MINORX(isp->isp_fwrev); |
369 | hba->fc_fw_micro = ISP_FW_MICROX(isp->isp_fwrev); |
370 | hba->fc_nchannels = isp->isp_nchan; |
371 | hba->fc_nports = isp->isp_nchan;/* XXXX 24XX STUFF? XXX */ |
372 | if (IS_FC(isp)) { |
373 | hba->fc_speed = FCPARAM(isp, bus)->isp_gbspeed; |
374 | hba->fc_topology = FCPARAM(isp, bus)->isp_topo + 1; |
375 | hba->fc_loopid = FCPARAM(isp, bus)->isp_loopid; |
376 | hba->nvram_node_wwn = FCPARAM(isp, bus)->isp_wwnn_nvram; |
377 | hba->nvram_port_wwn = FCPARAM(isp, bus)->isp_wwpn_nvram; |
378 | hba->active_node_wwn = FCPARAM(isp, bus)->isp_wwnn; |
379 | hba->active_port_wwn = FCPARAM(isp, bus)->isp_wwpn; |
380 | } else { |
381 | hba->fc_speed = 0; |
382 | hba->fc_topology = 0; |
383 | hba->nvram_node_wwn = 0ull; |
384 | hba->nvram_port_wwn = 0ull; |
385 | hba->active_node_wwn = 0ull; |
386 | hba->active_port_wwn = 0ull; |
387 | } |
388 | retval = 0; |
389 | break; |
390 | } |
391 | case ISP_TSK_MGMT: |
392 | { |
393 | int needmarker; |
394 | struct isp_fc_tsk_mgmt *fct = (struct isp_fc_tsk_mgmt *) addr; |
395 | uint16_t loopid; |
396 | mbreg_t mbs; |
397 | |
398 | if (IS_SCSI(isp)) { |
399 | break; |
400 | } |
401 | |
402 | bus = fct->chan; |
403 | if (bus < 0 || bus >= isp->isp_nchan) { |
404 | retval = -ENXIO; |
405 | break; |
406 | } |
407 | |
408 | memset(&mbs, 0, sizeof (mbs)); |
409 | needmarker = retval = 0; |
410 | loopid = fct->loopid; |
411 | if (ISP_CAP_2KLOGIN(isp) == 0) { |
412 | loopid <<= 8; |
413 | } |
414 | switch (fct->action) { |
415 | case IPT_CLEAR_ACA: |
416 | mbs.param[0] = MBOX_CLEAR_ACA; |
417 | mbs.param[1] = loopid; |
418 | mbs.param[2] = fct->lun; |
419 | break; |
420 | case IPT_TARGET_RESET: |
421 | mbs.param[0] = MBOX_TARGET_RESET; |
422 | mbs.param[1] = loopid; |
423 | needmarker = 1; |
424 | break; |
425 | case IPT_LUN_RESET: |
426 | mbs.param[0] = MBOX_LUN_RESET; |
427 | mbs.param[1] = loopid; |
428 | mbs.param[2] = fct->lun; |
429 | needmarker = 1; |
430 | break; |
431 | case IPT_CLEAR_TASK_SET: |
432 | mbs.param[0] = MBOX_CLEAR_TASK_SET; |
433 | mbs.param[1] = loopid; |
434 | mbs.param[2] = fct->lun; |
435 | needmarker = 1; |
436 | break; |
437 | case IPT_ABORT_TASK_SET: |
438 | mbs.param[0] = MBOX_ABORT_TASK_SET; |
439 | mbs.param[1] = loopid; |
440 | mbs.param[2] = fct->lun; |
441 | needmarker = 1; |
442 | break; |
443 | default: |
444 | retval = EINVAL; |
445 | break; |
446 | } |
447 | if (retval == 0) { |
448 | if (needmarker) { |
449 | FCPARAM(isp, bus)->sendmarker = 1; |
450 | } |
451 | ISP_LOCK(isp); |
452 | retval = isp_control(isp, ISPCTL_RUN_MBOXCMD, &mbs); |
453 | ISP_UNLOCK(isp); |
454 | if (retval) { |
455 | retval = EIO; |
456 | } |
457 | } |
458 | break; |
459 | } |
460 | case ISP_FC_GETDLIST: |
461 | { |
462 | isp_dlist_t local, *ua; |
463 | uint16_t nph, nphe, count, channel, lim; |
464 | struct wwnpair pair, *uptr; |
465 | |
466 | if (IS_SCSI(isp)) { |
467 | retval = EINVAL; |
468 | break; |
469 | } |
470 | |
471 | ua = *(isp_dlist_t **)addr; |
472 | if (copyin(ua, &local, sizeof (isp_dlist_t))) { |
473 | retval = EFAULT; |
474 | break; |
475 | } |
476 | lim = local.count; |
477 | channel = local.channel; |
478 | |
479 | ua = *(isp_dlist_t **)addr; |
480 | uptr = &ua->wwns[0]; |
481 | |
482 | if (ISP_CAP_2KLOGIN(isp)) { |
483 | nphe = NPH_MAX_2K; |
484 | } else { |
485 | nphe = NPH_MAX; |
486 | } |
487 | for (count = 0, nph = 0; count < lim && nph != nphe; nph++) { |
488 | ISP_LOCK(isp); |
489 | retval = isp_control(isp, ISPCTL_GET_NAMES, channel, |
490 | nph, &pair.wwnn, &pair.wwpn); |
491 | ISP_UNLOCK(isp); |
492 | if (retval || (pair.wwpn == INI_NONE && |
493 | pair.wwnn == INI_NONE)) { |
494 | retval = 0; |
495 | continue; |
496 | } |
497 | if (copyout(&pair, (void *)uptr++, sizeof (pair))) { |
498 | retval = EFAULT; |
499 | break; |
500 | } |
501 | count++; |
502 | } |
503 | if (retval == 0) { |
504 | if (copyout(&count, (void *)&ua->count, |
505 | sizeof (count))) { |
506 | retval = EFAULT; |
507 | } |
508 | } |
509 | break; |
510 | } |
511 | case SCBUSIORESET: |
512 | ISP_LOCK(isp); |
513 | if (isp_control(isp, ISPCTL_RESET_BUS, &chan->chan_channel)) { |
514 | retval = EIO; |
515 | } else { |
516 | retval = 0; |
517 | } |
518 | ISP_UNLOCK(isp); |
519 | break; |
520 | default: |
521 | break; |
522 | } |
523 | return (retval); |
524 | } |
525 | |
526 | static void |
527 | ispcmd(struct ispsoftc *isp, XS_T *xs) |
528 | { |
529 | volatile uint8_t ombi; |
530 | int lim, chan; |
531 | |
532 | ISP_LOCK(isp); |
533 | if (isp->isp_state < ISP_RUNSTATE) { |
534 | ISP_DISABLE_INTS(isp); |
535 | isp_init(isp); |
536 | if (isp->isp_state != ISP_INITSTATE) { |
537 | ISP_ENABLE_INTS(isp); |
538 | ISP_UNLOCK(isp); |
539 | isp_prt(isp, ISP_LOGERR, "isp not at init state" ); |
540 | XS_SETERR(xs, HBA_BOTCH); |
541 | scsipi_done(xs); |
542 | return; |
543 | } |
544 | isp->isp_state = ISP_RUNSTATE; |
545 | ISP_ENABLE_INTS(isp); |
546 | } |
547 | chan = XS_CHANNEL(xs); |
548 | |
549 | /* |
550 | * Handle the case of a FC card where the FC thread hasn't |
551 | * fired up yet and we don't yet have a known loop state. |
552 | */ |
553 | if (IS_FC(isp) && (FCPARAM(isp, chan)->isp_fwstate != FW_READY || |
554 | FCPARAM(isp, chan)->isp_loopstate != LOOP_READY) && |
555 | isp->isp_osinfo.thread == NULL) { |
556 | ombi = isp->isp_osinfo.mbox_sleep_ok != 0; |
557 | int delay_time; |
558 | |
559 | if (xs->xs_control & XS_CTL_POLL) { |
560 | isp->isp_osinfo.mbox_sleep_ok = 0; |
561 | } |
562 | |
563 | if (isp->isp_osinfo.loop_checked == 0) { |
564 | delay_time = 10 * 1000000; |
565 | isp->isp_osinfo.loop_checked = 1; |
566 | } else { |
567 | delay_time = 250000; |
568 | } |
569 | |
570 | if (isp_fc_runstate(isp, XS_CHANNEL(xs), delay_time) != 0) { |
571 | if (xs->xs_control & XS_CTL_POLL) { |
572 | isp->isp_osinfo.mbox_sleep_ok = ombi; |
573 | } |
574 | if (FCPARAM(isp, XS_CHANNEL(xs))->loop_seen_once == 0) { |
575 | XS_SETERR(xs, HBA_SELTIMEOUT); |
576 | scsipi_done(xs); |
577 | ISP_UNLOCK(isp); |
578 | return; |
579 | } |
580 | /* |
581 | * Otherwise, fall thru to be queued up for later. |
582 | */ |
583 | } else { |
584 | int wasblocked = |
585 | (isp->isp_osinfo.blocked || isp->isp_osinfo.paused); |
586 | isp->isp_osinfo.blocked = isp->isp_osinfo.paused = 0; |
587 | if (wasblocked) { |
588 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
589 | "THAW QUEUES @ LINE %d" , __LINE__); |
590 | scsipi_channel_thaw(&isp->isp_osinfo.chan[chan], |
591 | 1); |
592 | } |
593 | } |
594 | if (xs->xs_control & XS_CTL_POLL) { |
595 | isp->isp_osinfo.mbox_sleep_ok = ombi; |
596 | } |
597 | } |
598 | |
599 | if (isp->isp_osinfo.paused) { |
600 | isp_prt(isp, ISP_LOGWARN, "I/O while paused" ); |
601 | xs->error = XS_RESOURCE_SHORTAGE; |
602 | scsipi_done(xs); |
603 | ISP_UNLOCK(isp); |
604 | return; |
605 | } |
606 | if (isp->isp_osinfo.blocked) { |
607 | isp_prt(isp, ISP_LOGWARN, |
608 | "I/O while blocked with retries %d" , xs, xs->xs_retries); |
609 | if (xs->xs_retries) { |
610 | xs->error = XS_REQUEUE; |
611 | xs->xs_retries--; |
612 | } else { |
613 | XS_SETERR(xs, HBA_SELTIMEOUT); |
614 | } |
615 | scsipi_done(xs); |
616 | ISP_UNLOCK(isp); |
617 | return; |
618 | } |
619 | |
620 | if (xs->xs_control & XS_CTL_POLL) { |
621 | ombi = isp->isp_osinfo.mbox_sleep_ok; |
622 | isp->isp_osinfo.mbox_sleep_ok = 0; |
623 | } |
624 | |
625 | switch (isp_start(xs)) { |
626 | case CMD_QUEUED: |
627 | if (IS_FC(isp) && isp->isp_osinfo.wwns[XS_TGT(xs)] == 0) { |
628 | fcparam *fcp = FCPARAM(isp, XS_CHANNEL(xs)); |
629 | int dbidx = fcp->isp_dev_map[XS_TGT(xs)] - 1; |
630 | device_t dev = xs->xs_periph->periph_dev; |
631 | |
632 | if (dbidx >= 0 && dev && |
633 | prop_dictionary_set_uint64(device_properties(dev), |
634 | "port-wwn" , fcp->portdb[dbidx].port_wwn) == TRUE) { |
635 | isp->isp_osinfo.wwns[XS_TGT(xs)] = |
636 | fcp->portdb[dbidx].port_wwn; |
637 | } |
638 | } |
639 | if (xs->xs_control & XS_CTL_POLL) { |
640 | isp_polled_cmd_wait(isp, xs); |
641 | isp->isp_osinfo.mbox_sleep_ok = ombi; |
642 | } else if (xs->timeout) { |
643 | callout_reset(&xs->xs_callout, _XT(xs), isp_dog, xs); |
644 | } |
645 | break; |
646 | case CMD_EAGAIN: |
647 | isp->isp_osinfo.paused = 1; |
648 | xs->error = XS_RESOURCE_SHORTAGE; |
649 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
650 | "FREEZE QUEUES @ LINE %d" , __LINE__); |
651 | for (chan = 0; chan < isp->isp_nchan; chan++) { |
652 | scsipi_channel_freeze(&isp->isp_osinfo.chan[chan], 1); |
653 | } |
654 | scsipi_done(xs); |
655 | break; |
656 | case CMD_RQLATER: |
657 | /* |
658 | * We can only get RQLATER from FC devices (1 channel only) |
659 | * |
660 | * If we've never seen loop up see if if we've been down |
661 | * quickboot time, otherwise wait loop down limit time. |
662 | * If so, then we start giving up on commands. |
663 | */ |
664 | if (FCPARAM(isp, XS_CHANNEL(xs))->loop_seen_once == 0) { |
665 | lim = isp_quickboot_time; |
666 | } else { |
667 | lim = isp->isp_osinfo.loop_down_limit; |
668 | } |
669 | if (isp->isp_osinfo.loop_down_time >= lim) { |
670 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
671 | "RQLATER->SELTIMEOUT for %d (%d >= %d)" , XS_TGT(xs), |
672 | isp->isp_osinfo.loop_down_time, lim); |
673 | XS_SETERR(xs, HBA_SELTIMEOUT); |
674 | scsipi_done(xs); |
675 | break; |
676 | } |
677 | if (isp->isp_osinfo.blocked == 0) { |
678 | isp->isp_osinfo.blocked = 1; |
679 | scsipi_channel_freeze(&isp->isp_osinfo.chan[chan], 1); |
680 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
681 | "FREEZE QUEUES @ LINE %d" , __LINE__); |
682 | } else { |
683 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
684 | "RQLATER WITH FROZEN QUEUES @ LINE %d" , __LINE__); |
685 | } |
686 | xs->error = XS_REQUEUE; |
687 | scsipi_done(xs); |
688 | break; |
689 | case CMD_COMPLETE: |
690 | scsipi_done(xs); |
691 | break; |
692 | } |
693 | ISP_UNLOCK(isp); |
694 | } |
695 | |
696 | static void |
697 | isprequest(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) |
698 | { |
699 | struct ispsoftc *isp = device_private(chan->chan_adapter->adapt_dev); |
700 | |
701 | switch (req) { |
702 | case ADAPTER_REQ_RUN_XFER: |
703 | ispcmd(isp, (XS_T *) arg); |
704 | break; |
705 | |
706 | case ADAPTER_REQ_GROW_RESOURCES: |
707 | /* Not supported. */ |
708 | break; |
709 | |
710 | case ADAPTER_REQ_SET_XFER_MODE: |
711 | if (IS_SCSI(isp)) { |
712 | struct scsipi_xfer_mode *xm = arg; |
713 | int dflags = 0; |
714 | sdparam *sdp = SDPARAM(isp, chan->chan_channel); |
715 | |
716 | if (xm->xm_mode & PERIPH_CAP_TQING) |
717 | dflags |= DPARM_TQING; |
718 | if (xm->xm_mode & PERIPH_CAP_WIDE16) |
719 | dflags |= DPARM_WIDE; |
720 | if (xm->xm_mode & PERIPH_CAP_SYNC) |
721 | dflags |= DPARM_SYNC; |
722 | ISP_LOCK(isp); |
723 | sdp->isp_devparam[xm->xm_target].goal_flags |= dflags; |
724 | dflags = sdp->isp_devparam[xm->xm_target].goal_flags; |
725 | sdp->isp_devparam[xm->xm_target].dev_update = 1; |
726 | sdp->update = 1; |
727 | ISP_UNLOCK(isp); |
728 | isp_prt(isp, ISP_LOGDEBUG1, |
729 | "isprequest: device flags 0x%x for %d.%d.X" , |
730 | dflags, chan->chan_channel, xm->xm_target); |
731 | break; |
732 | } |
733 | default: |
734 | break; |
735 | } |
736 | } |
737 | |
738 | static void |
739 | isp_polled_cmd_wait(struct ispsoftc *isp, XS_T *xs) |
740 | { |
741 | int infinite = 0, mswait; |
742 | |
743 | /* |
744 | * If we can't use interrupts, poll on completion. |
745 | */ |
746 | if ((mswait = XS_TIME(xs)) == 0) { |
747 | infinite = 1; |
748 | } |
749 | |
750 | while (mswait || infinite) { |
751 | uint32_t isr; |
752 | uint16_t sema, mbox; |
753 | if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { |
754 | isp_intr(isp, isr, sema, mbox); |
755 | if (XS_CMD_DONE_P(xs)) { |
756 | break; |
757 | } |
758 | } |
759 | ISP_DELAY(1000); |
760 | mswait -= 1; |
761 | } |
762 | |
763 | /* |
764 | * If no other error occurred but we didn't finish |
765 | * something bad happened, so abort the command. |
766 | */ |
767 | if (XS_CMD_DONE_P(xs) == 0) { |
768 | if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) { |
769 | isp_reinit(isp, 0); |
770 | } |
771 | if (XS_NOERR(xs)) { |
772 | isp_prt(isp, ISP_LOGERR, "polled command timed out" ); |
773 | XS_SETERR(xs, HBA_BOTCH); |
774 | } |
775 | } |
776 | scsipi_done(xs); |
777 | } |
778 | |
779 | void |
780 | isp_done(XS_T *xs) |
781 | { |
782 | if (XS_CMD_WDOG_P(xs) == 0) { |
783 | struct ispsoftc *isp = XS_ISP(xs); |
784 | callout_stop(&xs->xs_callout); |
785 | if (XS_CMD_GRACE_P(xs)) { |
786 | isp_prt(isp, ISP_LOGDEBUG1, |
787 | "finished command on borrowed time" ); |
788 | } |
789 | XS_CMD_S_CLEAR(xs); |
790 | /* |
791 | * Fixup- if we get a QFULL, we need |
792 | * to set XS_BUSY as the error. |
793 | */ |
794 | if (xs->status == SCSI_QUEUE_FULL) { |
795 | xs->error = XS_BUSY; |
796 | } |
797 | if (isp->isp_osinfo.paused) { |
798 | int i; |
799 | isp->isp_osinfo.paused = 0; |
800 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
801 | "THAW QUEUES @ LINE %d" , __LINE__); |
802 | for (i = 0; i < isp->isp_nchan; i++) { |
803 | scsipi_channel_timed_thaw(&isp->isp_osinfo.chan[i]); |
804 | } |
805 | } |
806 | if (xs->error == XS_DRIVER_STUFFUP) { |
807 | isp_prt(isp, ISP_LOGERR, |
808 | "BOTCHED cmd for %d.%d.%d cmd 0x%x datalen %ld" , |
809 | XS_CHANNEL(xs), XS_TGT(xs), XS_LUN(xs), |
810 | XS_CDBP(xs)[0], (long) XS_XFRLEN(xs)); |
811 | } |
812 | scsipi_done(xs); |
813 | } |
814 | } |
815 | |
816 | static void |
817 | isp_dog(void *arg) |
818 | { |
819 | XS_T *xs = arg; |
820 | struct ispsoftc *isp = XS_ISP(xs); |
821 | uint32_t handle; |
822 | int sok; |
823 | |
824 | |
825 | ISP_ILOCK(isp); |
826 | sok = isp->isp_osinfo.mbox_sleep_ok; |
827 | isp->isp_osinfo.mbox_sleep_ok = 0; |
828 | /* |
829 | * We've decided this command is dead. Make sure we're not trying |
830 | * to kill a command that's already dead by getting its handle and |
831 | * and seeing whether it's still alive. |
832 | */ |
833 | handle = isp_find_handle(isp, xs); |
834 | if (handle) { |
835 | uint32_t isr; |
836 | uint16_t mbox, sema; |
837 | |
838 | if (XS_CMD_DONE_P(xs)) { |
839 | isp_prt(isp, ISP_LOGDEBUG1, |
840 | "watchdog found done cmd (handle 0x%x)" , handle); |
841 | goto out; |
842 | } |
843 | |
844 | if (XS_CMD_WDOG_P(xs)) { |
845 | isp_prt(isp, ISP_LOGDEBUG1, |
846 | "recursive watchdog (handle 0x%x)" , handle); |
847 | goto out; |
848 | } |
849 | |
850 | XS_CMD_S_WDOG(xs); |
851 | |
852 | if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { |
853 | isp_intr(isp, isr, sema, mbox); |
854 | |
855 | } |
856 | if (XS_CMD_DONE_P(xs)) { |
857 | isp_prt(isp, ISP_LOGDEBUG1, |
858 | "watchdog cleanup for handle 0x%x" , handle); |
859 | XS_CMD_C_WDOG(xs); |
860 | isp_done(xs); |
861 | } else if (XS_CMD_GRACE_P(xs)) { |
862 | isp_prt(isp, ISP_LOGDEBUG1, |
863 | "watchdog timeout for handle 0x%x" , handle); |
864 | /* |
865 | * Make sure the command is *really* dead before we |
866 | * release the handle (and DMA resources) for reuse. |
867 | */ |
868 | (void) isp_control(isp, ISPCTL_ABORT_CMD, arg); |
869 | |
870 | /* |
871 | * After this point, the command is really dead. |
872 | */ |
873 | if (XS_XFRLEN(xs)) { |
874 | ISP_DMAFREE(isp, xs, handle); |
875 | } |
876 | isp_destroy_handle(isp, handle); |
877 | XS_SETERR(xs, XS_TIMEOUT); |
878 | XS_CMD_S_CLEAR(xs); |
879 | isp_done(xs); |
880 | } else { |
881 | void *qe; |
882 | isp_marker_t local, *mp = &local; |
883 | isp_prt(isp, ISP_LOGDEBUG2, |
884 | "possible command timeout on handle %x" , handle); |
885 | XS_CMD_C_WDOG(xs); |
886 | callout_reset(&xs->xs_callout, hz, isp_dog, xs); |
887 | qe = isp_getrqentry(isp); |
888 | if (qe == NULL) |
889 | goto out; |
890 | XS_CMD_S_GRACE(xs); |
891 | ISP_MEMZERO((void *) mp, sizeof (*mp)); |
892 | mp->mrk_header.rqs_entry_count = 1; |
893 | mp->mrk_header.rqs_entry_type = RQSTYPE_MARKER; |
894 | mp->mrk_modifier = SYNC_ALL; |
895 | mp->mrk_target = XS_CHANNEL(xs) << 7; |
896 | isp_put_marker(isp, mp, qe); |
897 | ISP_SYNC_REQUEST(isp); |
898 | } |
899 | } else { |
900 | isp_prt(isp, ISP_LOGDEBUG0, "watchdog with no command" ); |
901 | } |
902 | out: |
903 | isp->isp_osinfo.mbox_sleep_ok = sok; |
904 | ISP_IUNLOCK(isp); |
905 | } |
906 | |
907 | /* |
908 | * Gone Device Timer Function- when we have decided that a device has gone |
909 | * away, we wait a specific period of time prior to telling the OS it has |
910 | * gone away. |
911 | * |
912 | * This timer function fires once a second and then scans the port database |
913 | * for devices that are marked dead but still have a virtual target assigned. |
914 | * We decrement a counter for that port database entry, and when it hits zero, |
915 | * we tell the OS the device has gone away. |
916 | */ |
917 | static void |
918 | isp_gdt(void *arg) |
919 | { |
920 | ispsoftc_t *isp = arg; |
921 | fcportdb_t *lp; |
922 | int dbidx, tgt, more_to_do = 0; |
923 | |
924 | isp_prt(isp, ISP_LOGDEBUG0, "GDT timer expired" ); |
925 | ISP_LOCK(isp); |
926 | for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { |
927 | lp = &FCPARAM(isp, 0)->portdb[dbidx]; |
928 | |
929 | if (lp->state != FC_PORTDB_STATE_ZOMBIE) { |
930 | continue; |
931 | } |
932 | if (lp->dev_map_idx == 0) { |
933 | continue; |
934 | } |
935 | if (lp->new_reserved == 0) { |
936 | continue; |
937 | } |
938 | lp->new_reserved -= 1; |
939 | if (lp->new_reserved != 0) { |
940 | more_to_do++; |
941 | continue; |
942 | } |
943 | tgt = lp->dev_map_idx - 1; |
944 | FCPARAM(isp, 0)->isp_dev_map[tgt] = 0; |
945 | lp->dev_map_idx = 0; |
946 | lp->state = FC_PORTDB_STATE_NIL; |
947 | isp_prt(isp, ISP_LOGCONFIG, prom3, lp->portid, tgt, |
948 | "Gone Device Timeout" ); |
949 | isp_make_gone(isp, tgt); |
950 | } |
951 | if (more_to_do) { |
952 | callout_schedule(&isp->isp_osinfo.gdt, hz); |
953 | } else { |
954 | isp->isp_osinfo.gdt_running = 0; |
955 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
956 | "stopping Gone Device Timer" ); |
957 | } |
958 | ISP_UNLOCK(isp); |
959 | } |
960 | |
961 | /* |
962 | * Loop Down Timer Function- when loop goes down, a timer is started and |
963 | * and after it expires we come here and take all probational devices that |
964 | * the OS knows about and the tell the OS that they've gone away. |
965 | * |
966 | * We don't clear the devices out of our port database because, when loop |
967 | * come back up, we have to do some actual cleanup with the chip at that |
968 | * point (implicit PLOGO, e.g., to get the chip's port database state right). |
969 | */ |
970 | static void |
971 | isp_ldt(void *arg) |
972 | { |
973 | ispsoftc_t *isp = arg; |
974 | fcportdb_t *lp; |
975 | int dbidx, tgt; |
976 | |
977 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "Loop Down Timer expired" ); |
978 | ISP_LOCK(isp); |
979 | |
980 | /* |
981 | * Notify to the OS all targets who we now consider have departed. |
982 | */ |
983 | for (dbidx = 0; dbidx < MAX_FC_TARG; dbidx++) { |
984 | lp = &FCPARAM(isp, 0)->portdb[dbidx]; |
985 | |
986 | if (lp->state != FC_PORTDB_STATE_PROBATIONAL) { |
987 | continue; |
988 | } |
989 | if (lp->dev_map_idx == 0) { |
990 | continue; |
991 | } |
992 | |
993 | /* |
994 | * XXX: CLEAN UP AND COMPLETE ANY PENDING COMMANDS FIRST! |
995 | */ |
996 | |
997 | /* |
998 | * Mark that we've announced that this device is gone.... |
999 | */ |
1000 | lp->reserved = 1; |
1001 | |
1002 | /* |
1003 | * but *don't* change the state of the entry. Just clear |
1004 | * any target id stuff and announce to CAM that the |
1005 | * device is gone. This way any necessary PLOGO stuff |
1006 | * will happen when loop comes back up. |
1007 | */ |
1008 | |
1009 | tgt = lp->dev_map_idx - 1; |
1010 | FCPARAM(isp, 0)->isp_dev_map[tgt] = 0; |
1011 | lp->dev_map_idx = 0; |
1012 | isp_prt(isp, ISP_LOGCONFIG, prom3, lp->portid, tgt, |
1013 | "Loop Down Timeout" ); |
1014 | isp_make_gone(isp, tgt); |
1015 | } |
1016 | |
1017 | /* |
1018 | * The loop down timer has expired. Wake up the kthread |
1019 | * to notice that fact (or make it false). |
1020 | */ |
1021 | isp->isp_osinfo.loop_down_time = isp->isp_osinfo.loop_down_limit+1; |
1022 | wakeup(&isp->isp_osinfo.thread); |
1023 | ISP_UNLOCK(isp); |
1024 | } |
1025 | |
1026 | static void |
1027 | isp_make_here(ispsoftc_t *isp, int tgt) |
1028 | { |
1029 | isp_prt(isp, ISP_LOGINFO, "target %d has arrived" , tgt); |
1030 | } |
1031 | |
1032 | static void |
1033 | isp_make_gone(ispsoftc_t *isp, int tgt) |
1034 | { |
1035 | isp_prt(isp, ISP_LOGINFO, "target %d has departed" , tgt); |
1036 | } |
1037 | |
1038 | static void |
1039 | isp_fc_worker(void *arg) |
1040 | { |
1041 | void scsipi_run_queue(struct scsipi_channel *); |
1042 | ispsoftc_t *isp = arg; |
1043 | int slp = 0; |
1044 | int chan = 0; |
1045 | |
1046 | int s = splbio(); |
1047 | /* |
1048 | * The first loop is for our usage where we have yet to have |
1049 | * gotten good fibre channel state. |
1050 | */ |
1051 | while (isp->isp_osinfo.thread != NULL) { |
1052 | int sok, lb, lim; |
1053 | |
1054 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "checking FC state" ); |
1055 | sok = isp->isp_osinfo.mbox_sleep_ok; |
1056 | isp->isp_osinfo.mbox_sleep_ok = 1; |
1057 | lb = isp_fc_runstate(isp, chan, 250000); |
1058 | isp->isp_osinfo.mbox_sleep_ok = sok; |
1059 | if (lb) { |
1060 | /* |
1061 | * Increment loop down time by the last sleep interval |
1062 | */ |
1063 | isp->isp_osinfo.loop_down_time += slp; |
1064 | |
1065 | if (lb < 0) { |
1066 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1067 | "FC loop not up (down count %d)" , |
1068 | isp->isp_osinfo.loop_down_time); |
1069 | } else { |
1070 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1071 | "FC got to %d (down count %d)" , |
1072 | lb, isp->isp_osinfo.loop_down_time); |
1073 | } |
1074 | |
1075 | |
1076 | /* |
1077 | * If we've never seen loop up and we've waited longer |
1078 | * than quickboot time, or we've seen loop up but we've |
1079 | * waited longer than loop_down_limit, give up and go |
1080 | * to sleep until loop comes up. |
1081 | */ |
1082 | if (FCPARAM(isp, 0)->loop_seen_once == 0) { |
1083 | lim = isp_quickboot_time; |
1084 | } else { |
1085 | lim = isp->isp_osinfo.loop_down_limit; |
1086 | } |
1087 | if (isp->isp_osinfo.loop_down_time >= lim) { |
1088 | /* |
1089 | * If we're now past our limit, release |
1090 | * the queues and let them come in and |
1091 | * either get HBA_SELTIMOUT or cause |
1092 | * another freeze. |
1093 | */ |
1094 | isp->isp_osinfo.blocked = 1; |
1095 | slp = 0; |
1096 | } else if (isp->isp_osinfo.loop_down_time < 10) { |
1097 | slp = 1; |
1098 | } else if (isp->isp_osinfo.loop_down_time < 30) { |
1099 | slp = 5; |
1100 | } else if (isp->isp_osinfo.loop_down_time < 60) { |
1101 | slp = 10; |
1102 | } else if (isp->isp_osinfo.loop_down_time < 120) { |
1103 | slp = 20; |
1104 | } else { |
1105 | slp = 30; |
1106 | } |
1107 | |
1108 | } else { |
1109 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1110 | "FC state OK" ); |
1111 | isp->isp_osinfo.loop_down_time = 0; |
1112 | slp = 0; |
1113 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1114 | "THAW QUEUES @ LINE %d" , __LINE__); |
1115 | scsipi_channel_thaw(&isp->isp_osinfo.chan[chan], 1); |
1116 | } |
1117 | |
1118 | /* |
1119 | * If we'd frozen the queues, unfreeze them now so that |
1120 | * we can start getting commands. If the FC state isn't |
1121 | * okay yet, they'll hit that in isp_start which will |
1122 | * freeze the queues again. |
1123 | */ |
1124 | if (isp->isp_osinfo.blocked) { |
1125 | isp->isp_osinfo.blocked = 0; |
1126 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1127 | "THAW QUEUES @ LINE %d" , __LINE__); |
1128 | scsipi_channel_thaw(&isp->isp_osinfo.chan[chan], 1); |
1129 | } |
1130 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, "sleep time %d" , slp); |
1131 | tsleep(&isp->isp_osinfo.thread, PRIBIO, "ispf" , slp * hz); |
1132 | |
1133 | /* |
1134 | * If slp is zero, we're waking up for the first time after |
1135 | * things have been okay. In this case, we set a deferral state |
1136 | * for all commands and delay hysteresis seconds before starting |
1137 | * the FC state evaluation. This gives the loop/fabric a chance |
1138 | * to settle. |
1139 | */ |
1140 | if (slp == 0 && isp_fabric_hysteresis) { |
1141 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1142 | "sleep hysteresis tick time %d" , |
1143 | isp_fabric_hysteresis * hz); |
1144 | (void) tsleep(&isp_fabric_hysteresis, PRIBIO, "ispT" , |
1145 | (isp_fabric_hysteresis * hz)); |
1146 | } |
1147 | } |
1148 | splx(s); |
1149 | |
1150 | /* In case parent is waiting for us to exit. */ |
1151 | wakeup(&isp->isp_osinfo.thread); |
1152 | kthread_exit(0); |
1153 | } |
1154 | |
1155 | /* |
1156 | * Free any associated resources prior to decommissioning and |
1157 | * set the card to a known state (so it doesn't wake up and kick |
1158 | * us when we aren't expecting it to). |
1159 | * |
1160 | * Locks are held before coming here. |
1161 | */ |
1162 | void |
1163 | isp_uninit(struct ispsoftc *isp) |
1164 | { |
1165 | isp_lock(isp); |
1166 | /* |
1167 | * Leave with interrupts disabled. |
1168 | */ |
1169 | ISP_DISABLE_INTS(isp); |
1170 | isp_unlock(isp); |
1171 | } |
1172 | |
1173 | void |
1174 | isp_async(struct ispsoftc *isp, ispasync_t cmd, ...) |
1175 | { |
1176 | int bus, tgt; |
1177 | const char *msg = NULL; |
1178 | static const char prom[] = |
1179 | "PortID 0x%06x handle 0x%x role %s %s\n" |
1180 | " WWNN 0x%08x%08x WWPN 0x%08x%08x" ; |
1181 | static const char prom2[] = |
1182 | "PortID 0x%06x handle 0x%x role %s %s tgt %u\n" |
1183 | " WWNN 0x%08x%08x WWPN 0x%08x%08x" ; |
1184 | fcportdb_t *lp; |
1185 | va_list ap; |
1186 | |
1187 | switch (cmd) { |
1188 | case ISPASYNC_NEW_TGT_PARAMS: |
1189 | if (IS_SCSI(isp)) { |
1190 | sdparam *sdp; |
1191 | int flags; |
1192 | struct scsipi_xfer_mode xm; |
1193 | |
1194 | va_start(ap, cmd); |
1195 | bus = va_arg(ap, int); |
1196 | tgt = va_arg(ap, int); |
1197 | va_end(ap); |
1198 | sdp = SDPARAM(isp, bus); |
1199 | flags = sdp->isp_devparam[tgt].actv_flags; |
1200 | |
1201 | xm.xm_mode = 0; |
1202 | xm.xm_period = sdp->isp_devparam[tgt].actv_period; |
1203 | xm.xm_offset = sdp->isp_devparam[tgt].actv_offset; |
1204 | xm.xm_target = tgt; |
1205 | |
1206 | if ((flags & DPARM_SYNC) && xm.xm_period && xm.xm_offset) |
1207 | xm.xm_mode |= PERIPH_CAP_SYNC; |
1208 | if (flags & DPARM_WIDE) |
1209 | xm.xm_mode |= PERIPH_CAP_WIDE16; |
1210 | if (flags & DPARM_TQING) |
1211 | xm.xm_mode |= PERIPH_CAP_TQING; |
1212 | scsipi_async_event(&isp->isp_osinfo.chan[bus], |
1213 | ASYNC_EVENT_XFER_MODE, &xm); |
1214 | break; |
1215 | } |
1216 | case ISPASYNC_BUS_RESET: |
1217 | va_start(ap, cmd); |
1218 | bus = va_arg(ap, int); |
1219 | va_end(ap); |
1220 | isp_prt(isp, ISP_LOGINFO, "SCSI bus %d reset detected" , bus); |
1221 | scsipi_async_event(&isp->isp_osinfo.chan[bus], |
1222 | ASYNC_EVENT_RESET, NULL); |
1223 | break; |
1224 | case ISPASYNC_LIP: |
1225 | if (msg == NULL) { |
1226 | msg = "LIP Received" ; |
1227 | } |
1228 | /* FALLTHROUGH */ |
1229 | case ISPASYNC_LOOP_RESET: |
1230 | if (msg == NULL) { |
1231 | msg = "LOOP Reset Received" ; |
1232 | } |
1233 | /* FALLTHROUGH */ |
1234 | case ISPASYNC_LOOP_DOWN: |
1235 | if (msg == NULL) { |
1236 | msg = "Loop DOWN" ; |
1237 | } |
1238 | va_start(ap, cmd); |
1239 | bus = va_arg(ap, int); |
1240 | va_end(ap); |
1241 | |
1242 | /* |
1243 | * Don't do queue freezes or blockage until we have the |
1244 | * thread running and interrupts that can unfreeze/unblock us. |
1245 | */ |
1246 | if (isp->isp_osinfo.mbox_sleep_ok && |
1247 | isp->isp_osinfo.blocked == 0 && |
1248 | isp->isp_osinfo.thread) { |
1249 | isp->isp_osinfo.blocked = 1; |
1250 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1251 | "FREEZE QUEUES @ LINE %d" , __LINE__); |
1252 | scsipi_channel_freeze(&isp->isp_osinfo.chan[bus], 1); |
1253 | if (callout_pending(&isp->isp_osinfo.ldt) == 0) { |
1254 | callout_schedule(&isp->isp_osinfo.ldt, |
1255 | isp->isp_osinfo.loop_down_limit * hz); |
1256 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1257 | "Starting Loop Down Timer" ); |
1258 | } |
1259 | } |
1260 | isp_prt(isp, ISP_LOGINFO, msg); |
1261 | break; |
1262 | case ISPASYNC_LOOP_UP: |
1263 | /* |
1264 | * Let the subsequent ISPASYNC_CHANGE_NOTIFY invoke |
1265 | * the FC worker thread. When the FC worker thread |
1266 | * is done, let *it* call scsipi_channel_thaw... |
1267 | */ |
1268 | isp_prt(isp, ISP_LOGINFO, "Loop UP" ); |
1269 | break; |
1270 | case ISPASYNC_DEV_ARRIVED: |
1271 | va_start(ap, cmd); |
1272 | bus = va_arg(ap, int); |
1273 | lp = va_arg(ap, fcportdb_t *); |
1274 | va_end(ap); |
1275 | lp->reserved = 0; |
1276 | if ((FCPARAM(isp, bus)->role & ISP_ROLE_INITIATOR) && |
1277 | (lp->roles & (SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT))) { |
1278 | int dbidx = lp - FCPARAM(isp, bus)->portdb; |
1279 | int i; |
1280 | |
1281 | for (i = 0; i < MAX_FC_TARG; i++) { |
1282 | if (i >= FL_ID && i <= SNS_ID) { |
1283 | continue; |
1284 | } |
1285 | if (FCPARAM(isp, bus)->isp_dev_map[i] == 0) { |
1286 | break; |
1287 | } |
1288 | } |
1289 | if (i < MAX_FC_TARG) { |
1290 | FCPARAM(isp, bus)->isp_dev_map[i] = dbidx + 1; |
1291 | lp->dev_map_idx = i + 1; |
1292 | } else { |
1293 | isp_prt(isp, ISP_LOGWARN, "out of target ids" ); |
1294 | isp_dump_portdb(isp, bus); |
1295 | } |
1296 | } |
1297 | if (lp->dev_map_idx) { |
1298 | tgt = lp->dev_map_idx - 1; |
1299 | isp_prt(isp, ISP_LOGCONFIG, prom2, |
1300 | lp->portid, lp->handle, |
1301 | roles[lp->roles], "arrived at" , tgt, |
1302 | (uint32_t) (lp->node_wwn >> 32), |
1303 | (uint32_t) lp->node_wwn, |
1304 | (uint32_t) (lp->port_wwn >> 32), |
1305 | (uint32_t) lp->port_wwn); |
1306 | isp_make_here(isp, tgt); |
1307 | } else { |
1308 | isp_prt(isp, ISP_LOGCONFIG, prom, |
1309 | lp->portid, lp->handle, |
1310 | roles[lp->roles], "arrived" , |
1311 | (uint32_t) (lp->node_wwn >> 32), |
1312 | (uint32_t) lp->node_wwn, |
1313 | (uint32_t) (lp->port_wwn >> 32), |
1314 | (uint32_t) lp->port_wwn); |
1315 | } |
1316 | break; |
1317 | case ISPASYNC_DEV_CHANGED: |
1318 | va_start(ap, cmd); |
1319 | bus = va_arg(ap, int); |
1320 | lp = va_arg(ap, fcportdb_t *); |
1321 | va_end(ap); |
1322 | if (isp_change_is_bad) { |
1323 | lp->state = FC_PORTDB_STATE_NIL; |
1324 | if (lp->dev_map_idx) { |
1325 | tgt = lp->dev_map_idx - 1; |
1326 | FCPARAM(isp, bus)->isp_dev_map[tgt] = 0; |
1327 | lp->dev_map_idx = 0; |
1328 | isp_prt(isp, ISP_LOGCONFIG, prom3, |
1329 | lp->portid, tgt, "change is bad" ); |
1330 | isp_make_gone(isp, tgt); |
1331 | } else { |
1332 | isp_prt(isp, ISP_LOGCONFIG, prom, |
1333 | lp->portid, lp->handle, |
1334 | roles[lp->roles], |
1335 | "changed and departed" , |
1336 | (uint32_t) (lp->node_wwn >> 32), |
1337 | (uint32_t) lp->node_wwn, |
1338 | (uint32_t) (lp->port_wwn >> 32), |
1339 | (uint32_t) lp->port_wwn); |
1340 | } |
1341 | } else { |
1342 | lp->portid = lp->new_portid; |
1343 | lp->roles = lp->new_roles; |
1344 | if (lp->dev_map_idx) { |
1345 | int t = lp->dev_map_idx - 1; |
1346 | FCPARAM(isp, bus)->isp_dev_map[t] = |
1347 | (lp - FCPARAM(isp, bus)->portdb) + 1; |
1348 | tgt = lp->dev_map_idx - 1; |
1349 | isp_prt(isp, ISP_LOGCONFIG, prom2, |
1350 | lp->portid, lp->handle, |
1351 | roles[lp->roles], "changed at" , tgt, |
1352 | (uint32_t) (lp->node_wwn >> 32), |
1353 | (uint32_t) lp->node_wwn, |
1354 | (uint32_t) (lp->port_wwn >> 32), |
1355 | (uint32_t) lp->port_wwn); |
1356 | } else { |
1357 | isp_prt(isp, ISP_LOGCONFIG, prom, |
1358 | lp->portid, lp->handle, |
1359 | roles[lp->roles], "changed" , |
1360 | (uint32_t) (lp->node_wwn >> 32), |
1361 | (uint32_t) lp->node_wwn, |
1362 | (uint32_t) (lp->port_wwn >> 32), |
1363 | (uint32_t) lp->port_wwn); |
1364 | } |
1365 | } |
1366 | break; |
1367 | case ISPASYNC_DEV_STAYED: |
1368 | va_start(ap, cmd); |
1369 | bus = va_arg(ap, int); |
1370 | lp = va_arg(ap, fcportdb_t *); |
1371 | va_end(ap); |
1372 | if (lp->dev_map_idx) { |
1373 | tgt = lp->dev_map_idx - 1; |
1374 | isp_prt(isp, ISP_LOGCONFIG, prom2, |
1375 | lp->portid, lp->handle, |
1376 | roles[lp->roles], "stayed at" , tgt, |
1377 | (uint32_t) (lp->node_wwn >> 32), |
1378 | (uint32_t) lp->node_wwn, |
1379 | (uint32_t) (lp->port_wwn >> 32), |
1380 | (uint32_t) lp->port_wwn); |
1381 | } else { |
1382 | isp_prt(isp, ISP_LOGCONFIG, prom, |
1383 | lp->portid, lp->handle, |
1384 | roles[lp->roles], "stayed" , |
1385 | (uint32_t) (lp->node_wwn >> 32), |
1386 | (uint32_t) lp->node_wwn, |
1387 | (uint32_t) (lp->port_wwn >> 32), |
1388 | (uint32_t) lp->port_wwn); |
1389 | } |
1390 | break; |
1391 | case ISPASYNC_DEV_GONE: |
1392 | va_start(ap, cmd); |
1393 | bus = va_arg(ap, int); |
1394 | lp = va_arg(ap, fcportdb_t *); |
1395 | va_end(ap); |
1396 | /* |
1397 | * If this has a virtual target and we haven't marked it |
1398 | * that we're going to have isp_gdt tell the OS it's gone, |
1399 | * set the isp_gdt timer running on it. |
1400 | * |
1401 | * If it isn't marked that isp_gdt is going to get rid of it, |
1402 | * announce that it's gone. |
1403 | */ |
1404 | if (lp->dev_map_idx && lp->reserved == 0) { |
1405 | lp->reserved = 1; |
1406 | lp->new_reserved = isp->isp_osinfo.gone_device_time; |
1407 | lp->state = FC_PORTDB_STATE_ZOMBIE; |
1408 | if (isp->isp_osinfo.gdt_running == 0) { |
1409 | isp->isp_osinfo.gdt_running = 1; |
1410 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1411 | "starting Gone Device Timer" ); |
1412 | callout_schedule(&isp->isp_osinfo.gdt, hz); |
1413 | } |
1414 | tgt = lp->dev_map_idx - 1; |
1415 | isp_prt(isp, ISP_LOGCONFIG, prom2, |
1416 | lp->portid, lp->handle, |
1417 | roles[lp->roles], "gone zombie at" , tgt, |
1418 | (uint32_t) (lp->node_wwn >> 32), |
1419 | (uint32_t) lp->node_wwn, |
1420 | (uint32_t) (lp->port_wwn >> 32), |
1421 | (uint32_t) lp->port_wwn); |
1422 | } else if (lp->reserved == 0) { |
1423 | isp_prt(isp, ISP_LOGCONFIG, prom, |
1424 | lp->portid, lp->handle, |
1425 | roles[lp->roles], "departed" , |
1426 | (uint32_t) (lp->node_wwn >> 32), |
1427 | (uint32_t) lp->node_wwn, |
1428 | (uint32_t) (lp->port_wwn >> 32), |
1429 | (uint32_t) lp->port_wwn); |
1430 | } |
1431 | break; |
1432 | case ISPASYNC_CHANGE_NOTIFY: |
1433 | { |
1434 | int opt; |
1435 | |
1436 | va_start(ap, cmd); |
1437 | bus = va_arg(ap, int); |
1438 | opt = va_arg(ap, int); |
1439 | va_end(ap); |
1440 | |
1441 | if (opt == ISPASYNC_CHANGE_PDB) { |
1442 | msg = "Port Database Changed" ; |
1443 | } else if (opt == ISPASYNC_CHANGE_SNS) { |
1444 | msg = "Name Server Database Changed" ; |
1445 | } else { |
1446 | msg = "Other Change Notify" ; |
1447 | } |
1448 | /* |
1449 | * If the loop down timer is running, cancel it. |
1450 | */ |
1451 | if (callout_pending(&isp->isp_osinfo.ldt)) { |
1452 | callout_stop(&isp->isp_osinfo.ldt); |
1453 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1454 | "Stopping Loop Down Timer" ); |
1455 | } |
1456 | isp_prt(isp, ISP_LOGINFO, msg); |
1457 | /* |
1458 | * We can set blocked here because we know it's now okay |
1459 | * to try and run isp_fc_runstate (in order to build loop |
1460 | * state). But we don't try and freeze the midlayer's queue |
1461 | * if we have no thread that we can wake to later unfreeze |
1462 | * it. |
1463 | */ |
1464 | if (isp->isp_osinfo.blocked == 0) { |
1465 | isp->isp_osinfo.blocked = 1; |
1466 | if (isp->isp_osinfo.thread) { |
1467 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1468 | "FREEZE QUEUES @ LINE %d" , __LINE__); |
1469 | scsipi_channel_freeze(&isp->isp_osinfo.chan[bus], 1); |
1470 | } |
1471 | } |
1472 | /* |
1473 | * Note that we have work for the thread to do, and |
1474 | * if the thread is here already, wake it up. |
1475 | */ |
1476 | if (isp->isp_osinfo.thread) { |
1477 | wakeup(&isp->isp_osinfo.thread); |
1478 | } else { |
1479 | isp_prt(isp, ISP_LOGDEBUG1, "no FC thread yet" ); |
1480 | } |
1481 | break; |
1482 | } |
1483 | case ISPASYNC_FW_CRASH: |
1484 | { |
1485 | uint16_t mbox1; |
1486 | mbox1 = ISP_READ(isp, OUTMAILBOX1); |
1487 | if (IS_DUALBUS(isp)) { |
1488 | bus = ISP_READ(isp, OUTMAILBOX6); |
1489 | } else { |
1490 | bus = 0; |
1491 | } |
1492 | isp_prt(isp, ISP_LOGERR, |
1493 | "Internal Firmware Error on bus %d @ RISC Address 0x%x" , |
1494 | bus, mbox1); |
1495 | if (IS_FC(isp)) { |
1496 | if (isp->isp_osinfo.blocked == 0) { |
1497 | isp->isp_osinfo.blocked = 1; |
1498 | isp_prt(isp, ISP_LOGSANCFG|ISP_LOGDEBUG0, |
1499 | "FREEZE QUEUES @ LINE %d" , __LINE__); |
1500 | scsipi_channel_freeze(&isp->isp_osinfo.chan[bus], 1); |
1501 | } |
1502 | } |
1503 | mbox1 = isp->isp_osinfo.mbox_sleep_ok; |
1504 | isp->isp_osinfo.mbox_sleep_ok = 0; |
1505 | isp_reinit(isp, 0); |
1506 | isp->isp_osinfo.mbox_sleep_ok = mbox1; |
1507 | isp_async(isp, ISPASYNC_FW_RESTARTED, NULL); |
1508 | break; |
1509 | } |
1510 | default: |
1511 | break; |
1512 | } |
1513 | } |
1514 | |
1515 | void |
1516 | isp_prt(struct ispsoftc *isp, int level, const char *fmt, ...) |
1517 | { |
1518 | va_list ap; |
1519 | if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) { |
1520 | return; |
1521 | } |
1522 | printf("%s: " , device_xname(isp->isp_osinfo.dev)); |
1523 | va_start(ap, fmt); |
1524 | vprintf(fmt, ap); |
1525 | va_end(ap); |
1526 | printf("\n" ); |
1527 | } |
1528 | |
1529 | void |
1530 | isp_xs_prt(struct ispsoftc *isp, XS_T *xs, int level, const char *fmt, ...) |
1531 | { |
1532 | va_list ap; |
1533 | if (level != ISP_LOGALL && (level & isp->isp_dblev) == 0) { |
1534 | return; |
1535 | } |
1536 | scsipi_printaddr(xs->xs_periph); |
1537 | va_start(ap, fmt); |
1538 | vprintf(fmt, ap); |
1539 | va_end(ap); |
1540 | printf("\n" ); |
1541 | } |
1542 | |
1543 | void |
1544 | isp_lock(struct ispsoftc *isp) |
1545 | { |
1546 | int s = splbio(); |
1547 | if (isp->isp_osinfo.islocked++ == 0) { |
1548 | isp->isp_osinfo.splsaved = s; |
1549 | } else { |
1550 | splx(s); |
1551 | } |
1552 | } |
1553 | |
1554 | void |
1555 | isp_unlock(struct ispsoftc *isp) |
1556 | { |
1557 | if (isp->isp_osinfo.islocked-- <= 1) { |
1558 | isp->isp_osinfo.islocked = 0; |
1559 | splx(isp->isp_osinfo.splsaved); |
1560 | } |
1561 | } |
1562 | |
1563 | uint64_t |
1564 | isp_microtime_sub(struct timeval *b, struct timeval *a) |
1565 | { |
1566 | struct timeval x; |
1567 | uint64_t elapsed; |
1568 | timersub(b, a, &x); |
1569 | elapsed = GET_NANOSEC(&x); |
1570 | if (elapsed == 0) |
1571 | elapsed++; |
1572 | return (elapsed); |
1573 | } |
1574 | |
1575 | int |
1576 | isp_mbox_acquire(ispsoftc_t *isp) |
1577 | { |
1578 | if (isp->isp_osinfo.mboxbsy) { |
1579 | return (1); |
1580 | } else { |
1581 | isp->isp_osinfo.mboxcmd_done = 0; |
1582 | isp->isp_osinfo.mboxbsy = 1; |
1583 | return (0); |
1584 | } |
1585 | } |
1586 | |
1587 | void |
1588 | isp_mbox_wait_complete(struct ispsoftc *isp, mbreg_t *mbp) |
1589 | { |
1590 | unsigned int usecs = mbp->timeout; |
1591 | unsigned int maxc, olim, ilim; |
1592 | struct timeval start; |
1593 | |
1594 | if (usecs == 0) { |
1595 | usecs = MBCMD_DEFAULT_TIMEOUT; |
1596 | } |
1597 | maxc = isp->isp_mbxwrk0 + 1; |
1598 | |
1599 | microtime(&start); |
1600 | if (isp->isp_osinfo.mbox_sleep_ok) { |
1601 | int to; |
1602 | struct timeval tv, utv; |
1603 | |
1604 | tv.tv_sec = 0; |
1605 | tv.tv_usec = 0; |
1606 | for (olim = 0; olim < maxc; olim++) { |
1607 | utv.tv_sec = usecs / 1000000; |
1608 | utv.tv_usec = usecs % 1000000; |
1609 | timeradd(&tv, &utv, &tv); |
1610 | } |
1611 | to = tvtohz(&tv); |
1612 | if (to == 0) |
1613 | to = 1; |
1614 | timeradd(&tv, &start, &tv); |
1615 | |
1616 | isp->isp_osinfo.mbox_sleep_ok = 0; |
1617 | isp->isp_osinfo.mbox_sleeping = 1; |
1618 | tsleep(&isp->isp_mbxworkp, PRIBIO, "ispmbx_sleep" , to); |
1619 | isp->isp_osinfo.mbox_sleeping = 0; |
1620 | isp->isp_osinfo.mbox_sleep_ok = 1; |
1621 | } else { |
1622 | for (olim = 0; olim < maxc; olim++) { |
1623 | for (ilim = 0; ilim < usecs; ilim += 100) { |
1624 | uint32_t isr; |
1625 | uint16_t sema, mbox; |
1626 | if (isp->isp_osinfo.mboxcmd_done) { |
1627 | break; |
1628 | } |
1629 | if (ISP_READ_ISR(isp, &isr, &sema, &mbox)) { |
1630 | isp_intr(isp, isr, sema, mbox); |
1631 | if (isp->isp_osinfo.mboxcmd_done) { |
1632 | break; |
1633 | } |
1634 | } |
1635 | ISP_DELAY(100); |
1636 | } |
1637 | if (isp->isp_osinfo.mboxcmd_done) { |
1638 | break; |
1639 | } |
1640 | } |
1641 | } |
1642 | if (isp->isp_osinfo.mboxcmd_done == 0) { |
1643 | struct timeval finish, elapsed; |
1644 | |
1645 | microtime(&finish); |
1646 | timersub(&finish, &start, &elapsed); |
1647 | isp_prt(isp, ISP_LOGWARN, |
1648 | "%s Mailbox Command (0x%x) Timeout (%uus actual)" , |
1649 | isp->isp_osinfo.mbox_sleep_ok? "Interrupting" : "Polled" , |
1650 | isp->isp_lastmbxcmd, (elapsed.tv_sec * 1000000) + |
1651 | elapsed.tv_usec); |
1652 | mbp->param[0] = MBOX_TIMEOUT; |
1653 | isp->isp_osinfo.mboxcmd_done = 1; |
1654 | } |
1655 | } |
1656 | |
1657 | void |
1658 | isp_mbox_notify_done(ispsoftc_t *isp) |
1659 | { |
1660 | if (isp->isp_osinfo.mbox_sleeping) { |
1661 | wakeup(&isp->isp_mbxworkp); |
1662 | } |
1663 | isp->isp_osinfo.mboxcmd_done = 1; |
1664 | } |
1665 | |
1666 | void |
1667 | isp_mbox_release(ispsoftc_t *isp) |
1668 | { |
1669 | isp->isp_osinfo.mboxbsy = 0; |
1670 | } |
1671 | |