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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <sys/conf.h>
27 #include <sys/ddi.h>
28 #include <sys/stat.h>
29 #include <sys/pci.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/file.h>
33 #include <sys/cred.h>
34 #include <sys/byteorder.h>
35 #include <sys/atomic.h>
36 #include <sys/scsi/scsi.h>
37 #include <sys/mac_client.h>
38 #include <sys/modhash.h>
39
40 /*
41 * leadville header files
42 */
43 #include <sys/fibre-channel/fc.h>
44 #include <sys/fibre-channel/impl/fc_fcaif.h>
45
46 /*
47 * fcoe header files
48 */
49 #include <sys/fcoe/fcoe_common.h>
50
51 /*
52 * fcoei header files
53 */
54 #include <fcoei.h>
55
56 /*
57 * forward declaration of stack functions
58 */
59 static uint32_t fcoei_xch_check(
60 mod_hash_key_t key, mod_hash_val_t *val, void *arg);
61 static int fcoei_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
62 static int fcoei_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
63 static int fcoei_open(dev_t *devp, int flag, int otype, cred_t *credp);
64 static int fcoei_close(dev_t dev, int flag, int otype, cred_t *credp);
65 static int fcoei_ioctl(
66 dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval);
67 static int fcoei_attach_init(fcoei_soft_state_t *ss);
68 static int fcoei_detach_uninit(fcoei_soft_state_t *ss);
69 static void fcoei_watchdog(void *arg);
70 static void fcoei_process_events(fcoei_soft_state_t *ss);
71 static void fcoei_trigger_fp_attach(void *arg);
72 static void fcoei_abts_exchange(fcoei_exchange_t *xch);
73 static void fcoei_clear_watchdog_jobs(fcoei_soft_state_t *ss);
74
75 /*
76 * Driver identificaton stuff
77 */
78 static struct cb_ops fcoei_cb_ops = {
79 fcoei_open,
80 fcoei_close,
81 nodev,
82 nodev,
83 nodev,
84 nodev,
85 nodev,
86 fcoei_ioctl,
87 nodev,
88 nodev,
89 nodev,
90 nochpoll,
91 ddi_prop_op,
92 0,
93 D_MP | D_NEW | D_HOTPLUG,
94 CB_REV,
95 nodev,
96 nodev
97 };
98
99 static struct dev_ops fcoei_ops = {
100 DEVO_REV,
101 0,
102 nodev,
103 nulldev,
104 nulldev,
105 fcoei_attach,
106 fcoei_detach,
107 nodev,
108 &fcoei_cb_ops,
109 NULL,
110 ddi_power,
111 ddi_quiesce_not_needed
112 };
113
114 static struct modldrv modldrv = {
115 &mod_driverops,
116 FCOEI_NAME_VERSION,
117 &fcoei_ops,
118 };
119
120 static struct modlinkage modlinkage = {
121 MODREV_1,
122 { &modldrv, NULL }
123 };
124
125 /*
126 * Driver's global variables
127 */
128 void *fcoei_state = NULL;
129 int fcoei_use_ext_log = 0;
130
131 /*
132 * Common loadable module entry points _init, _fini, _info
133 */
134 int
135 _init(void)
136 {
137 int ret;
138
139 ret = ddi_soft_state_init(&fcoei_state, sizeof (fcoei_soft_state_t), 0);
140 if (ret != DDI_SUCCESS) {
141 FCOEI_LOG(__FUNCTION__, "soft state init failed: %x", ret);
142 return (ret);
143 }
144
145 ret = mod_install(&modlinkage);
146 if (ret != 0) {
147 ddi_soft_state_fini(&fcoei_state);
148 FCOEI_LOG(__FUNCTION__, "fcoei mod_install failed: %x", ret);
149 return (ret);
150 }
151
152 /*
153 * Let FCTL initialize devo_bus_ops
154 */
155 fc_fca_init(&fcoei_ops);
156
157 FCOEI_LOG(__FUNCTION__, "fcoei _init succeeded");
158 return (ret);
159 }
160
161 int
162 _fini(void)
163 {
164 int ret;
165
166 ret = mod_remove(&modlinkage);
167 if (ret != 0) {
168 FCOEI_EXT_LOG(__FUNCTION__, "fcoei mod_remove failed: %x", ret);
169 return (ret);
170 }
171
172 ddi_soft_state_fini(&fcoei_state);
173 FCOEI_LOG(__FUNCTION__, "fcoei _fini succeeded");
174 return (ret);
175 }
176
177 int
178 _info(struct modinfo *modinfop)
179 {
180 return (mod_info(&modlinkage, modinfop));
181 }
182
183 /*
184 * Autoconfiguration entry points: attach, detach, getinfo
185 */
186
187 static int
188 fcoei_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
189 {
190 int ret;
191 int fcoe_ret;
192 int instance;
193 fcoei_soft_state_t *ss;
194
195 instance = ddi_get_instance(dip);
196 FCOEI_LOG(__FUNCTION__, "instance is %d", instance);
197 switch (cmd) {
198 case DDI_ATTACH:
199 ret = ddi_soft_state_zalloc(fcoei_state, instance);
200 if (ret != DDI_SUCCESS) {
201 FCOEI_LOG(__FUNCTION__, "ss zalloc failed: %x", ret);
202 return (ret);
203 }
204
205 /*
206 * Get the soft state, and do basic initialization with dip
207 */
208 ss = ddi_get_soft_state(fcoei_state, instance);
209 ss->ss_dip = dip;
210
211 fcoe_ret = fcoei_attach_init(ss);
212 if (fcoe_ret != FCOE_SUCCESS) {
213 ddi_soft_state_free(fcoei_state, instance);
214 FCOEI_LOG(__FUNCTION__, "fcoei_attach_init failed: "
215 "%x", fcoe_ret);
216 return (DDI_FAILURE);
217 }
218
219 ss->ss_flags |= SS_FLAG_TRIGGER_FP_ATTACH;
220 (void) timeout(fcoei_trigger_fp_attach, ss, FCOE_SEC2TICK(1));
221 FCOEI_LOG(__FUNCTION__, "fcoei_attach succeeded: dip-%p, "
222 "cmd-%x", dip, cmd);
223 return (DDI_SUCCESS);
224
225 case DDI_RESUME:
226 return (DDI_SUCCESS);
227
228 default:
229 FCOEI_LOG(__FUNCTION__, "unsupported attach cmd-%X", cmd);
230 return (DDI_FAILURE);
231 }
232 }
233
234 static int
235 fcoei_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
236 {
237 int fcoe_ret;
238 int instance;
239 fcoei_soft_state_t *ss;
240
241 instance = ddi_get_instance(dip);
242 ss = ddi_get_soft_state(fcoei_state, instance);
243 if (ss == NULL) {
244 FCOEI_LOG(__FUNCTION__, "get ss failed: dip-%p", dip);
245 return (DDI_FAILURE);
246 }
247
248 switch (cmd) {
249 case DDI_DETACH:
250 if (ss->ss_flags & SS_FLAG_TRIGGER_FP_ATTACH) {
251 FCOEI_LOG(__FUNCTION__, "still await fp attach");
252 return (DDI_FAILURE);
253 }
254
255 if (ss->ss_flags & SS_FLAG_LV_BOUND) {
256 FCOEI_LOG(__FUNCTION__, "fp is not detached yet");
257 return (DDI_FAILURE);
258 }
259
260 fcoe_ret = fcoei_detach_uninit(ss);
261 if (fcoe_ret != FCOE_SUCCESS) {
262 FCOEI_LOG(__FUNCTION__, "fcoei_detach_uninit failed:"
263 " dip-%p, fcoe_ret-%d", dip, fcoe_ret);
264 return (DDI_FAILURE);
265 }
266
267 FCOEI_LOG(__FUNCTION__, "succeeded: dip-%p, cmd-%x", dip, cmd);
268 return (DDI_SUCCESS);
269
270 case DDI_SUSPEND:
271 return (DDI_SUCCESS);
272
273 default:
274 FCOEI_LOG(__FUNCTION__, "unspported detach cmd-%X", cmd);
275 return (DDI_FAILURE);
276 }
277 }
278
279 /*
280 * Device access entry points: open, close, ioctl
281 */
282
283 static int
284 fcoei_open(dev_t *devp, int flag, int otype, cred_t *credp)
285 {
286 fcoei_soft_state_t *ss;
287
288 if (otype != OTYP_CHR) {
289 FCOEI_LOG(__FUNCTION__, "flag: %x", flag);
290 return (EINVAL);
291 }
292
293 if (drv_priv(credp)) {
294 return (EPERM);
295 }
296
297 /*
298 * First of all, get related soft state
299 */
300 ss = ddi_get_soft_state(fcoei_state, (int)getminor(*devp));
301 if (ss == NULL) {
302 return (ENXIO);
303 }
304
305 mutex_enter(&ss->ss_ioctl_mutex);
306 if (ss->ss_ioctl_flags & FCOEI_IOCTL_FLAG_OPEN) {
307 /*
308 * We don't support concurrent open
309 */
310 mutex_exit(&ss->ss_ioctl_mutex);
311 return (EBUSY);
312 }
313
314 ss->ss_ioctl_flags |= FCOEI_IOCTL_FLAG_OPEN;
315 mutex_exit(&ss->ss_ioctl_mutex);
316
317 return (0);
318 }
319
320 static int
321 fcoei_close(dev_t dev, int flag, int otype, cred_t *credp)
322 {
323 fcoei_soft_state_t *ss;
324
325 if (otype != OTYP_CHR) {
326 FCOEI_LOG(__FUNCTION__, "flag: %x, %p", flag, credp);
327 return (EINVAL);
328 }
329
330 /*
331 * First of all, get related soft state
332 */
333 ss = ddi_get_soft_state(fcoei_state, (int)getminor(dev));
334 if (ss == NULL) {
335 return (ENXIO);
336 }
337
338 mutex_enter(&ss->ss_ioctl_mutex);
339 if (!(ss->ss_ioctl_flags & FCOEI_IOCTL_FLAG_OPEN)) {
340 /*
341 * If it's not open, we can exit
342 */
343
344 mutex_exit(&ss->ss_ioctl_mutex);
345 return (ENODEV);
346 }
347
348 ss->ss_ioctl_flags &= ~FCOEI_IOCTL_FLAG_OPEN;
349 mutex_exit(&ss->ss_ioctl_mutex);
350
351 return (0);
352 }
353
354 static int
355 fcoei_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
356 cred_t *credp, int *rval)
357 {
358 fcoei_soft_state_t *ss;
359 int ret = 0;
360
361 if (drv_priv(credp) != 0) {
362 FCOEI_LOG(__FUNCTION__, "data: %p, %x", data, mode);
363 return (EPERM);
364 }
365
366 /*
367 * Get related soft state
368 */
369 ss = ddi_get_soft_state(fcoei_state, (int32_t)getminor(dev));
370 if (!ss) {
371 return (ENXIO);
372 }
373
374 /*
375 * Process ioctl
376 */
377 switch (cmd) {
378
379 default:
380 FCOEI_LOG(__FUNCTION__, "ioctl-0x%02X", cmd);
381 ret = ENOTTY;
382 }
383
384 /*
385 * Set return value
386 */
387 *rval = ret;
388 return (ret);
389 }
390
391 /*
392 * fcoei_attach_init
393 * init related stuff of the soft state
394 *
395 * Input:
396 * ss = the soft state that will be processed
397 *
398 * Return:
399 * if it succeeded or not
400 *
401 * Comment:
402 * N/A
403 */
404 static int
405 fcoei_attach_init(fcoei_soft_state_t *ss)
406 {
407 fcoe_port_t *eport;
408 fcoe_client_t client_fcoei;
409 char taskq_name[32];
410 int ret;
411 la_els_logi_t *els = &ss->ss_els_logi;
412 svc_param_t *class3_param;
413
414 /*
415 * Register fcoei to FCOE as its client
416 */
417 client_fcoei.ect_eport_flags = EPORT_FLAG_INI_MODE |
418 EPORT_FLAG_IS_DIRECT_P2P;
419 client_fcoei.ect_max_fc_frame_size = FCOE_MAX_FC_FRAME_SIZE;
420 client_fcoei.ect_private_frame_struct_size = sizeof (fcoei_frame_t);
421 fcoei_init_ect_vectors(&client_fcoei);
422 client_fcoei.ect_client_port_struct = ss;
423 client_fcoei.ect_fcoe_ver = FCOE_VER_NOW;
424 FCOEI_LOG(__FUNCTION__, "version: %x %x", FCOE_VER_NOW, fcoe_ver_now);
425 ret = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip,
426 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_id", -1);
427 if (ret == -1) {
428 FCOEI_LOG(__FUNCTION__, "get mac_id failed");
429 return (DDI_FAILURE);
430 } else {
431 client_fcoei.ect_channelid = ret;
432 }
433
434 /*
435 * It's fcoe's responsiblity to initialize eport's all elements,
436 * so we needn't do eport initialization
437 */
438 eport = fcoe_register_client(&client_fcoei);
439 if (eport == NULL) {
440 goto fail_register_client;
441 } else {
442 ss->ss_eport = eport;
443 FCOE_SET_DEFAULT_FPORT_ADDR(eport->eport_efh_dst);
444 }
445
446 /*
447 * Now it's time to register fca_tran to FCTL
448 * Remember fc_local_port is transparent to FCA (fcoei)
449 */
450 ss->ss_fca_tran.fca_version = FCTL_FCA_MODREV_5;
451 ss->ss_fca_tran.fca_numports = 1;
452 ss->ss_fca_tran.fca_pkt_size = sizeof (fcoei_exchange_t);
453 ss->ss_fca_tran.fca_cmd_max = 2048;
454
455 /*
456 * scsi_tran_hba_setup could need these stuff
457 */
458 ss->ss_fca_tran.fca_dma_lim = NULL;
459 ss->ss_fca_tran.fca_iblock = NULL;
460 ss->ss_fca_tran.fca_dma_attr = NULL;
461 ss->ss_fca_tran.fca_acc_attr = NULL;
462
463 /*
464 * Initialize vectors
465 */
466 fcoei_init_fcatran_vectors(&ss->ss_fca_tran);
467
468 /*
469 * fc_fca_attach only sets driver's private, it has nothing to with
470 * common port object between fcoei and leadville.
471 * After this attach, fp_attach will be triggered, and it will call
472 * fca_bind_port to let fcoei to know about common port object.
473 */
474 if (fc_fca_attach(ss->ss_dip, &ss->ss_fca_tran) != DDI_SUCCESS) {
475 goto fail_fca_attach;
476 }
477
478 /*
479 * It's time to do ss initialization
480 */
481 ret = ddi_create_minor_node(ss->ss_dip, "admin",
482 S_IFCHR, ddi_get_instance(ss->ss_dip), DDI_NT_NEXUS, 0);
483 if (ret != DDI_SUCCESS) {
484 goto fail_minor_node;
485 }
486
487 ss->ss_flags = 0;
488 ss->ss_port = NULL;
489 /*
490 * ss->ss_eport has been initialized
491 */
492
493 ss->ss_sol_oxid_hash = mod_hash_create_idhash(
494 "fcoei_sol_oxid_hash", FCOEI_SOL_HASH_SIZE,
495 mod_hash_null_valdtor);
496 ss->ss_unsol_rxid_hash = mod_hash_create_idhash(
497 "fcoei_unsol_rxid_hash", FCOEI_UNSOL_HASH_SIZE,
498 mod_hash_null_valdtor);
499 list_create(&ss->ss_comp_xch_list, sizeof (fcoei_exchange_t),
500 offsetof(fcoei_exchange_t, xch_comp_node));
501 ss->ss_next_sol_oxid = 0xFFFF;
502 ss->ss_next_unsol_rxid = 0xFFFF;
503
504 mutex_init(&ss->ss_watchdog_mutex, 0, MUTEX_DRIVER, 0);
505 cv_init(&ss->ss_watchdog_cv, NULL, CV_DRIVER, NULL);
506 (void) snprintf(taskq_name, 32, "leadville_fcoei_%d_taskq",
507 ddi_get_instance(ss->ss_dip));
508 taskq_name[31] = 0;
509 ss->ss_taskq = ddi_taskq_create(ss->ss_dip,
510 taskq_name, 64, TASKQ_DEFAULTPRI, DDI_SLEEP);
511
512 ss->ss_link_state = FC_STATE_OFFLINE;
513 ss->ss_link_speed = 0;
514 ss->ss_port_event_counter = 0;
515
516 list_create(&ss->ss_event_list, sizeof (fcoei_event_t),
517 offsetof(fcoei_event_t, ae_node));
518
519 ss->ss_sol_cnt1 = 0;
520 ss->ss_sol_cnt2 = 0;
521 ss->ss_sol_cnt = &ss->ss_sol_cnt1;
522 ss->ss_unsol_cnt1 = 0;
523 ss->ss_unsol_cnt2 = 0;
524 ss->ss_unsol_cnt = &ss->ss_unsol_cnt1;
525 ss->ss_ioctl_flags = 0;
526
527 mutex_init(&ss->ss_ioctl_mutex, 0, MUTEX_DRIVER, 0);
528
529 bcopy(eport->eport_portwwn, els->nport_ww_name.raw_wwn, 8);
530 bcopy(eport->eport_nodewwn, els->node_ww_name.raw_wwn, 8);
531 els->common_service.fcph_version = 0x2008;
532 els->common_service.btob_credit = 3;
533 els->common_service.cmn_features = 0x8800;
534 els->common_service.conc_sequences = 0xff;
535 els->common_service.relative_offset = 3;
536 els->common_service.e_d_tov = 0x07d0;
537 class3_param = (svc_param_t *)&els->class_3;
538 class3_param->class_opt = 0x8800;
539 class3_param->rcv_size = els->common_service.rx_bufsize = 2048;
540 class3_param->conc_sequences = 0xff;
541 class3_param->open_seq_per_xchng = 1;
542
543 /*
544 * Fill out RNID Management Information
545 */
546 bcopy(ss->ss_eport->eport_portwwn, ss->ss_rnid.global_id, 8);
547 ss->ss_rnid.unit_type = FCOEI_RNID_HBA;
548 ss->ss_rnid.ip_version = FCOEI_RNID_IPV4;
549
550 /*
551 * Start our watchdog
552 */
553 (void) ddi_taskq_dispatch(ss->ss_taskq,
554 fcoei_watchdog, ss, DDI_SLEEP);
555 while (!(ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING)) {
556 delay(50);
557 }
558
559 /*
560 * Report the device to the system
561 */
562 ddi_report_dev(ss->ss_dip);
563 return (DDI_SUCCESS);
564
565
566 fail_minor_node:
567 FCOEI_LOG(__FUNCTION__, "fail_minor_node");
568 (void) fc_fca_detach(ss->ss_dip);
569
570 fail_fca_attach:
571 eport->eport_deregister_client(eport);
572 FCOEI_LOG(__FUNCTION__, "fail_fca_attach");
573
574 fail_register_client:
575 FCOEI_LOG(__FUNCTION__, "fail_register_client");
576 return (DDI_FAILURE);
577 }
578
579 /*
580 * fcoei_detach_uninit
581 * uninit related stuff of the soft state
582 *
583 * Input:
584 * ss = the soft state that will be processed
585 *
586 * Return:
587 * if it succeeded or not
588 *
589 * Comment:
590 * N/A
591 */
592 int
593 fcoei_detach_uninit(fcoei_soft_state_t *ss)
594 {
595 /*
596 * Stop watchdog first
597 */
598 if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
599 ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG;
600 cv_broadcast(&ss->ss_watchdog_cv);
601 }
602
603 /*
604 * Destroy the taskq
605 */
606 ddi_taskq_wait(ss->ss_taskq);
607 ddi_taskq_destroy(ss->ss_taskq);
608
609 /*
610 * Release all allocated resources
611 */
612 mutex_destroy(&ss->ss_ioctl_mutex);
613 mutex_destroy(&ss->ss_watchdog_mutex);
614 cv_destroy(&ss->ss_watchdog_cv);
615 mod_hash_destroy_idhash(ss->ss_sol_oxid_hash);
616 mod_hash_destroy_idhash(ss->ss_unsol_rxid_hash);
617 list_destroy(&ss->ss_event_list);
618 ss->ss_eport->eport_deregister_client(ss->ss_eport);
619 ddi_remove_minor_node(ss->ss_dip, NULL);
620
621 /*
622 * Release itself
623 */
624 ddi_soft_state_free(fcoei_state, ddi_get_instance(ss->ss_dip));
625 return (FCOE_SUCCESS);
626 }
627
628 /*
629 * fcoei_watchdog
630 * Perform periodic checking and routine tasks
631 *
632 * Input:
633 * arg = the soft state that will be processed
634 *
635 * Return:
636 * N/A
637 *
638 * Comment:
639 * N/A
640 */
641 static void
642 fcoei_watchdog(void *arg)
643 {
644 fcoei_soft_state_t *ss;
645 clock_t tmp_delay;
646 clock_t start_clock;
647 clock_t last_clock;
648
649 /*
650 * For debugging
651 */
652 ss = (fcoei_soft_state_t *)arg;
653 FCOEI_LOG(__FUNCTION__, "ss %p", ss);
654 FCOEI_LOG(__FUNCTION__, "sol_hash %p", ss->ss_sol_oxid_hash);
655 FCOEI_LOG(__FUNCTION__, "unsol_hash %p", ss->ss_unsol_rxid_hash);
656 ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING;
657 tmp_delay = FCOE_SEC2TICK(1) / 2;
658 last_clock = CURRENT_CLOCK;
659
660 /*
661 * If nobody reqeusts to terminate the watchdog, we will work forever
662 */
663 while (!(ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG)) {
664 /*
665 * We handle all asynchronous events serially
666 */
667 fcoei_process_events(ss);
668
669 /*
670 * To avoid to check timing too freqently, we check
671 * if we need skip timing stuff.
672 */
673 start_clock = CURRENT_CLOCK;
674 if ((start_clock - last_clock) < tmp_delay) {
675 goto end_timing;
676 } else {
677 last_clock = start_clock;
678 }
679
680 /*
681 * It's time to do timeout checking of solicited exchanges
682 */
683 if (ss->ss_sol_cnt == (&ss->ss_sol_cnt1)) {
684 if (ss->ss_sol_cnt2 == 0) {
685 ss->ss_sol_cnt = &ss->ss_sol_cnt2;
686 } else {
687 mod_hash_walk(ss->ss_sol_oxid_hash,
688 fcoei_xch_check, ss);
689 }
690 } else {
691 if (ss->ss_sol_cnt1 == 0) {
692 ss->ss_sol_cnt = &ss->ss_sol_cnt1;
693 } else {
694 mod_hash_walk(ss->ss_sol_oxid_hash,
695 fcoei_xch_check, ss);
696 }
697 }
698
699 /*
700 * It's time to do timeout checking of unsolicited exchange
701 */
702 if (ss->ss_unsol_cnt == (&ss->ss_unsol_cnt1)) {
703 if (ss->ss_unsol_cnt2 == 0) {
704 ss->ss_unsol_cnt = &ss->ss_unsol_cnt2;
705 } else {
706 mod_hash_walk(ss->ss_unsol_rxid_hash,
707 fcoei_xch_check, ss);
708 }
709 } else {
710 if (ss->ss_unsol_cnt1 == 0) {
711 ss->ss_unsol_cnt = &ss->ss_unsol_cnt1;
712 } else {
713 mod_hash_walk(ss->ss_unsol_rxid_hash,
714 fcoei_xch_check, ss);
715 }
716 }
717
718 /*
719 * Check if there are exchanges which are ready to complete
720 */
721 fcoei_handle_comp_xch_list(ss);
722
723 end_timing:
724 /*
725 * Wait for next cycle
726 */
727 mutex_enter(&ss->ss_watchdog_mutex);
728 ss->ss_flags |= SS_FLAG_WATCHDOG_IDLE;
729 if (!list_is_empty(&ss->ss_event_list)) {
730 goto skip_wait;
731 }
732
733 (void) cv_timedwait(&ss->ss_watchdog_cv,
734 &ss->ss_watchdog_mutex, CURRENT_CLOCK +
735 (clock_t)tmp_delay);
736 skip_wait:
737 ss->ss_flags &= ~SS_FLAG_WATCHDOG_IDLE;
738 mutex_exit(&ss->ss_watchdog_mutex);
739 }
740
741 /*
742 * Do clear work before exit
743 */
744 fcoei_clear_watchdog_jobs(ss);
745
746 /*
747 * Watchdog has stopped
748 */
749 ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING;
750 }
751
752 static void
753 fcoei_clear_watchdog_jobs(fcoei_soft_state_t *ss)
754 {
755 fcoei_event_t *ae;
756 fcoe_frame_t *frm;
757
758 mutex_enter(&ss->ss_watchdog_mutex);
759 while (!list_is_empty(&ss->ss_event_list)) {
760 ae = (fcoei_event_t *)list_head(&ss->ss_event_list);
761 list_remove(&ss->ss_event_list, ae);
762 switch (ae->ae_type) {
763 case AE_EVENT_SOL_FRAME:
764 frm = (fcoe_frame_t *)ae->ae_obj;
765 frm->frm_eport->eport_release_frame(frm);
766 break;
767
768 case AE_EVENT_UNSOL_FRAME:
769 frm = (fcoe_frame_t *)ae->ae_obj;
770 frm->frm_eport->eport_free_netb(frm->frm_netb);
771 frm->frm_eport->eport_release_frame(frm);
772 break;
773
774 case AE_EVENT_PORT:
775 atomic_dec_32(&ss->ss_port_event_counter);
776 /* FALLTHROUGH */
777
778 case AE_EVENT_RESET:
779 kmem_free(ae, sizeof (fcoei_event_t));
780 break;
781
782 case AE_EVENT_EXCHANGE:
783 /* FALLTHROUGH */
784
785 default:
786 break;
787 }
788 }
789
790 mod_hash_clear(ss->ss_unsol_rxid_hash);
791 mod_hash_clear(ss->ss_sol_oxid_hash);
792
793 while (!list_is_empty(&ss->ss_comp_xch_list)) {
794 (void) list_remove_head(&ss->ss_comp_xch_list);
795 }
796 mutex_exit(&ss->ss_watchdog_mutex);
797 }
798
799 /*
800 * fcoei_process_events
801 * Process the events one by one
802 *
803 * Input:
804 * ss = the soft state that will be processed
805 *
806 * Return:
807 * N/A
808 *
809 * Comment:
810 * N/A
811 */
812 static void
813 fcoei_process_events(fcoei_soft_state_t *ss)
814 {
815 fcoei_event_t *ae = NULL;
816
817 /*
818 * It's the only place to delete node from ss_event_list, so we needn't
819 * hold mutex to check if the list is empty.
820 */
821 ASSERT(!MUTEX_HELD(&ss->ss_watchdog_mutex));
822 while (list_is_empty(&ss->ss_event_list) == B_FALSE) {
823 mutex_enter(&ss->ss_watchdog_mutex);
824 ae = (fcoei_event_t *)list_remove_head(&ss->ss_event_list);
825 mutex_exit(&ss->ss_watchdog_mutex);
826
827 switch (ae->ae_type) {
828 case AE_EVENT_SOL_FRAME:
829 fcoei_handle_sol_frame_done((fcoe_frame_t *)ae->ae_obj);
830 break;
831
832 case AE_EVENT_UNSOL_FRAME:
833 fcoei_process_unsol_frame((fcoe_frame_t *)ae->ae_obj);
834 break;
835
836 case AE_EVENT_EXCHANGE:
837 fcoei_process_event_exchange(ae);
838 break;
839
840 case AE_EVENT_PORT:
841 fcoei_process_event_port(ae);
842 break;
843
844 case AE_EVENT_RESET:
845 fcoei_process_event_reset(ae);
846 break;
847
848 default:
849 FCOEI_LOG(__FUNCTION__, "unsupported events");
850 }
851
852 }
853 }
854
855 /*
856 * fcoei_handle_tmout_xch_list
857 * Complete every exchange in the timed-out xch list of the soft state
858 *
859 * Input:
860 * ss = the soft state that need be handled
861 *
862 * Return:
863 * N/A
864 *
865 * Comment:
866 * When mod_hash_walk is in progress, we can't change the hashtable.
867 * This is post-walk handling of exchange timing
868 */
869 void
870 fcoei_handle_comp_xch_list(fcoei_soft_state_t *ss)
871 {
872 fcoei_exchange_t *xch = NULL;
873
874 while ((xch = list_remove_head(&ss->ss_comp_xch_list)) != NULL) {
875 fcoei_complete_xch(xch, NULL, xch->xch_fpkt->pkt_state,
876 xch->xch_fpkt->pkt_reason);
877 }
878 }
879
880 /*
881 * fcoei_xch_check
882 * Check if the exchange timed out or link is down
883 *
884 * Input:
885 * key = rxid of the unsolicited exchange
886 * val = the unsolicited exchange
887 * arg = the soft state
888 *
889 * Return:
890 * MH_WALK_CONTINUE = continue to walk
891 *
892 * Comment:
893 * We need send ABTS for timed-out for solicited exchange
894 * If it's solicited FLOGI, we need set SS_FLAG_FLOGI_FAILED
895 * If the link is down, we think it has timed out too.
896 */
897 /* ARGSUSED */
898 static uint32_t
899 fcoei_xch_check(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
900 {
901 fcoei_exchange_t *xch = (fcoei_exchange_t *)val;
902
903 ASSERT(xch->xch_ss == arg);
904 if ((xch->xch_end_tick < CURRENT_CLOCK) &&
905 (xch->xch_ss->ss_link_state != FC_STATE_OFFLINE)) {
906 if (xch->xch_flags & XCH_FLAG_IN_SOL_HASH) {
907 ASSERT(xch->xch_oxid == CMHK(key));
908 /*
909 * It's solicited exchange
910 */
911 fcoei_abts_exchange(xch);
912 if (LA_ELS_FLOGI == ((ls_code_t *)(void *)
913 xch->xch_fpkt->pkt_cmd)->ls_code) {
914 /*
915 * It's solicited FLOGI
916 */
917 xch->xch_ss->ss_flags |= SS_FLAG_FLOGI_FAILED;
918 }
919 }
920
921 FCOEI_LOG(__FUNCTION__, "oxid-%x/rxid-%x timed out",
922 xch->xch_oxid, xch->xch_rxid);
923 xch->xch_flags |= XCH_FLAG_TMOUT;
924 xch->xch_fpkt->pkt_state = FC_PKT_TIMEOUT;
925 xch->xch_fpkt->pkt_reason = FC_REASON_ABORTED;
926 list_insert_tail(&xch->xch_ss->ss_comp_xch_list, xch);
927 } else if (xch->xch_ss->ss_link_state == FC_STATE_OFFLINE) {
928 FCOEI_LOG(__FUNCTION__, "oxid-%x/rxid-%x offline complete",
929 xch->xch_oxid, xch->xch_rxid);
930 xch->xch_flags |= XCH_FLAG_TMOUT;
931 xch->xch_fpkt->pkt_state = FC_PKT_PORT_OFFLINE;
932 xch->xch_fpkt->pkt_reason = FC_REASON_OFFLINE;
933 list_insert_tail(&xch->xch_ss->ss_comp_xch_list, xch);
934 }
935
936 return (MH_WALK_CONTINUE);
937 }
938
939 /*
940 * fcoei_init_ifm
941 * initialize fcoei_frame
942 *
943 * Input:
944 * frm = the frame that ifm need link to
945 * xch = the exchange that ifm need link to
946 *
947 * Return:
948 * N/A
949 *
950 * Comment:
951 * For solicited frames, it's called after FC frame header initialization
952 * For unsolicited frames, it's called just after the frame enters fcoei
953 */
954 void
955 fcoei_init_ifm(fcoe_frame_t *frm, fcoei_exchange_t *xch)
956 {
957 FRM2IFM(frm)->ifm_frm = frm;
958 FRM2IFM(frm)->ifm_xch = xch;
959 FRM2IFM(frm)->ifm_rctl = FRM_R_CTL(frm);
960 }
961
962 /*
963 * fcoei_trigger_fp_attach
964 * Trigger fp_attach for this fcoei port
965 *
966 * Input:
967 * arg = the soft state that fp will attach
968 *
969 * Return:
970 * N/A
971 *
972 * Comment:
973 * N/A
974 */
975 static void
976 fcoei_trigger_fp_attach(void * arg)
977 {
978 fcoei_soft_state_t *ss = (fcoei_soft_state_t *)arg;
979 dev_info_t *child = NULL;
980 int rval = NDI_FAILURE;
981
982 ndi_devi_alloc_sleep(ss->ss_dip, "fp", DEVI_PSEUDO_NODEID, &child);
983 if (child == NULL) {
984 FCOEI_LOG(__FUNCTION__, "can't alloc dev_info");
985 return;
986 }
987
988 /*
989 * fp/fctl need this property
990 */
991 if (ddi_prop_update_string(DDI_DEV_T_NONE, child,
992 "bus-addr", "0,0") != DDI_PROP_SUCCESS) {
993 FCOEI_LOG(__FUNCTION__, "update bus-addr failed");
994 (void) ndi_devi_free(child);
995 return;
996 }
997
998 /*
999 * If it's physical HBA, fp.conf will register the property.
1000 * fcoei is one software HBA, so we need register it manually
1001 */
1002 if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
1003 "port", 0) != DDI_PROP_SUCCESS) {
1004 FCOEI_LOG(__FUNCTION__, "update port failed");
1005 (void) ndi_devi_free(child);
1006 return;
1007 }
1008
1009 /*
1010 * It will call fp_attach eventually
1011 */
1012 rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
1013 ss->ss_flags &= ~SS_FLAG_TRIGGER_FP_ATTACH;
1014 if (rval != NDI_SUCCESS) {
1015 FCOEI_LOG(__FUNCTION__, "devi_online: %d", rval);
1016 } else {
1017 FCOEI_LOG(__FUNCTION__, "triggered successfully");
1018 }
1019 }
1020
1021 /*
1022 * fcoei_abts_exchange
1023 * Send ABTS to abort solicited exchange
1024 *
1025 * Input:
1026 * xch = the exchange that will be aborted
1027 *
1028 * Return:
1029 * N/A
1030 *
1031 * Comment:
1032 * ABTS frame uses the same oxid as the exchange
1033 */
1034 static void
1035 fcoei_abts_exchange(fcoei_exchange_t *xch)
1036 {
1037 fc_packet_t *fpkt = xch->xch_fpkt;
1038 fcoe_frame_t *frm = NULL;
1039
1040 /*
1041 * BLS_ABTS doesn't contain any other payload except FCFH
1042 */
1043 frm = xch->xch_ss->ss_eport->eport_alloc_frame(xch->xch_ss->ss_eport,
1044 FCFH_SIZE, NULL);
1045 if (frm == NULL) {
1046 FCOEI_LOG(__FUNCTION__, "can't alloc frame: %p", xch);
1047 return;
1048 }
1049
1050 FFM_R_CTL(0x81, frm);
1051 FFM_D_ID(fpkt->pkt_cmd_fhdr.d_id, frm);
1052 FFM_S_ID(fpkt->pkt_cmd_fhdr.s_id, frm);
1053 FFM_F_CTL(0x090000, frm);
1054 FFM_SEQ_ID(0x01, frm);
1055 FFM_OXID(xch->xch_oxid, frm);
1056 FFM_RXID(xch->xch_rxid, frm);
1057 fcoei_init_ifm(frm, xch);
1058 xch->xch_ss->ss_eport->eport_tx_frame(frm);
1059 }
1060
1061 /*
1062 * fcoei_complete_xch
1063 * Complete the exchange
1064 *
1065 * Input:
1066 * xch = the exchange that will be completed
1067 * frm = newly-allocated frame that has not been submitted
1068 * pkt_state = LV fpkt state
1069 * pkt_reason = LV fpkt reason
1070 *
1071 * Return:
1072 * N/A
1073 *
1074 * Comment:
1075 * N/A
1076 */
1077 void
1078 fcoei_complete_xch(fcoei_exchange_t *xch, fcoe_frame_t *frm,
1079 uint8_t pkt_state, uint8_t pkt_reason)
1080 {
1081 mod_hash_val_t val;
1082
1083 if (pkt_state != FC_PKT_SUCCESS) {
1084 FCOEI_LOG(__FUNCTION__, "FHDR: %x/%x/%x, %x/%x/%x",
1085 xch->xch_fpkt->pkt_cmd_fhdr.r_ctl,
1086 xch->xch_fpkt->pkt_cmd_fhdr.f_ctl,
1087 xch->xch_fpkt->pkt_cmd_fhdr.type,
1088 xch->xch_fpkt->pkt_resp_fhdr.r_ctl,
1089 xch->xch_fpkt->pkt_resp_fhdr.f_ctl,
1090 xch->xch_fpkt->pkt_resp_fhdr.type);
1091 FCOEI_LOG(__FUNCTION__, "%p/%p/%x/%x",
1092 xch, frm, pkt_state, pkt_reason);
1093 }
1094
1095 if (frm != NULL) {
1096 /*
1097 * It's newly-allocated frame , which we haven't sent out
1098 */
1099 xch->xch_ss->ss_eport->eport_free_netb(frm->frm_netb);
1100 xch->xch_ss->ss_eport->eport_release_frame(frm);
1101 FCOEI_LOG(__FUNCTION__, "xch: %p, not submitted", xch);
1102 }
1103
1104 /*
1105 * If xch is in hash table, we need remove it
1106 */
1107 if (xch->xch_flags & XCH_FLAG_IN_SOL_HASH) {
1108 (void) mod_hash_remove(xch->xch_ss->ss_sol_oxid_hash,
1109 FMHK(xch->xch_oxid), &val);
1110 ASSERT((fcoei_exchange_t *)val == xch);
1111 xch->xch_flags &= ~XCH_FLAG_IN_SOL_HASH;
1112 } else if (xch->xch_flags & XCH_FLAG_IN_UNSOL_HASH) {
1113 (void) mod_hash_remove(xch->xch_ss->ss_unsol_rxid_hash,
1114 FMHK(xch->xch_rxid), &val);
1115 ASSERT((fcoei_exchange_t *)val == xch);
1116 xch->xch_flags &= ~XCH_FLAG_IN_UNSOL_HASH;
1117 } else {
1118 FCOEI_LOG(__FUNCTION__, "xch not in any hash: %p", xch);
1119 }
1120
1121 xch->xch_fpkt->pkt_state = pkt_state;
1122 xch->xch_fpkt->pkt_reason = pkt_reason;
1123 if (xch->xch_fpkt->pkt_tran_flags & FC_TRAN_NO_INTR) {
1124 FCOEI_LOG(__FUNCTION__, "polled xch is done: %p", xch);
1125 sema_v(&xch->xch_sema);
1126 } else {
1127 xch->xch_fpkt->pkt_comp(xch->xch_fpkt);
1128 }
1129 }