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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * The following notice accompanied the original version of this file:
28  *
29  * BSD LICENSE
30  *
31  * Copyright(c) 2007 Intel Corporation. All rights reserved.
32  * All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  *
38  *   * Redistributions of source code must retain the above copyright
39  *     notice, this list of conditions and the following disclaimer.
40  *   * Redistributions in binary form must reproduce the above copyright
41  *     notice, this list of conditions and the following disclaimer in
42  *     the documentation and/or other materials provided with the
43  *     distribution.
44  *   * Neither the name of Intel Corporation nor the names of its
45  *     contributors may be used to endorse or promote products derived
46  *     from this software without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
51  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
52  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
53  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
54  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
55  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
56  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
57  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
58  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59  */
60 
61 /*
62  * Driver kernel header files
63  */
64 #include <sys/conf.h>
65 #include <sys/ddi.h>
66 #include <sys/stat.h>
67 #include <sys/pci.h>
68 #include <sys/sunddi.h>
69 #include <sys/modctl.h>
70 #include <sys/file.h>
71 #include <sys/cred.h>
72 #include <sys/byteorder.h>
73 #include <sys/atomic.h>
74 #include <sys/modhash.h>
75 #include <sys/scsi/scsi.h>
76 #include <sys/ethernet.h>
77 
78 /*
79  * COMSTAR header files
80  */
81 #include <sys/stmf_defines.h>
82 #include <sys/fct_defines.h>
83 #include <sys/stmf.h>
84 #include <sys/portif.h>
85 #include <sys/fct.h>
86 
87 /*
88  * FCoE header files
89  */
90 #include <sys/fcoe/fcoe_common.h>
91 
92 /*
93  * Driver's own header files
94  */
95 #include <fcoet.h>
96 #include <fcoet_eth.h>
97 #include <fcoet_fc.h>
98 
99 /*
100  * static function forward declaration
101  */
102 static int fcoet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
103 static int fcoet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
104 static int fcoet_open(dev_t *devp, int flag, int otype, cred_t *credp);
105 static int fcoet_close(dev_t dev, int flag, int otype, cred_t *credp);
106 static int fcoet_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
107     cred_t *credp, int *rval);
108 static fct_status_t fcoet_attach_init(fcoet_soft_state_t *ss);
109 static fct_status_t fcoet_detach_uninit(fcoet_soft_state_t *ss);
110 static void fcoet_watchdog(void *arg);
111 static void fcoet_handle_sol_flogi(fcoet_soft_state_t *ss);
112 static stmf_data_buf_t *fcoet_dbuf_alloc(fct_local_port_t *port,
113     uint32_t size, uint32_t *pminsize, uint32_t flags);
114 static void fcoet_dbuf_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf);
115 static int fcoet_dbuf_init(fcoet_soft_state_t *ss);
116 static void fcoet_dbuf_destroy(fcoet_soft_state_t *ss);
117 static uint_t
118 fcoet_sol_oxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg);
119 static uint_t
120 fcoet_unsol_rxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg);
121 
122 /*
123  * Driver identificaton stuff
124  */
125 static struct cb_ops fcoet_cb_ops = {
126 	fcoet_open,
127 	fcoet_close,
128 	nodev,
129 	nodev,
130 	nodev,
131 	nodev,
132 	nodev,
133 	fcoet_ioctl,
134 	nodev,
135 	nodev,
136 	nodev,
137 	nochpoll,
138 	ddi_prop_op,
139 	0,
140 	D_MP | D_NEW
141 };
142 
143 static struct dev_ops fcoet_ops = {
144 	DEVO_REV,
145 	0,
146 	nodev,
147 	nulldev,
148 	nulldev,
149 	fcoet_attach,
150 	fcoet_detach,
151 	nodev,
152 	&fcoet_cb_ops,
153 	NULL,
154 	ddi_power
155 };
156 
157 static struct modldrv modldrv = {
158 	&mod_driverops,
159 	FCOET_MOD_NAME,
160 	&fcoet_ops,
161 };
162 
163 static struct modlinkage modlinkage = {
164 	MODREV_1, &modldrv, NULL
165 };
166 
167 /*
168  * Driver's global variables
169  */
170 static kmutex_t	 fcoet_mutex;
171 static void	*fcoet_state = NULL;
172 
173 int fcoet_use_ext_log = 1;
174 static char				 fcoet_provider_name[] = "fcoet";
175 static struct stmf_port_provider	*fcoet_pp	= NULL;
176 
177 /*
178  * Common loadable module entry points _init, _fini, _info
179  */
180 
181 int
182 _init(void)
183 {
184 	int ret;
185 
186 	ret = ddi_soft_state_init(&fcoet_state, sizeof (fcoet_soft_state_t), 0);
187 	if (ret == 0) {
188 		fcoet_pp = (stmf_port_provider_t *)
189 		    stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
190 		fcoet_pp->pp_portif_rev = PORTIF_REV_1;
191 		fcoet_pp->pp_name = fcoet_provider_name;
192 		if (stmf_register_port_provider(fcoet_pp) != STMF_SUCCESS) {
193 			stmf_free(fcoet_pp);
194 			ddi_soft_state_fini(&fcoet_state);
195 			return (EIO);
196 		}
197 
198 		mutex_init(&fcoet_mutex, 0, MUTEX_DRIVER, 0);
199 		ret = mod_install(&modlinkage);
200 		if (ret) {
201 			stmf_deregister_port_provider(fcoet_pp);
202 			stmf_free(fcoet_pp);
203 			mutex_destroy(&fcoet_mutex);
204 			ddi_soft_state_fini(&fcoet_state);
205 		}
206 	}
207 
208 	FCOET_LOG("_init", "exit _init with %x", ret);
209 	return (ret);
210 }
211 
212 int
213 _fini(void)
214 {
215 	int ret;
216 
217 	ret = mod_remove(&modlinkage);
218 	if (ret == 0) {
219 		stmf_deregister_port_provider(fcoet_pp);
220 		stmf_free(fcoet_pp);
221 		mutex_destroy(&fcoet_mutex);
222 		ddi_soft_state_fini(&fcoet_state);
223 	}
224 
225 	FCOET_LOG("_fini", "exit _fini with %x", ret);
226 	return (ret);
227 }
228 
229 int
230 _info(struct modinfo *modinfop)
231 {
232 	return (mod_info(&modlinkage, modinfop));
233 }
234 
235 /*
236  * Autoconfiguration entry points: attach, detach, getinfo
237  */
238 
239 static int
240 fcoet_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
241 {
242 	int			 ret = DDI_FAILURE;
243 	int			 instance;
244 	fcoet_soft_state_t	*ss;
245 
246 	instance = ddi_get_instance(dip);
247 	FCOET_LOG("fcoet_attach", "get instance %d", instance);
248 
249 	switch (cmd) {
250 	case DDI_ATTACH:
251 		ret = ddi_soft_state_zalloc(fcoet_state, instance);
252 		if (ret != DDI_SUCCESS) {
253 			return (ret);
254 		}
255 
256 		ss = ddi_get_soft_state(fcoet_state, instance);
257 		ss->ss_instance = instance;
258 		ss->ss_dip = dip;
259 
260 		ret = fcoet_attach_init(ss);
261 		if (ret != FCOE_SUCCESS) {
262 			ddi_soft_state_free(fcoet_state, instance);
263 			ret = DDI_FAILURE;
264 		}
265 
266 		FCOET_LOG("fcoet_attach", "end with-%x", ret);
267 		break;
268 
269 	case DDI_RESUME:
270 		ret = DDI_SUCCESS;
271 		break;
272 
273 	default:
274 		FCOET_LOG("fcoet_attach", "unspported attach cmd-%x", cmd);
275 		break;
276 	}
277 
278 	return (ret);
279 }
280 
281 static int
282 fcoet_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
283 {
284 	int			 ret = DDI_FAILURE;
285 	int			 fcoe_ret;
286 	int			 instance;
287 	fcoet_soft_state_t	*ss;
288 
289 	instance = ddi_get_instance(dip);
290 	ss = ddi_get_soft_state(fcoet_state, instance);
291 	if (ss == NULL) {
292 		return (ret);
293 	}
294 
295 	switch (cmd) {
296 	case DDI_DETACH:
297 		fcoe_ret = fcoet_detach_uninit(ss);
298 		if (fcoe_ret == FCOE_SUCCESS) {
299 			ret = DDI_SUCCESS;
300 		}
301 
302 		FCOET_LOG("fcoet_detach", "fcoet_detach_uninit end with-%x",
303 		    fcoe_ret);
304 		break;
305 
306 	case DDI_SUSPEND:
307 		ret = DDI_SUCCESS;
308 		break;
309 
310 	default:
311 		FCOET_LOG("fcoet_detach", "unsupported detach cmd-%x", cmd);
312 		break;
313 	}
314 
315 	return (ret);
316 }
317 
318 /*
319  * Device access entry points
320  */
321 static int
322 fcoet_open(dev_t *devp, int flag, int otype, cred_t *credp)
323 {
324 	int			 instance;
325 	fcoet_soft_state_t	*ss;
326 
327 	if (otype != OTYP_CHR) {
328 		return (EINVAL);
329 	}
330 
331 	/*
332 	 * Since this is for debugging only, only allow root to issue ioctl now
333 	 */
334 	if (drv_priv(credp)) {
335 		return (EPERM);
336 	}
337 
338 	instance = (int)getminor(*devp);
339 	ss = ddi_get_soft_state(fcoet_state, instance);
340 	if (ss == NULL) {
341 		return (ENXIO);
342 	}
343 
344 	mutex_enter(&ss->ss_ioctl_mutex);
345 	if (ss->ss_ioctl_flags & FCOET_IOCTL_FLAG_EXCL) {
346 		/*
347 		 * It is already open for exclusive access.
348 		 * So shut the door on this caller.
349 		 */
350 		mutex_exit(&ss->ss_ioctl_mutex);
351 		return (EBUSY);
352 	}
353 
354 	if (flag & FEXCL) {
355 		if (ss->ss_ioctl_flags & FCOET_IOCTL_FLAG_OPEN) {
356 			/*
357 			 * Exclusive operation not possible
358 			 * as it is already opened
359 			 */
360 			mutex_exit(&ss->ss_ioctl_mutex);
361 			return (EBUSY);
362 		}
363 		ss->ss_ioctl_flags |= FCOET_IOCTL_FLAG_EXCL;
364 	}
365 	ss->ss_ioctl_flags |= FCOET_IOCTL_FLAG_OPEN;
366 	mutex_exit(&ss->ss_ioctl_mutex);
367 
368 	return (0);
369 }
370 
371 /* ARGSUSED */
372 static int
373 fcoet_close(dev_t dev, int flag, int otype, cred_t *credp)
374 {
375 	int			 instance;
376 	fcoet_soft_state_t	*ss;
377 
378 	if (otype != OTYP_CHR) {
379 		return (EINVAL);
380 	}
381 
382 	instance = (int)getminor(dev);
383 	ss = ddi_get_soft_state(fcoet_state, instance);
384 	if (ss == NULL) {
385 		return (ENXIO);
386 	}
387 
388 	mutex_enter(&ss->ss_ioctl_mutex);
389 	if ((ss->ss_ioctl_flags & FCOET_IOCTL_FLAG_OPEN) == 0) {
390 		mutex_exit(&ss->ss_ioctl_mutex);
391 		return (ENODEV);
392 	}
393 
394 	/*
395 	 * It looks there's one hole here, maybe there could several concurrent
396 	 * shareed open session, but we never check this case.
397 	 * But it will not hurt too much, disregard it now.
398 	 */
399 	ss->ss_ioctl_flags &= ~FCOET_IOCTL_FLAG_MASK;
400 	mutex_exit(&ss->ss_ioctl_mutex);
401 
402 	return (0);
403 }
404 
405 /* ARGSUSED */
406 static int
407 fcoet_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
408     cred_t *credp, int *rval)
409 {
410 	fcoet_soft_state_t	*ss;
411 	int		 ret = 0;
412 
413 	if (drv_priv(credp) != 0) {
414 		return (EPERM);
415 	}
416 
417 	ss = ddi_get_soft_state(fcoet_state, (int32_t)getminor(dev));
418 	if (ss == NULL) {
419 		return (ENXIO);
420 	}
421 
422 	switch (cmd) {
423 	default:
424 		FCOET_LOG("fcoet_ioctl", "ioctl-0x%02X", cmd);
425 		ret = ENOTTY;
426 		break;
427 	}
428 
429 	*rval = ret;
430 	return (ret);
431 }
432 
433 static fct_status_t
434 fcoet_attach_init(fcoet_soft_state_t *ss)
435 {
436 	fcoe_client_t		 client_fcoet;
437 	fcoe_port_t		*eport;
438 	fct_local_port_t	*port;
439 	fct_dbuf_store_t	*fds;
440 	char			 *mac_name;
441 	char			 taskq_name[32];
442 	int			 ret;
443 
444 	/*
445 	 * FCoE (fcoe is fcoet's dependent driver)
446 	 * First we need register fcoet to FCoE as one client
447 	 */
448 	client_fcoet.ect_eport_flags = EPORT_FLAG_TGT_MODE |
449 	    EPORT_FLAG_IS_DIRECT_P2P;
450 	client_fcoet.ect_max_fc_frame_size = 2136;
451 	client_fcoet.ect_private_frame_struct_size = sizeof (fcoet_frame_t);
452 	client_fcoet.ect_rx_frame = fcoet_rx_frame;
453 	client_fcoet.ect_port_event = fcoet_port_event;
454 	client_fcoet.ect_release_sol_frame = fcoet_release_sol_frame;
455 	client_fcoet.ect_client_port_struct = ss;
456 	ret = ddi_prop_lookup_string(DDI_DEV_T_ANY, ss->ss_dip,
457 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "mac_name", &mac_name);
458 	if (ret != DDI_PROP_SUCCESS) {
459 		FCOET_LOG("fcoet_attach_init", "lookup_string failed");
460 		return (DDI_FAILURE);
461 	} else {
462 		bcopy(mac_name, client_fcoet.ect_channel_name,
463 		    strlen(mac_name));
464 		client_fcoet.ect_channel_name[strlen(mac_name)] = 0;
465 		ddi_prop_free(mac_name);
466 	}
467 	FCOET_LOG("fcoet_attach_init", "channel_name is %s",
468 	    client_fcoet.ect_channel_name);
469 
470 	/*
471 	 * It's FCoE's responsiblity to initialize eport's all elements
472 	 */
473 	eport = fcoe_register_client(&client_fcoet);
474 	if (eport == NULL) {
475 		goto fail_register_client;
476 	}
477 
478 	/*
479 	 * Now it's time to register local port to FCT
480 	 */
481 	if (fcoet_dbuf_init(ss) != FCOE_SUCCESS) {
482 		goto fail_init_dbuf;
483 	}
484 
485 	fds = (fct_dbuf_store_t *)fct_alloc(FCT_STRUCT_DBUF_STORE, 0, 0);
486 	if (fds == NULL) {
487 		goto fail_alloc_dbuf;
488 	} else {
489 		fds->fds_alloc_data_buf = fcoet_dbuf_alloc;
490 		fds->fds_free_data_buf = fcoet_dbuf_free;
491 		fds->fds_fca_private = (void *)ss;
492 	}
493 
494 	port = (fct_local_port_t *)fct_alloc(FCT_STRUCT_LOCAL_PORT, 0, 0);
495 	if (port == NULL) {
496 		goto fail_alloc_port;
497 	} else {
498 		/*
499 		 * Do ss's initialization now
500 		 */
501 		(void) snprintf(ss->ss_alias, sizeof (ss->ss_alias), "fcoet%d",
502 		    ss->ss_instance);
503 		ret = ddi_create_minor_node(ss->ss_dip, "admin",
504 		    S_IFCHR, ss->ss_instance, DDI_NT_STMF_PP, 0);
505 		if (ret != DDI_SUCCESS) {
506 			goto fail_minor_node;
507 		}
508 
509 		ss->ss_state = FCT_STATE_OFFLINE;
510 		ss->ss_state_not_acked = 1;
511 		ss->ss_flags = 0;
512 		ss->ss_port = port;
513 		ss->ss_eport = eport;
514 		FCOE_SET_DEFAULT_FPORT_ADDR(eport->eport_efh_dst);
515 
516 		ss->ss_rportid_in_dereg = 0;
517 		ss->ss_rport_dereg_state = 0;
518 
519 		ss->ss_next_sol_oxid = 0xFFFF;
520 		ss->ss_next_unsol_rxid = 0xFFFF;
521 		ss->ss_sol_oxid_hash = mod_hash_create_idhash(
522 		    "ss_sol_oxid_hash", FCOET_SOL_HASH_SIZE,
523 		    mod_hash_null_valdtor);
524 		ss->ss_unsol_rxid_hash = mod_hash_create_idhash(
525 		    "ss_unsol_rxid_hash", FCOET_SOL_HASH_SIZE,
526 		    mod_hash_null_valdtor);
527 
528 		ss->ss_watch_count = 0;
529 		mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0);
530 		cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL);
531 
532 		list_create(&ss->ss_abort_xchg_list, sizeof (fcoet_exchange_t),
533 		    offsetof(fcoet_exchange_t, xch_abort_node));
534 
535 		ss->ss_sol_flogi = NULL;
536 		ss->ss_sol_flogi_state = SFS_WAIT_LINKUP;
537 
538 		bzero(&ss->ss_link_info, sizeof (fct_link_info_t));
539 
540 		ss->ss_ioctl_flags = 0;
541 		mutex_init(&ss->ss_ioctl_mutex, 0, MUTEX_DRIVER, 0);
542 
543 		ss->ss_change_state_flags = 0;
544 	}
545 
546 	/*
547 	 * Do port's initialization
548 	 *
549 	 * port_fct_private and port_lport have been initialized by fct_alloc
550 	 */
551 	port->port_fca_private = ss;
552 	bcopy(ss->ss_eport->eport_nodewwn, port->port_nwwn, 8);
553 	bcopy(ss->ss_eport->eport_portwwn, port->port_pwwn, 8);
554 	port->port_default_alias = ss->ss_alias;
555 	port->port_sym_node_name = NULL;
556 	port->port_sym_port_name = NULL;
557 
558 	port->port_pp = fcoet_pp;
559 
560 	port->port_hard_address = 0;
561 	port->port_max_logins = FCOET_MAX_LOGINS;
562 	port->port_max_xchges = FCOET_MAX_XCHGES;
563 	port->port_fca_fcp_cmd_size = sizeof (fcoet_exchange_t);
564 	port->port_fca_rp_private_size = 0;
565 	port->port_fca_sol_els_private_size = sizeof (fcoet_exchange_t);
566 	port->port_fca_sol_ct_private_size = sizeof (fcoet_exchange_t);
567 
568 	port->port_fca_abort_timeout = 5 * 1000;	/* 5 seconds */
569 	port->port_fds = fds;
570 
571 	port->port_get_link_info = fcoet_get_link_info;
572 	port->port_register_remote_port = fcoet_register_remote_port;
573 	port->port_deregister_remote_port = fcoet_deregister_remote_port;
574 	port->port_send_cmd = fcoet_send_cmd;
575 	port->port_xfer_scsi_data = fcoet_xfer_scsi_data;
576 	port->port_send_cmd_response = fcoet_send_cmd_response;
577 	port->port_abort_cmd = fcoet_abort_cmd;
578 	port->port_ctl = fcoet_ctl;
579 	port->port_flogi_xchg = fcoet_do_flogi;
580 	port->port_populate_hba_details = fcoet_populate_hba_fru_details;
581 	if (fct_register_local_port(port) != FCT_SUCCESS) {
582 		goto fail_register_port;
583 	}
584 
585 	/*
586 	 * Start watchdog thread
587 	 */
588 	(void) snprintf(taskq_name, 32, "stmf_fct_fcoet_%d_taskq",
589 	    ss->ss_instance);
590 	taskq_name[31] = 0;
591 	if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL,
592 	    taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) {
593 		goto fail_create_taskq;
594 	}
595 
596 	atomic_and_32(&ss->ss_flags, ~SS_FLAG_TERMINATE_WATCHDOG);
597 	(void) ddi_taskq_dispatch(ss->ss_watchdog_taskq,
598 	    fcoet_watchdog, ss, DDI_SLEEP);
599 	while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) {
600 		delay(10);
601 	}
602 
603 	ddi_report_dev(ss->ss_dip);
604 	return (DDI_SUCCESS);
605 
606 fail_create_taskq:
607 	if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
608 		atomic_or_32(&ss->ss_flags, SS_FLAG_TERMINATE_WATCHDOG);
609 		cv_broadcast(&ss->ss_watch_cv);
610 		while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
611 			delay(10);
612 		}
613 	}
614 
615 	ddi_taskq_destroy(ss->ss_watchdog_taskq);
616 	FCOET_LOG("fcoet_attach_init", "fail_register_port");
617 
618 fail_register_port:
619 	mutex_destroy(&ss->ss_ioctl_mutex);
620 	mutex_destroy(&ss->ss_watch_mutex);
621 	cv_destroy(&ss->ss_watch_cv);
622 	mod_hash_destroy_hash(ss->ss_sol_oxid_hash);
623 	mod_hash_destroy_hash(ss->ss_unsol_rxid_hash);
624 	list_destroy(&ss->ss_abort_xchg_list);
625 	FCOET_LOG("fcoet_attach_init", "fail_create_taskq");
626 
627 fail_minor_node:
628 	fct_free(port);
629 	FCOET_LOG("fcoet_attach_init", "fail_minor_node");
630 
631 fail_alloc_port:
632 	fct_free(fds);
633 	FCOET_LOG("fcoet_attach_init", "fail_alloc_port");
634 
635 fail_alloc_dbuf:
636 	fcoet_dbuf_destroy(ss);
637 	FCOET_LOG("fcoet_attach_init", "fail_alloc_dbuf");
638 
639 fail_init_dbuf:
640 	ss->ss_eport->eport_deregister_client(ss->ss_eport);
641 	FCOET_LOG("fcoet_attach_init", "fail_init_dbuf");
642 
643 fail_register_client:
644 	FCOET_LOG("fcoet_attach_init", "fail_register_client");
645 	return (DDI_FAILURE);
646 }
647 
648 static fct_status_t
649 fcoet_detach_uninit(fcoet_soft_state_t *ss)
650 {
651 	if ((ss->ss_state != FCT_STATE_OFFLINE) ||
652 	    ss->ss_state_not_acked) {
653 		return (FCOE_FAILURE);
654 	}
655 
656 	/*
657 	 * Avoid modunload before running fcinfo remove-target-port
658 	 */
659 	if (ss->ss_eport != NULL &&
660 	    ss->ss_eport->eport_flags & EPORT_FLAG_MAC_IN_USE) {
661 		return (FCOE_FAILURE);
662 	}
663 
664 	if (ss->ss_port == NULL) {
665 		return (FCOE_SUCCESS);
666 	}
667 
668 	ss->ss_sol_oxid_hash_empty = 1;
669 	ss->ss_unsol_rxid_hash_empty = 1;
670 	mod_hash_walk(ss->ss_sol_oxid_hash, fcoet_sol_oxid_hash_empty, ss);
671 	mod_hash_walk(ss->ss_unsol_rxid_hash, fcoet_unsol_rxid_hash_empty, ss);
672 	if ((!ss->ss_sol_oxid_hash_empty) || (!ss->ss_unsol_rxid_hash_empty)) {
673 		return (FCOE_FAILURE);
674 	}
675 
676 	/*
677 	 * We need offline the port manually, before we want to detach it
678 	 * or it will not succeed.
679 	 */
680 	if (fct_deregister_local_port(ss->ss_port) != FCT_SUCCESS) {
681 		FCOET_LOG("fcoet_detach_uninit",
682 		    "fct_deregister_local_port failed");
683 		return (FCOE_FAILURE);
684 	}
685 
686 	/*
687 	 * Stop watchdog
688 	 */
689 	if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
690 		atomic_or_32(&ss->ss_flags, SS_FLAG_TERMINATE_WATCHDOG);
691 		cv_broadcast(&ss->ss_watch_cv);
692 		while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
693 			delay(10);
694 		}
695 	}
696 
697 	ddi_taskq_destroy(ss->ss_watchdog_taskq);
698 
699 	/*
700 	 * Release all resources
701 	 */
702 	mutex_destroy(&ss->ss_ioctl_mutex);
703 	mutex_destroy(&ss->ss_watch_mutex);
704 	cv_destroy(&ss->ss_watch_cv);
705 	mod_hash_destroy_hash(ss->ss_sol_oxid_hash);
706 	mod_hash_destroy_hash(ss->ss_unsol_rxid_hash);
707 	list_destroy(&ss->ss_abort_xchg_list);
708 
709 	fct_free(ss->ss_port->port_fds);
710 	fct_free(ss->ss_port);
711 	ss->ss_port = NULL;
712 
713 	fcoet_dbuf_destroy(ss);
714 
715 	if (ss->ss_eport != NULL &&
716 	    ss->ss_eport->eport_deregister_client != NULL) {
717 		ss->ss_eport->eport_deregister_client(ss->ss_eport);
718 	}
719 	ddi_soft_state_free(fcoet_state, ss->ss_instance);
720 	return (FCOE_SUCCESS);
721 }
722 
723 static void
724 fcoet_watchdog(void *arg)
725 {
726 	fcoet_soft_state_t	*ss = (fcoet_soft_state_t *)arg;
727 	clock_t			 tmp_delay = 0;
728 	fcoet_exchange_t	*xchg, *xchg_next;
729 
730 	FCOET_LOG("fcoet_watchdog", "fcoet_soft_state is %p", ss);
731 
732 	mutex_enter(&ss->ss_watch_mutex);
733 	atomic_or_32(&ss->ss_flags, SS_FLAG_WATCHDOG_RUNNING);
734 	tmp_delay = STMF_SEC2TICK(1)/2;
735 
736 	while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) {
737 		ss->ss_watch_count++;
738 
739 		if (ss->ss_sol_flogi_state != SFS_FLOGI_DONE) {
740 			fcoet_handle_sol_flogi(ss);
741 		}
742 		for (xchg = list_head(&ss->ss_abort_xchg_list); xchg; ) {
743 			xchg_next = list_next(&ss->ss_abort_xchg_list, xchg);
744 			if (xchg->xch_ref == 0) {
745 				list_remove(&ss->ss_abort_xchg_list, xchg);
746 				mutex_exit(&ss->ss_watch_mutex);
747 				/* xchg abort done */
748 				if (xchg->xch_dbuf_num) {
749 					kmem_free((void*)xchg->xch_dbufs,
750 					    xchg->xch_dbuf_num *
751 					    sizeof (void *));
752 					xchg->xch_dbufs = NULL;
753 					xchg->xch_dbuf_num = 0;
754 				}
755 				fct_cmd_fca_aborted(xchg->xch_cmd,
756 				    FCT_ABORT_SUCCESS, FCT_IOF_FCA_DONE);
757 				mutex_enter(&ss->ss_watch_mutex);
758 			}
759 			xchg = xchg_next;
760 		}
761 
762 		atomic_or_32(&ss->ss_flags, SS_FLAG_DOG_WAITING);
763 		(void) cv_timedwait(&ss->ss_watch_cv,
764 		    &ss->ss_watch_mutex, ddi_get_lbolt() +
765 		    (clock_t)tmp_delay);
766 		atomic_and_32(&ss->ss_flags, ~SS_FLAG_DOG_WAITING);
767 	}
768 
769 	/*
770 	 * Ensure no ongoing FLOGI, before terminate the watchdog
771 	 */
772 	if (ss->ss_sol_flogi) {
773 		fcoet_clear_sol_exchange(ss->ss_sol_flogi);
774 		fct_free(ss->ss_sol_flogi->xch_cmd);
775 		ss->ss_sol_flogi = NULL;
776 	}
777 
778 	atomic_and_32(&ss->ss_flags, ~SS_FLAG_WATCHDOG_RUNNING);
779 	mutex_exit(&ss->ss_watch_mutex);
780 }
781 
782 static void
783 fcoet_handle_sol_flogi(fcoet_soft_state_t *ss)
784 {
785 	clock_t			twosec = STMF_SEC2TICK(2);
786 
787 check_state_again:
788 	if (ss->ss_flags & SS_FLAG_PORT_DISABLED) {
789 		ss->ss_sol_flogi_state = SFS_WAIT_LINKUP;
790 	}
791 
792 	switch (ss->ss_sol_flogi_state) {
793 	case SFS_WAIT_LINKUP:
794 		if (ss->ss_sol_flogi) {
795 			if (ss->ss_sol_flogi->xch_ref == 0) {
796 				fcoet_clear_sol_exchange(ss->ss_sol_flogi);
797 				fct_free(ss->ss_sol_flogi->xch_cmd);
798 				ss->ss_sol_flogi = NULL;
799 			}
800 		}
801 		break;
802 
803 	case SFS_FLOGI_INIT:
804 		if (ss->ss_sol_flogi) {
805 			/*
806 			 * wait for the response to finish
807 			 */
808 			ss->ss_sol_flogi_state = SFS_CLEAR_FLOGI;
809 			break;
810 		}
811 		fcoet_send_sol_flogi(ss);
812 		ss->ss_sol_flogi_state++;
813 		break;
814 
815 	case SFS_FLOGI_CHECK_TIMEOUT:
816 		if ((ss->ss_sol_flogi->xch_start_time + twosec) <
817 		    ddi_get_lbolt()) {
818 			ss->ss_sol_flogi_state++;
819 		}
820 		break;
821 
822 	case SFS_ABTS_INIT:
823 		fcoet_send_sol_abts(ss->ss_sol_flogi);
824 		ss->ss_sol_flogi_state++;
825 		break;
826 
827 	case SFS_CLEAR_FLOGI:
828 		if (ss->ss_sol_flogi) {
829 			if (ss->ss_sol_flogi->xch_ref) {
830 				break;
831 			}
832 			fcoet_clear_sol_exchange(ss->ss_sol_flogi);
833 			fct_free(ss->ss_sol_flogi->xch_cmd);
834 			ss->ss_sol_flogi = NULL;
835 		}
836 		ss->ss_sol_flogi_state = SFS_FLOGI_INIT;
837 		goto check_state_again;
838 
839 	case SFS_FLOGI_ACC:
840 		ss->ss_sol_flogi_state++;
841 		goto check_state_again;
842 
843 	case SFS_FLOGI_DONE:
844 		if (!(ss->ss_flags & SS_FLAG_PORT_DISABLED) &&
845 		    ss->ss_sol_flogi) {
846 			fcoet_clear_sol_exchange(ss->ss_sol_flogi);
847 			fct_free(ss->ss_sol_flogi->xch_cmd);
848 			ss->ss_sol_flogi = NULL;
849 		}
850 
851 		/*
852 		 * We'd better to offline it first, and delay 0.1 seconds,
853 		 * before we say it's on again.
854 		 */
855 		fct_handle_event(ss->ss_port,
856 		    FCT_EVENT_LINK_DOWN, 0, NULL);
857 		delay(STMF_SEC2TICK(1)/10);
858 		fct_handle_event(ss->ss_port,
859 		    FCT_EVENT_LINK_UP, 0, NULL);
860 		break;
861 
862 	default:
863 		ASSERT(0);
864 		break;
865 	}
866 }
867 
868 /* ARGSUSED */
869 static int
870 fcoet_dbuf_init(fcoet_soft_state_t *ss)
871 {
872 	return (FCOE_SUCCESS);
873 }
874 
875 /* ARGSUSED */
876 static void
877 fcoet_dbuf_destroy(fcoet_soft_state_t *ss)
878 {
879 
880 }
881 
882 /* ARGSUSED */
883 static stmf_data_buf_t *
884 fcoet_dbuf_alloc(fct_local_port_t *port, uint32_t size, uint32_t *pminsize,
885     uint32_t flags)
886 {
887 	stmf_data_buf_t	*dbuf;
888 	int		 add_size;
889 	int		 sge_num;
890 	int		 sge_size;
891 	int		 idx;
892 	int		 ii;
893 	void		*netb;
894 	uint8_t		*fc_buf;
895 	fcoet_soft_state_t	*ss =
896 	    (fcoet_soft_state_t *)port->port_fca_private;
897 
898 	if (size > FCOET_MAX_DBUF_LEN) {
899 		if (*pminsize > FCOET_MAX_DBUF_LEN) {
900 			return (NULL);
901 		}
902 
903 		size = FCOET_MAX_DBUF_LEN;
904 	}
905 
906 	sge_num = (size - 1) / ss->ss_fcp_data_payload_size + 1;
907 	add_size = (sge_num - 1) * sizeof (struct stmf_sglist_ent) +
908 	    sge_num * sizeof (mblk_t *);
909 	dbuf = stmf_alloc(STMF_STRUCT_DATA_BUF, add_size, 0);
910 	if (dbuf == NULL) {
911 		return (NULL);
912 	}
913 	dbuf->db_buf_size = size;
914 	dbuf->db_data_size = size;
915 	dbuf->db_sglist_length = 0;
916 	dbuf->db_flags |= DB_DONT_REUSE;
917 	FCOET_SET_SEG_NUM(dbuf, sge_num);
918 
919 	/*
920 	 * Initialize non-last sg entries
921 	 */
922 	for (idx = 0; idx < sge_num - 1; idx++) {
923 		sge_size = ss->ss_fcp_data_payload_size;
924 		netb = ss->ss_eport->eport_alloc_netb(
925 		    ss->ss_eport, sizeof (fcoe_fc_frame_header_t) +
926 		    sge_size, &fc_buf);
927 		if (netb == NULL) {
928 			for (ii = 0; ii < idx; ii++) {
929 				ss->ss_eport->eport_free_netb(
930 				    FCOET_GET_NETB(dbuf, ii));
931 			}
932 			stmf_free(dbuf);
933 			FCOET_LOG("fcoe_dbuf_alloc", "no netb");
934 			return (NULL);
935 		}
936 		FCOET_SET_NETB(dbuf, idx, netb);
937 		dbuf->db_sglist[idx].seg_addr = fc_buf +
938 		    sizeof (fcoe_fc_frame_header_t);
939 		dbuf->db_sglist[idx].seg_length = sge_size;
940 	}
941 
942 	/*
943 	 * Initialize the last sg entry
944 	 */
945 	if (size % ss->ss_fcp_data_payload_size) {
946 		sge_size = P2ROUNDUP(size % ss->ss_fcp_data_payload_size, 4);
947 	} else {
948 		sge_size = ss->ss_fcp_data_payload_size;
949 	}
950 
951 	netb = ss->ss_eport->eport_alloc_netb(
952 	    ss->ss_eport,
953 	    sizeof (fcoe_fc_frame_header_t) +
954 	    sge_size, &fc_buf);
955 	if (netb == NULL) {
956 		for (ii = 0; ii < idx; ii++) {
957 			ss->ss_eport->eport_free_netb(
958 			    FCOET_GET_NETB(dbuf, ii));
959 		}
960 		stmf_free(dbuf);
961 		FCOET_LOG("fcoe_dbuf_alloc", "no netb");
962 		return (NULL);
963 	}
964 
965 	FCOET_SET_NETB(dbuf, idx, netb);
966 	dbuf->db_sglist[idx].seg_addr = fc_buf +
967 	    sizeof (fcoe_fc_frame_header_t);
968 	dbuf->db_sglist[idx].seg_length = sge_size;
969 
970 	/*
971 	 * Let COMSTAR know how many sg entries we will use
972 	 */
973 	dbuf->db_sglist_length = idx + 1;
974 
975 	return (dbuf);
976 }
977 
978 static void
979 fcoet_dbuf_free(fct_dbuf_store_t *fds, stmf_data_buf_t *dbuf)
980 {
981 	int	idx;
982 	fcoet_soft_state_t	*ss =
983 	    (fcoet_soft_state_t *)fds->fds_fca_private;
984 
985 	for (idx = 0; idx < FCOET_GET_SEG_NUM(dbuf); idx++) {
986 		if (FCOET_GET_NETB(dbuf, idx)) {
987 			ss->ss_eport->eport_free_netb(
988 			    FCOET_GET_NETB(dbuf, idx));
989 		}
990 	}
991 
992 	stmf_free(dbuf);
993 }
994 
995 /*
996  * We should have initialized fcoe_frame_t before
997  */
998 void
999 fcoet_init_tfm(fcoe_frame_t *frm, fcoet_exchange_t *xch)
1000 {
1001 	FRM2TFM(frm)->tfm_fcoe_frame = frm;
1002 	FRM2TFM(frm)->tfm_xch = xch;
1003 	FRM2TFM(frm)->tfm_seq = NULL;
1004 }
1005 
1006 /* ARGSUSED */
1007 static uint_t
1008 fcoet_sol_oxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1009 {
1010 	fcoet_soft_state_t	*ss = (fcoet_soft_state_t *)arg;
1011 
1012 	ss->ss_sol_oxid_hash_empty = 0;
1013 	FCOET_LOG("fcoet_sol_oxid_hash_empty", "one ongoing xch: %p", val);
1014 	return (MH_WALK_CONTINUE);
1015 }
1016 
1017 /* ARGSUSED */
1018 static uint_t
1019 fcoet_unsol_rxid_hash_empty(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1020 {
1021 	fcoet_soft_state_t	*ss = (fcoet_soft_state_t *)arg;
1022 
1023 	ss->ss_sol_oxid_hash_empty = 0;
1024 	FCOET_LOG("fcoet_unsol_rxid_hash_empty", "one ongoing xch: %p", val);
1025 	return (MH_WALK_CONTINUE);
1026 }
1027 
1028 /* ARGSUSED */
1029 void
1030 fcoet_modhash_find_cb(mod_hash_key_t key, mod_hash_val_t val)
1031 {
1032 	ASSERT(val != NULL);
1033 	fcoet_exchange_t *xch = (fcoet_exchange_t *)val;
1034 	FCOET_BUSY_XCHG(xch);
1035 }
1036