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