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
9 * http://www.opensource.org/licenses/cddl1.txt.
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 (c) 2004-2012 Emulex. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #define DEF_EVENT_STRUCT /* Needed for emlxs_events.h in emlxs_event.h */
28 #include <emlxs.h>
29
30
31 /* Required for EMLXS_CONTEXT in EMLXS_MSGF calls */
32 EMLXS_MSG_DEF(EMLXS_EVENT_C);
33
34
35 static uint32_t emlxs_event_check(emlxs_port_t *port, emlxs_event_t *evt);
36 static void emlxs_event_destroy(emlxs_hba_t *hba, emlxs_event_entry_t *entry);
37
38 extern void
39 emlxs_null_func() {}
40
41
42 static uint32_t
43 emlxs_event_check(emlxs_port_t *port, emlxs_event_t *evt)
44 {
45 emlxs_hba_t *hba = HBA;
46
47 /* Check if the event is being requested */
48 if ((hba->event_mask & evt->mask)) {
49 return (1);
50 }
51
52 #ifdef SAN_DIAG_SUPPORT
53 if ((port->sd_event_mask & evt->mask)) {
54 return (1);
55 }
56 #endif /* SAN_DIAG_SUPPORT */
57
58 return (0);
59
60 } /* emlxs_event_check() */
61
62
63 extern uint32_t
64 emlxs_event_queue_create(emlxs_hba_t *hba)
65 {
66 emlxs_event_queue_t *eventq = &EVENTQ;
67 ddi_iblock_cookie_t iblock;
68
69 /* Clear the queue */
70 bzero(eventq, sizeof (emlxs_event_queue_t));
71
72 cv_init(&eventq->lock_cv, NULL, CV_DRIVER, NULL);
73
74 if (!(hba->intr_flags & EMLXS_MSI_ENABLED)) {
75 /* Get the current interrupt block cookie */
76 (void) ddi_get_iblock_cookie(hba->dip, (uint_t)EMLXS_INUMBER,
77 &iblock);
78
79 /* Create the mutex lock */
80 mutex_init(&eventq->lock, NULL, MUTEX_DRIVER, (void *)iblock);
81 }
82 #ifdef MSI_SUPPORT
83 else {
84 /* Create event mutex lock */
85 mutex_init(&eventq->lock, NULL, MUTEX_DRIVER,
86 DDI_INTR_PRI(hba->intr_arg));
87 }
88 #endif
89
90 return (1);
91
92 } /* emlxs_event_queue_create() */
93
94
95 extern void
96 emlxs_event_queue_destroy(emlxs_hba_t *hba)
97 {
98 emlxs_port_t *vport;
99 emlxs_event_queue_t *eventq = &EVENTQ;
100 uint32_t i;
101 uint32_t wakeup = 0;
102
103 mutex_enter(&eventq->lock);
104
105 /* Clear all event masks and broadcast a wakeup */
106 /* to clear any sleeping threads */
107 if (hba->event_mask) {
108 hba->event_mask = 0;
109 hba->event_timer = 0;
110 wakeup = 1;
111 }
112
113 for (i = 0; i < MAX_VPORTS; i++) {
114 vport = &VPORT(i);
115
116 if (vport->sd_event_mask) {
117 vport->sd_event_mask = 0;
118 wakeup = 1;
119 }
120 }
121
122 if (wakeup) {
123 cv_broadcast(&eventq->lock_cv);
124
125 mutex_exit(&eventq->lock);
126 BUSYWAIT_MS(10);
127 mutex_enter(&eventq->lock);
128 }
129
130 /* Destroy the remaining events */
131 while (eventq->first) {
132 emlxs_event_destroy(hba, eventq->first);
133 }
134
135 mutex_exit(&eventq->lock);
136
137 /* Destroy the queue lock */
138 mutex_destroy(&eventq->lock);
139 cv_destroy(&eventq->lock_cv);
140
141 /* Clear the queue */
142 bzero(eventq, sizeof (emlxs_event_queue_t));
143
144 return;
145
146 } /* emlxs_event_queue_destroy() */
147
148
149 /* Event queue lock must be held */
150 static void
151 emlxs_event_destroy(emlxs_hba_t *hba, emlxs_event_entry_t *entry)
152 {
153 emlxs_event_queue_t *eventq = &EVENTQ;
154 emlxs_port_t *port;
155 uint32_t missed = 0;
156
157 port = (emlxs_port_t *)entry->port;
158
159 eventq->count--;
160 if (eventq->count == 0) {
161 eventq->first = NULL;
162 eventq->last = NULL;
163 } else {
164 if (entry->prev) {
165 entry->prev->next = entry->next;
166 }
167 if (entry->next) {
168 entry->next->prev = entry->prev;
169 }
170 if (eventq->first == entry) {
171 eventq->first = entry->next;
172 }
173 if (eventq->last == entry) {
174 eventq->last = entry->prev;
175 }
176 }
177
178 entry->prev = NULL;
179 entry->next = NULL;
180
181 if ((entry->evt->mask == EVT_LINK) ||
182 (entry->evt->mask == EVT_RSCN)) {
183 if (!(entry->flag & EMLXS_DFC_EVENT_DONE)) {
184 hba->hba_event.missed++;
185 missed = 1;
186 }
187 }
188
189 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_dequeued_msg,
190 "%s[%d]: flag=%x missed=%d cnt=%d",
191 entry->evt->label, entry->id, entry->flag, missed, eventq->count);
192
193 /* Call notification handler */
194 if (entry->evt->destroy != emlxs_null_func) {
195 entry->evt->destroy(entry);
196 }
197
198 /* Free context buffer */
199 if (entry->bp && entry->size) {
200 kmem_free(entry->bp, entry->size);
201 }
202
203 /* Free entry buffer */
204 kmem_free(entry, sizeof (emlxs_event_entry_t));
205
206 return;
207
208 } /* emlxs_event_destroy() */
209
210
211 extern void
212 emlxs_event(emlxs_port_t *port, emlxs_event_t *evt, void *bp, uint32_t size)
213 {
214 emlxs_hba_t *hba = HBA;
215 emlxs_event_queue_t *eventq = &EVENTQ;
216 emlxs_event_entry_t *entry;
217 uint32_t i;
218 uint32_t mask;
219
220 if (emlxs_event_check(port, evt) == 0) {
221 goto failed;
222 }
223
224 /* Create event entry */
225 if (!(entry = (emlxs_event_entry_t *)kmem_alloc(
226 sizeof (emlxs_event_entry_t), KM_NOSLEEP))) {
227 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
228 "%s: Unable to allocate event entry.", evt->label);
229
230 goto failed;
231 }
232
233 /* Initialize */
234 bzero(entry, sizeof (emlxs_event_entry_t));
235
236 entry->evt = evt;
237 entry->port = (void *)port;
238 entry->bp = bp;
239 entry->size = size;
240
241 mutex_enter(&eventq->lock);
242
243 /* Set the event timer */
244 entry->timestamp = hba->timer_tics;
245 if (evt->timeout) {
246 entry->timer = entry->timestamp + evt->timeout;
247 }
248
249 /* Eventq id starts with 1 */
250 if (eventq->next_id == 0) {
251 eventq->next_id = 1;
252 }
253
254 /* Set the event id */
255 entry->id = eventq->next_id++;
256
257 /* Set last event table */
258 mask = evt->mask;
259 for (i = 0; i < 32; i++) {
260 if (mask & 0x01) {
261 eventq->last_id[i] = entry->id;
262 }
263 mask >>= 1;
264 }
265
266 /* Put event on bottom of queue */
267 entry->next = NULL;
268 if (eventq->count == 0) {
269 entry->prev = NULL;
270 eventq->first = entry;
271 eventq->last = entry;
272 } else {
273 entry->prev = eventq->last;
274 entry->prev->next = entry;
275 eventq->last = entry;
276 }
277 eventq->count++;
278
279 if ((entry->evt->mask == EVT_LINK) ||
280 (entry->evt->mask == EVT_RSCN)) {
281 hba->hba_event.new++;
282 }
283
284 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_queued_msg,
285 "%s[%d]: bp=%p size=%d cnt=%d", entry->evt->label,
286 entry->id, bp, size, eventq->count);
287
288 /* Broadcast the event */
289 cv_broadcast(&eventq->lock_cv);
290
291 mutex_exit(&eventq->lock);
292
293 return;
294
295 failed:
296
297 if (bp && size) {
298 kmem_free(bp, size);
299 }
300
301 return;
302
303 } /* emlxs_event() */
304
305
306 extern void
307 emlxs_timer_check_events(emlxs_hba_t *hba)
308 {
309 emlxs_config_t *cfg = &CFG;
310 emlxs_event_queue_t *eventq = &EVENTQ;
311 emlxs_event_entry_t *entry;
312 emlxs_event_entry_t *next;
313
314 if (!cfg[CFG_TIMEOUT_ENABLE].current) {
315 return;
316 }
317
318 if ((hba->event_timer > hba->timer_tics)) {
319 return;
320 }
321
322 if (eventq->count) {
323 mutex_enter(&eventq->lock);
324
325 entry = eventq->first;
326 while (entry) {
327 if ((!entry->timer) ||
328 (entry->timer > hba->timer_tics)) {
329 entry = entry->next;
330 continue;
331 }
332
333 /* Event timed out, destroy it */
334 next = entry->next;
335 emlxs_event_destroy(hba, entry);
336 entry = next;
337 }
338
339 mutex_exit(&eventq->lock);
340 }
341
342 /* Set next event timer check */
343 hba->event_timer = hba->timer_tics + EMLXS_EVENT_PERIOD;
344
345 return;
346
347 } /* emlxs_timer_check_events() */
348
349
350 extern void
351 emlxs_log_rscn_event(emlxs_port_t *port, uint8_t *payload, uint32_t size)
352 {
353 uint8_t *bp;
354 uint32_t *ptr;
355
356 /* Check if the event is being requested */
357 if (emlxs_event_check(port, &emlxs_rscn_event) == 0) {
358 return;
359 }
360
361 if (size > MAX_RSCN_PAYLOAD) {
362 size = MAX_RSCN_PAYLOAD;
363 }
364
365 size += sizeof (uint32_t);
366
367 /* Save a copy of the payload for the event log */
368 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
369 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
370 "%s: Unable to allocate buffer.", emlxs_rscn_event.label);
371
372 return;
373 }
374
375 /*
376 * Buffer Format:
377 * word[0] = DID of the RSCN
378 * word[1] = RSCN Payload
379 */
380 ptr = (uint32_t *)bp;
381 *ptr++ = port->did;
382 bcopy(payload, (char *)ptr, (size - sizeof (uint32_t)));
383
384 emlxs_event(port, &emlxs_rscn_event, bp, size);
385
386 return;
387
388 } /* emlxs_log_rscn_event() */
389
390
391 extern void
392 emlxs_log_vportrscn_event(emlxs_port_t *port, uint8_t *payload, uint32_t size)
393 {
394 uint8_t *bp;
395 uint8_t *ptr;
396
397 /* Check if the event is being requested */
398 if (emlxs_event_check(port, &emlxs_vportrscn_event) == 0) {
399 return;
400 }
401
402 if (size > MAX_RSCN_PAYLOAD) {
403 size = MAX_RSCN_PAYLOAD;
404 }
405
406 size += sizeof (NAME_TYPE);
407
408 /* Save a copy of the payload for the event log */
409 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
410 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
411 "%s: Unable to allocate buffer.",
412 emlxs_vportrscn_event.label);
413
414 return;
415 }
416
417 /*
418 * Buffer Format:
419 * word[0 - 4] = WWPN of the RSCN
420 * word[5] = RSCN Payload
421 */
422 ptr = bp;
423 bcopy(&port->wwpn, ptr, sizeof (NAME_TYPE));
424 ptr += sizeof (NAME_TYPE);
425 bcopy(payload, ptr, (size - sizeof (NAME_TYPE)));
426
427 emlxs_event(port, &emlxs_vportrscn_event, bp, size);
428
429 return;
430
431 } /* emlxs_log_vportrscn_event() */
432
433
434 extern uint32_t
435 emlxs_flush_ct_event(emlxs_port_t *port, uint32_t rxid)
436 {
437 emlxs_hba_t *hba = HBA;
438 emlxs_event_queue_t *eventq = &EVENTQ;
439 emlxs_event_entry_t *entry;
440 uint32_t *ptr;
441 uint32_t found = 0;
442
443 mutex_enter(&eventq->lock);
444
445 for (entry = eventq->first; entry != NULL; entry = entry->next) {
446 if ((entry->port != port) ||
447 (entry->evt != &emlxs_ct_event)) {
448 continue;
449 }
450
451 ptr = (uint32_t *)entry->bp;
452 if (rxid == *ptr) {
453 /* This will prevent a CT exchange abort */
454 /* in emlxs_ct_event_destroy() */
455 entry->flag |= EMLXS_DFC_EVENT_DONE;
456
457 emlxs_event_destroy(hba, entry);
458 found = 1;
459 break;
460 }
461 }
462
463 mutex_exit(&eventq->lock);
464
465 return (found);
466
467 } /* emlxs_flush_ct_event() */
468
469
470 extern uint32_t
471 emlxs_log_ct_event(emlxs_port_t *port, uint8_t *payload, uint32_t size,
472 uint32_t rxid)
473 {
474 uint8_t *bp;
475 uint32_t *ptr;
476
477 /* Check if the event is being requested */
478 if (emlxs_event_check(port, &emlxs_ct_event) == 0) {
479 return (1);
480 }
481
482 if (size > MAX_CT_PAYLOAD) {
483 size = MAX_CT_PAYLOAD;
484 }
485
486 size += sizeof (uint32_t);
487
488 /* Save a copy of the payload for the event log */
489 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
490 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
491 "%s: Unable to allocate buffer.", emlxs_ct_event.label);
492
493 return (1);
494 }
495
496 /*
497 * Buffer Format:
498 * word[0] = RXID tag for outgoing reply to this CT request
499 * word[1] = CT Payload
500 */
501 ptr = (uint32_t *)bp;
502 *ptr++ = rxid;
503 bcopy(payload, (char *)ptr, (size - sizeof (uint32_t)));
504
505 emlxs_event(port, &emlxs_ct_event, bp, size);
506
507 return (0);
508
509 } /* emlxs_log_ct_event() */
510
511
512 extern void
513 emlxs_ct_event_destroy(emlxs_event_entry_t *entry)
514 {
515 emlxs_port_t *port = (emlxs_port_t *)entry->port;
516 emlxs_hba_t *hba = HBA;
517 uint32_t rxid;
518
519 if (!(entry->flag & EMLXS_DFC_EVENT_DONE)) {
520
521 rxid = *(uint32_t *)entry->bp;
522
523 /* Abort exchange */
524 emlxs_thread_spawn(hba, emlxs_abort_ct_exchange,
525 entry->port, (void *)(unsigned long)rxid);
526 }
527
528 return;
529
530 } /* emlxs_ct_event_destroy() */
531
532
533 extern void
534 emlxs_log_link_event(emlxs_port_t *port)
535 {
536 emlxs_hba_t *hba = HBA;
537 uint8_t *bp;
538 dfc_linkinfo_t *linkinfo;
539 uint8_t *byte;
540 uint8_t *linkspeed;
541 uint8_t *liptype;
542 uint8_t *resv1;
543 uint8_t *resv2;
544 uint32_t size;
545
546 /* Check if the event is being requested */
547 if (emlxs_event_check(port, &emlxs_link_event) == 0) {
548 return;
549 }
550
551 size = sizeof (dfc_linkinfo_t) + sizeof (uint32_t);
552
553 /* Save a copy of the buffer for the event log */
554 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
555 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
556 "%s: Unable to allocate buffer.", emlxs_link_event.label);
557
558 return;
559 }
560
561 /*
562 * Buffer Format:
563 * word[0] = Linkspeed:8
564 * word[0] = LIP_type:8
565 * word[0] = resv1:8
566 * word[0] = resv2:8
567 * word[1] = dfc_linkinfo_t data
568 */
569 byte = (uint8_t *)bp;
570 linkspeed = &byte[0];
571 liptype = &byte[1];
572 resv1 = &byte[2];
573 resv2 = &byte[3];
574 linkinfo = (dfc_linkinfo_t *)&byte[4];
575
576 *resv1 = 0;
577 *resv2 = 0;
578
579 if (hba->state <= FC_LINK_DOWN) {
580 *linkspeed = 0;
581 *liptype = 0;
582 } else {
583 /* Set linkspeed */
584 if (hba->linkspeed == LA_2GHZ_LINK) {
585 *linkspeed = HBA_PORTSPEED_2GBIT;
586 } else if (hba->linkspeed == LA_4GHZ_LINK) {
587 *linkspeed = HBA_PORTSPEED_4GBIT;
588 } else if (hba->linkspeed == LA_8GHZ_LINK) {
589 *linkspeed = HBA_PORTSPEED_8GBIT;
590 } else if (hba->linkspeed == LA_10GHZ_LINK) {
591 *linkspeed = HBA_PORTSPEED_10GBIT;
592 } else if (hba->linkspeed == LA_16GHZ_LINK) {
593 *linkspeed = HBA_PORTSPEED_16GBIT;
594 } else if (hba->linkspeed == LA_32GHZ_LINK) {
595 *linkspeed = HBA_PORTSPEED_32GBIT;
596 } else {
597 *linkspeed = HBA_PORTSPEED_1GBIT;
598 }
599
600 /* Set LIP type */
601 *liptype = port->lip_type;
602 }
603
604 bzero(linkinfo, sizeof (dfc_linkinfo_t));
605
606 linkinfo->a_linkEventTag = hba->link_event_tag;
607 linkinfo->a_linkUp = HBASTATS.LinkUp;
608 linkinfo->a_linkDown = HBASTATS.LinkDown;
609 linkinfo->a_linkMulti = HBASTATS.LinkMultiEvent;
610
611 if (hba->state <= FC_LINK_DOWN) {
612 linkinfo->a_linkState = LNK_DOWN;
613 linkinfo->a_DID = port->prev_did;
614 } else if (hba->state < FC_READY) {
615 linkinfo->a_linkState = LNK_DISCOVERY;
616 } else {
617 linkinfo->a_linkState = LNK_READY;
618 }
619
620 if (linkinfo->a_linkState != LNK_DOWN) {
621 if (hba->topology == TOPOLOGY_LOOP) {
622 if (hba->flag & FC_FABRIC_ATTACHED) {
623 linkinfo->a_topology = LNK_PUBLIC_LOOP;
624 } else {
625 linkinfo->a_topology = LNK_LOOP;
626 }
627
628 linkinfo->a_alpa = port->did & 0xff;
629 linkinfo->a_DID = linkinfo->a_alpa;
630 linkinfo->a_alpaCnt = port->alpa_map[0];
631
632 if (linkinfo->a_alpaCnt > 127) {
633 linkinfo->a_alpaCnt = 127;
634 }
635
636 bcopy((void *)&port->alpa_map[1], linkinfo->a_alpaMap,
637 linkinfo->a_alpaCnt);
638 } else {
639 if (port->node_count == 1) {
640 linkinfo->a_topology = LNK_PT2PT;
641 } else {
642 linkinfo->a_topology = LNK_FABRIC;
643 }
644
645 linkinfo->a_DID = port->did;
646 }
647 }
648
649 bcopy(&hba->wwpn, linkinfo->a_wwpName, 8);
650 bcopy(&hba->wwnn, linkinfo->a_wwnName, 8);
651
652 emlxs_event(port, &emlxs_link_event, bp, size);
653
654 return;
655
656 } /* emlxs_log_link_event() */
657
658
659 extern void
660 emlxs_log_dump_event(emlxs_port_t *port, uint8_t *buffer, uint32_t size)
661 {
662 emlxs_hba_t *hba = HBA;
663 uint8_t *bp;
664
665 /* Check if the event is being requested */
666 if (emlxs_event_check(port, &emlxs_dump_event) == 0) {
667 #ifdef DUMP_SUPPORT
668 /* Schedule a dump thread */
669 emlxs_dump(hba, EMLXS_DRV_DUMP, 0, 0);
670 #endif /* DUMP_SUPPORT */
671 return;
672 }
673
674 if (buffer && size) {
675 /* Save a copy of the buffer for the event log */
676 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
677 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
678 "%s: Unable to allocate buffer.",
679 emlxs_dump_event.label);
680
681 return;
682 }
683
684 bcopy(buffer, bp, size);
685 } else {
686 bp = NULL;
687 size = 0;
688 }
689
690 emlxs_event(port, &emlxs_dump_event, bp, size);
691
692 return;
693
694 } /* emlxs_log_dump_event() */
695
696
697 extern void
698 emlxs_log_temp_event(emlxs_port_t *port, uint32_t type, uint32_t temp)
699 {
700 emlxs_hba_t *hba = HBA;
701 uint32_t *bp;
702 uint32_t size;
703
704 /* Check if the event is being requested */
705 if (emlxs_event_check(port, &emlxs_temp_event) == 0) {
706 #ifdef DUMP_SUPPORT
707 /* Schedule a dump thread */
708 emlxs_dump(hba, EMLXS_TEMP_DUMP, type, temp);
709 #endif /* DUMP_SUPPORT */
710 return;
711 }
712
713 size = 2 * sizeof (uint32_t);
714
715 if (!(bp = (uint32_t *)kmem_alloc(size, KM_NOSLEEP))) {
716 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
717 "%s: Unable to allocate buffer.", emlxs_temp_event.label);
718
719 return;
720 }
721
722 bp[0] = type;
723 bp[1] = temp;
724
725 emlxs_event(port, &emlxs_temp_event, bp, size);
726
727 return;
728
729 } /* emlxs_log_temp_event() */
730
731
732
733 extern void
734 emlxs_log_fcoe_event(emlxs_port_t *port, menlo_init_rsp_t *init_rsp)
735 {
736 emlxs_hba_t *hba = HBA;
737 uint8_t *bp;
738 uint32_t size;
739
740 /* Check if the event is being requested */
741 if (emlxs_event_check(port, &emlxs_fcoe_event) == 0) {
742 return;
743 }
744
745 /* Check if this is a FCOE adapter */
746 if (hba->model_info.device_id != PCI_DEVICE_ID_HORNET) {
747 return;
748 }
749
750 size = sizeof (menlo_init_rsp_t);
751
752 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
753 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
754 "%s: Unable to allocate buffer.", emlxs_fcoe_event.label);
755
756 return;
757 }
758
759 bcopy((uint8_t *)init_rsp, bp, size);
760
761 emlxs_event(port, &emlxs_fcoe_event, bp, size);
762
763 return;
764
765 } /* emlxs_log_fcoe_event() */
766
767
768 extern void
769 emlxs_log_async_event(emlxs_port_t *port, IOCB *iocb)
770 {
771 uint8_t *bp;
772 uint32_t size;
773
774 if (emlxs_event_check(port, &emlxs_async_event) == 0) {
775 return;
776 }
777
778 /* ASYNC_STATUS_CN response size */
779 size = 64;
780
781 if (!(bp = (uint8_t *)kmem_alloc(size, KM_NOSLEEP))) {
782 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
783 "%s: Unable to allocate buffer.", emlxs_async_event.label);
784
785 return;
786 }
787
788 bcopy((uint8_t *)iocb, bp, size);
789
790 emlxs_event(port, &emlxs_async_event, bp, size);
791
792 return;
793
794 } /* emlxs_log_async_event() */
795
796
797 extern uint32_t
798 emlxs_get_dfc_eventinfo(emlxs_port_t *port, HBA_EVENTINFO *eventinfo,
799 uint32_t *eventcount, uint32_t *missed)
800 {
801 emlxs_hba_t *hba = HBA;
802 emlxs_event_queue_t *eventq = &EVENTQ;
803 emlxs_event_entry_t *entry;
804 uint32_t max_events;
805 dfc_linkinfo_t *linkinfo;
806 uint32_t *word;
807 uint8_t *byte;
808 uint8_t linkspeed;
809 uint8_t liptype;
810 fc_affected_id_t *aid;
811 uint32_t events;
812 uint8_t format;
813
814 if (!eventinfo || !eventcount || !missed) {
815 return (DFC_ARG_NULL);
816 }
817
818 max_events = *eventcount;
819 *eventcount = 0;
820 *missed = 0;
821
822 mutex_enter(&eventq->lock);
823
824 /* Account for missed events */
825 if (hba->hba_event.new > hba->hba_event.missed) {
826 hba->hba_event.new -= hba->hba_event.missed;
827 } else {
828 hba->hba_event.new = 0;
829 }
830
831 *missed = hba->hba_event.missed;
832 hba->hba_event.missed = 0;
833
834 if (!hba->hba_event.new) {
835 hba->hba_event.last_id = eventq->next_id - 1;
836 mutex_exit(&eventq->lock);
837 return (0);
838 }
839
840 /* A new event has occurred since last acquisition */
841
842 events = 0;
843 entry = eventq->first;
844 while (entry && (events < max_events)) {
845
846 /* Skip old events */
847 if (entry->id <= hba->hba_event.last_id) {
848 entry = entry->next;
849 continue;
850 }
851
852 /* Process this entry */
853 switch (entry->evt->mask) {
854 case EVT_LINK:
855 byte = (uint8_t *)entry->bp;
856 linkspeed = byte[0];
857 liptype = byte[1];
858 linkinfo = (dfc_linkinfo_t *)&byte[4];
859
860 if (linkinfo->a_linkState == LNK_DOWN) {
861 eventinfo->EventCode =
862 HBA_EVENT_LINK_DOWN;
863 eventinfo->Event.Link_EventInfo.
864 PortFcId = linkinfo->a_DID;
865 eventinfo->Event.Link_EventInfo.
866 Reserved[0] = 0;
867 eventinfo->Event.Link_EventInfo.
868 Reserved[1] = 0;
869 eventinfo->Event.Link_EventInfo.
870 Reserved[2] = 0;
871 } else {
872 eventinfo->EventCode =
873 HBA_EVENT_LINK_UP;
874 eventinfo->Event.Link_EventInfo.
875 PortFcId = linkinfo->a_DID;
876
877 if ((linkinfo->a_topology ==
878 LNK_PUBLIC_LOOP) ||
879 (linkinfo->a_topology ==
880 LNK_LOOP)) {
881 eventinfo->Event.
882 Link_EventInfo.
883 Reserved[0] = 2;
884 } else {
885 eventinfo->Event.
886 Link_EventInfo.
887 Reserved[0] = 1;
888 }
889
890 eventinfo->Event.Link_EventInfo.
891 Reserved[1] = liptype;
892 eventinfo->Event.Link_EventInfo.
893 Reserved[2] = linkspeed;
894 }
895
896 eventinfo++;
897 events++;
898 hba->hba_event.new--;
899 break;
900
901 case EVT_RSCN:
902 word = (uint32_t *)entry->bp;
903 eventinfo->EventCode = HBA_EVENT_RSCN;
904 eventinfo->Event.RSCN_EventInfo.PortFcId =
905 word[0] & 0xFFFFFF;
906 /* word[1] is the RSCN payload command */
907
908 aid = (fc_affected_id_t *)&word[2];
909 format = aid->aff_format;
910
911 switch (format) {
912 case 0: /* Port */
913 eventinfo->Event.RSCN_EventInfo.
914 NPortPage =
915 aid->aff_d_id & 0x00ffffff;
916 break;
917
918 case 1: /* Area */
919 eventinfo->Event.RSCN_EventInfo.
920 NPortPage =
921 aid->aff_d_id & 0x00ffff00;
922 break;
923
924 case 2: /* Domain */
925 eventinfo->Event.RSCN_EventInfo.
926 NPortPage =
927 aid->aff_d_id & 0x00ff0000;
928 break;
929
930 case 3: /* Network */
931 eventinfo->Event.RSCN_EventInfo.
932 NPortPage = 0;
933 break;
934 }
935
936 eventinfo->Event.RSCN_EventInfo.Reserved[0] =
937 0;
938 eventinfo->Event.RSCN_EventInfo.Reserved[1] =
939 0;
940
941 eventinfo++;
942 events++;
943 hba->hba_event.new--;
944 break;
945 }
946
947 hba->hba_event.last_id = entry->id;
948 entry = entry->next;
949 }
950
951 /* Return number of events acquired */
952 *eventcount = events;
953
954 mutex_exit(&eventq->lock);
955
956 return (0);
957
958 } /* emlxs_get_dfc_eventinfo() */
959
960
961 void
962 emlxs_get_dfc_event(emlxs_port_t *port, emlxs_dfc_event_t *dfc_event,
963 uint32_t sleep)
964 {
965 emlxs_hba_t *hba = HBA;
966 emlxs_event_queue_t *eventq = &EVENTQ;
967 emlxs_event_entry_t *entry;
968 uint32_t found;
969 uint32_t mask;
970 uint32_t i;
971 uint32_t size = 0;
972 uint32_t rc;
973
974 if (dfc_event->dataout && dfc_event->size) {
975 size = dfc_event->size;
976 }
977 dfc_event->size = 0;
978
979 /* Calculate the event index */
980 mask = dfc_event->event;
981 for (i = 0; i < 32; i++) {
982 if (mask & 0x01) {
983 break;
984 }
985
986 mask >>= 1;
987 }
988
989 if (i == 32) {
990 return;
991 }
992
993 mutex_enter(&eventq->lock);
994
995 wait_for_event:
996
997 /* Check if no new event has occurred */
998 if (dfc_event->last_id == eventq->last_id[i]) {
999 if (!sleep) {
1000 mutex_exit(&eventq->lock);
1001 return;
1002 }
1003
1004 /* While event is still active and */
1005 /* no new event has been logged */
1006 while ((dfc_event->event & hba->event_mask) &&
1007 (dfc_event->last_id == eventq->last_id[i])) {
1008
1009 rc = cv_wait_sig(&eventq->lock_cv, &eventq->lock);
1010
1011 /* Check if thread was killed by kernel */
1012 if (rc == 0) {
1013 dfc_event->pid = 0;
1014 dfc_event->event = 0;
1015 mutex_exit(&eventq->lock);
1016 return;
1017 }
1018 }
1019
1020 /* If the event is no longer registered then */
1021 /* return immediately */
1022 if (!(dfc_event->event & hba->event_mask)) {
1023 mutex_exit(&eventq->lock);
1024 return;
1025 }
1026 }
1027
1028 /* !!! An event has occurred since last_id !!! */
1029
1030 /* Check if event data is not being requested */
1031 if (!size) {
1032 /* If so, then just return the last event id */
1033 dfc_event->last_id = eventq->last_id[i];
1034
1035 mutex_exit(&eventq->lock);
1036 return;
1037 }
1038
1039 /* !!! The requester wants the next event buffer !!! */
1040
1041 found = 0;
1042 entry = eventq->first;
1043 while (entry) {
1044 if ((entry->id > dfc_event->last_id) &&
1045 (entry->evt->mask == dfc_event->event)) {
1046 found = 1;
1047 break;
1048 }
1049
1050 entry = entry->next;
1051 }
1052
1053 if (!found) {
1054 /* Update last_id to the last known event */
1055 dfc_event->last_id = eventq->last_id[i];
1056
1057 /* Try waiting again if we can */
1058 goto wait_for_event;
1059 }
1060
1061 /* !!! Next event found !!! */
1062
1063 /* Copy the context buffer to the buffer provided */
1064 if (entry->bp && entry->size) {
1065 if (entry->size < size) {
1066 size = entry->size;
1067 }
1068
1069 bcopy((void *)entry->bp, dfc_event->dataout, size);
1070
1071 /* Event has been retrieved by DFCLIB */
1072 entry->flag |= EMLXS_DFC_EVENT_DONE;
1073
1074 dfc_event->size = size;
1075 }
1076
1077 dfc_event->last_id = entry->id;
1078
1079 mutex_exit(&eventq->lock);
1080
1081 return;
1082
1083 } /* emlxs_get_dfc_event() */
1084
1085
1086 uint32_t
1087 emlxs_kill_dfc_event(emlxs_port_t *port, emlxs_dfc_event_t *dfc_event)
1088 {
1089 emlxs_hba_t *hba = HBA;
1090 emlxs_event_queue_t *eventq = &EVENTQ;
1091
1092 mutex_enter(&eventq->lock);
1093 dfc_event->pid = 0;
1094 dfc_event->event = 0;
1095 cv_broadcast(&eventq->lock_cv);
1096 mutex_exit(&eventq->lock);
1097
1098 return (0);
1099
1100 } /* emlxs_kill_dfc_event() */
1101
1102
1103 #ifdef SAN_DIAG_SUPPORT
1104 extern void
1105 emlxs_log_sd_basic_els_event(emlxs_port_t *port, uint32_t subcat,
1106 HBA_WWN *portname, HBA_WWN *nodename)
1107 {
1108 struct sd_plogi_rcv_v0 *bp;
1109 uint32_t size;
1110
1111 /* Check if the event is being requested */
1112 if (emlxs_event_check(port, &emlxs_sd_els_event) == 0) {
1113 return;
1114 }
1115
1116 size = sizeof (struct sd_plogi_rcv_v0);
1117
1118 if (!(bp = (struct sd_plogi_rcv_v0 *)kmem_alloc(size, KM_NOSLEEP))) {
1119 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1120 "%s: Unable to allocate buffer.", emlxs_sd_els_event.label);
1121
1122 return;
1123 }
1124
1125 /*
1126 * we are using version field to store subtype, libdfc
1127 * will fix this up before returning data to app.
1128 */
1129 bp->sd_plogir_version = subcat;
1130 bcopy((uint8_t *)portname, (uint8_t *)&bp->sd_plogir_portname,
1131 sizeof (HBA_WWN));
1132 bcopy((uint8_t *)nodename, (uint8_t *)&bp->sd_plogir_nodename,
1133 sizeof (HBA_WWN));
1134
1135 emlxs_event(port, &emlxs_sd_els_event, bp, size);
1136
1137 return;
1138
1139 } /* emlxs_log_sd_basic_els_event() */
1140
1141
1142 extern void
1143 emlxs_log_sd_prlo_event(emlxs_port_t *port, HBA_WWN *remoteport)
1144 {
1145 struct sd_prlo_rcv_v0 *bp;
1146 uint32_t size;
1147
1148 /* Check if the event is being requested */
1149 if (emlxs_event_check(port, &emlxs_sd_els_event) == 0) {
1150 return;
1151 }
1152
1153 size = sizeof (struct sd_prlo_rcv_v0);
1154
1155 if (!(bp = (struct sd_prlo_rcv_v0 *)kmem_alloc(size,
1156 KM_NOSLEEP))) {
1157 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1158 "%s PRLO: Unable to allocate buffer.",
1159 emlxs_sd_els_event.label);
1160
1161 return;
1162 }
1163
1164 /*
1165 * we are using version field to store subtype, libdfc
1166 * will fix this up before returning data to app.
1167 */
1168 bp->sd_prlor_version = SD_ELS_SUBCATEGORY_PRLO_RCV;
1169 bcopy((uint8_t *)remoteport, (uint8_t *)&bp->sd_prlor_remoteport,
1170 sizeof (HBA_WWN));
1171
1172 emlxs_event(port, &emlxs_sd_els_event, bp, size);
1173
1174 return;
1175
1176 } /* emlxs_log_sd_prlo_event() */
1177
1178
1179 extern void
1180 emlxs_log_sd_lsrjt_event(emlxs_port_t *port, HBA_WWN *remoteport,
1181 uint32_t orig_cmd, uint32_t reason, uint32_t reason_expl)
1182 {
1183 struct sd_lsrjt_rcv_v0 *bp;
1184 uint32_t size;
1185
1186 /* Check if the event is being requested */
1187 if (emlxs_event_check(port, &emlxs_sd_els_event) == 0) {
1188 return;
1189 }
1190
1191 size = sizeof (struct sd_lsrjt_rcv_v0);
1192
1193 if (!(bp = (struct sd_lsrjt_rcv_v0 *)kmem_alloc(size,
1194 KM_NOSLEEP))) {
1195 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1196 "%s LSRJT: Unable to allocate buffer.",
1197 emlxs_sd_els_event.label);
1198
1199 return;
1200 }
1201
1202 /*
1203 * we are using version field to store subtype, libdfc
1204 * will fix this up before returning data to app.
1205 */
1206 bp->sd_lsrjtr_version = SD_ELS_SUBCATEGORY_LSRJT_RCV;
1207 bcopy((uint8_t *)remoteport, (uint8_t *)&bp->sd_lsrjtr_remoteport,
1208 sizeof (HBA_WWN));
1209 bp->sd_lsrjtr_original_cmd = orig_cmd;
1210 bp->sd_lsrjtr_reasoncode = reason;
1211 bp->sd_lsrjtr_reasoncodeexpl = reason_expl;
1212
1213 emlxs_event(port, &emlxs_sd_els_event, bp, size);
1214
1215 return;
1216
1217 } /* emlxs_log_sd_lsrjt_event() */
1218
1219
1220 extern void
1221 emlxs_log_sd_fc_bsy_event(emlxs_port_t *port, HBA_WWN *remoteport)
1222 {
1223 struct sd_pbsy_rcv_v0 *bp;
1224 uint32_t size;
1225
1226 /* Check if the event is being requested */
1227 if (emlxs_event_check(port, &emlxs_sd_fabric_event) == 0) {
1228 return;
1229 }
1230
1231 size = sizeof (struct sd_pbsy_rcv_v0);
1232
1233 if (!(bp = (struct sd_pbsy_rcv_v0 *)kmem_alloc(size,
1234 KM_NOSLEEP))) {
1235 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1236 "%s BSY: Unable to allocate buffer.",
1237 emlxs_sd_fabric_event.label);
1238
1239 return;
1240 }
1241
1242 /*
1243 * we are using version field to store subtype, libdfc
1244 * will fix this up before returning data to app.
1245 */
1246 if (remoteport == NULL)
1247 bp->sd_pbsyr_evt_version = SD_FABRIC_SUBCATEGORY_FABRIC_BUSY;
1248 else
1249 {
1250 bp->sd_pbsyr_evt_version = SD_FABRIC_SUBCATEGORY_PORT_BUSY;
1251 bcopy((uint8_t *)remoteport, (uint8_t *)&bp->sd_pbsyr_rport,
1252 sizeof (HBA_WWN));
1253 }
1254
1255 emlxs_event(port, &emlxs_sd_fabric_event, bp, size);
1256
1257 return;
1258
1259 } /* emlxs_log_sd_fc_bsy_event() */
1260
1261
1262 extern void
1263 emlxs_log_sd_fc_rdchk_event(emlxs_port_t *port, HBA_WWN *remoteport,
1264 uint32_t lun, uint32_t opcode, uint32_t fcp_param)
1265 {
1266 struct sd_fcprdchkerr_v0 *bp;
1267 uint32_t size;
1268
1269 /* Check if the event is being requested */
1270 if (emlxs_event_check(port, &emlxs_sd_fabric_event) == 0) {
1271 return;
1272 }
1273
1274 size = sizeof (struct sd_fcprdchkerr_v0);
1275
1276 if (!(bp = (struct sd_fcprdchkerr_v0 *)kmem_alloc(size,
1277 KM_NOSLEEP))) {
1278 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1279 "%s RDCHK: Unable to allocate buffer.",
1280 emlxs_sd_fabric_event.label);
1281
1282 return;
1283 }
1284
1285 /*
1286 * we are using version field to store subtype, libdfc
1287 * will fix this up before returning data to app.
1288 */
1289 bp->sd_fcprdchkerr_version = SD_FABRIC_SUBCATEGORY_FCPRDCHKERR;
1290 bcopy((uint8_t *)remoteport, (uint8_t *)&bp->sd_fcprdchkerr_rport,
1291 sizeof (HBA_WWN));
1292 bp->sd_fcprdchkerr_lun = lun;
1293 bp->sd_fcprdchkerr_opcode = opcode;
1294 bp->sd_fcprdchkerr_fcpiparam = fcp_param;
1295
1296 emlxs_event(port, &emlxs_sd_fabric_event, bp, size);
1297
1298 return;
1299
1300 } /* emlxs_log_sd_rdchk_event() */
1301
1302
1303 extern void
1304 emlxs_log_sd_scsi_event(emlxs_port_t *port, uint32_t type,
1305 HBA_WWN *remoteport, int32_t lun)
1306 {
1307 struct sd_scsi_generic_v0 *bp;
1308 uint32_t size;
1309
1310 /* Check if the event is being requested */
1311 if (emlxs_event_check(port, &emlxs_sd_scsi_event) == 0) {
1312 return;
1313 }
1314
1315 size = sizeof (struct sd_scsi_generic_v0);
1316
1317 if (!(bp = (struct sd_scsi_generic_v0 *)kmem_alloc(size,
1318 KM_NOSLEEP))) {
1319 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1320 "%s: Unable to allocate buffer.",
1321 emlxs_sd_scsi_event.label);
1322
1323 return;
1324 }
1325
1326 /*
1327 * we are using version field to store subtype, libdfc
1328 * will fix this up before returning data to app.
1329 */
1330 bp->sd_scsi_generic_version = type;
1331 bcopy((uint8_t *)remoteport, (uint8_t *)&bp->sd_scsi_generic_rport,
1332 sizeof (HBA_WWN));
1333 bp->sd_scsi_generic_lun = lun;
1334
1335 emlxs_event(port, &emlxs_sd_scsi_event, bp, size);
1336
1337 return;
1338
1339 } /* emlxs_log_sd_scsi_event() */
1340
1341
1342 extern void
1343 emlxs_log_sd_scsi_check_event(emlxs_port_t *port, HBA_WWN *remoteport,
1344 uint32_t lun, uint32_t cmdcode, uint32_t sensekey,
1345 uint32_t asc, uint32_t ascq)
1346 {
1347 struct sd_scsi_checkcond_v0 *bp;
1348 uint32_t size;
1349
1350 /* Check if the event is being requested */
1351 if (emlxs_event_check(port, &emlxs_sd_scsi_event) == 0) {
1352 return;
1353 }
1354
1355 size = sizeof (struct sd_scsi_checkcond_v0);
1356
1357 if (!(bp = (struct sd_scsi_checkcond_v0 *)kmem_alloc(size,
1358 KM_NOSLEEP))) {
1359 EMLXS_MSGF(EMLXS_CONTEXT, &emlxs_event_debug_msg,
1360 "%s CHECK: Unable to allocate buffer.",
1361 emlxs_sd_scsi_event.label);
1362
1363 return;
1364 }
1365
1366 /*
1367 * we are using version field to store subtype, libdfc
1368 * will fix this up before returning data to app.
1369 */
1370 bp->sd_scsi_checkcond_version = SD_SCSI_SUBCATEGORY_CHECKCONDITION;
1371 bcopy((uint8_t *)remoteport, (uint8_t *)&bp->sd_scsi_checkcond_rport,
1372 sizeof (HBA_WWN));
1373 bp->sd_scsi_checkcond_lun = lun;
1374 bp->sd_scsi_checkcond_cmdcode = cmdcode;
1375 bp->sd_scsi_checkcond_sensekey = sensekey;
1376 bp->sd_scsi_checkcond_asc = asc;
1377 bp->sd_scsi_checkcond_ascq = ascq;
1378
1379 emlxs_event(port, &emlxs_sd_scsi_event, bp, size);
1380
1381 return;
1382
1383 } /* emlxs_log_sd_scsi_check_event() */
1384
1385
1386 void
1387 emlxs_get_sd_event(emlxs_port_t *port, emlxs_dfc_event_t *dfc_event,
1388 uint32_t sleep)
1389 {
1390 emlxs_hba_t *hba = HBA;
1391 emlxs_event_queue_t *eventq = &EVENTQ;
1392 emlxs_event_entry_t *entry;
1393 uint32_t found;
1394 uint32_t mask;
1395 uint32_t i;
1396 uint32_t size = 0;
1397 uint32_t rc;
1398
1399 if (dfc_event->dataout && dfc_event->size) {
1400 size = dfc_event->size;
1401 }
1402 dfc_event->size = 0;
1403
1404 /* Calculate the event index */
1405 mask = dfc_event->event;
1406 for (i = 0; i < 32; i++) {
1407 if (mask & 0x01) {
1408 break;
1409 }
1410
1411 mask >>= 1;
1412 }
1413
1414 if (i == 32) {
1415 return;
1416 }
1417
1418 mutex_enter(&eventq->lock);
1419
1420 wait_for_event:
1421
1422 /* Check if no new event has ocurred */
1423 if (dfc_event->last_id == eventq->last_id[i]) {
1424 if (!sleep) {
1425 mutex_exit(&eventq->lock);
1426 return;
1427 }
1428
1429 /* While event is active and no new event has been logged */
1430 while ((dfc_event->event & port->sd_event_mask) &&
1431 (dfc_event->last_id == eventq->last_id[i])) {
1432 rc = cv_wait_sig(&eventq->lock_cv, &eventq->lock);
1433
1434 /* Check if thread was killed by kernel */
1435 if (rc == 0) {
1436 dfc_event->pid = 0;
1437 dfc_event->event = 0;
1438 mutex_exit(&eventq->lock);
1439 return;
1440 }
1441 }
1442
1443 /* If the event is no longer registered then return */
1444 if (!(dfc_event->event & port->sd_event_mask)) {
1445 mutex_exit(&eventq->lock);
1446 return;
1447 }
1448 }
1449
1450 /* !!! An event has occurred since last_id !!! */
1451
1452 /* Check if event data is not being requested */
1453 if (!size) {
1454 /* If so, then just return the last event id */
1455 dfc_event->last_id = eventq->last_id[i];
1456
1457 mutex_exit(&eventq->lock);
1458 return;
1459 }
1460
1461 /* !!! The requester wants the next event buffer !!! */
1462
1463 found = 0;
1464 entry = eventq->first;
1465 while (entry) {
1466 if ((entry->id > dfc_event->last_id) &&
1467 (entry->port == (void *)port) &&
1468 (entry->evt->mask == dfc_event->event)) {
1469 found = 1;
1470 break;
1471 }
1472
1473 entry = entry->next;
1474 }
1475
1476 if (!found) {
1477 /* Update last_id to the last known event */
1478 dfc_event->last_id = eventq->last_id[i];
1479
1480 /* Try waiting again if we can */
1481 goto wait_for_event;
1482 }
1483
1484 /* !!! Next event found !!! */
1485
1486 /* Copy the context buffer to the buffer provided */
1487 if (entry->bp && entry->size) {
1488 if (entry->size < size) {
1489 size = entry->size;
1490 }
1491
1492 bcopy((void *)entry->bp, dfc_event->dataout, size);
1493
1494 /* Event has been retrieved by SANDIAG */
1495 entry->flag |= EMLXS_SD_EVENT_DONE;
1496
1497 dfc_event->size = size;
1498 }
1499
1500 dfc_event->last_id = entry->id;
1501
1502 mutex_exit(&eventq->lock);
1503
1504 return;
1505
1506 } /* emlxs_get_sd_event */
1507 #endif /* SAN_DIAG_SUPPORT */