| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * This file is part of the Chelsio FCoE driver for Linux. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This software is available to you under a choice of one of two | 
					
						
							|  |  |  |  * licenses.  You may choose to be licensed under the terms of the GNU | 
					
						
							|  |  |  |  * General Public License (GPL) Version 2, available from the file | 
					
						
							|  |  |  |  * COPYING in the main directory of this source tree, or the | 
					
						
							|  |  |  |  * OpenIB.org BSD license below: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     Redistribution and use in source and binary forms, with or | 
					
						
							|  |  |  |  *     without modification, are permitted provided that the following | 
					
						
							|  |  |  |  *     conditions are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *      - Redistributions of source code must retain the above | 
					
						
							|  |  |  |  *        copyright notice, this list of conditions and the following | 
					
						
							|  |  |  |  *        disclaimer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *      - Redistributions in binary form must reproduce the above | 
					
						
							|  |  |  |  *        copyright notice, this list of conditions and the following | 
					
						
							|  |  |  |  *        disclaimer in the documentation and/or other materials | 
					
						
							|  |  |  |  *        provided with the distribution. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
					
						
							|  |  |  |  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
					
						
							|  |  |  |  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
					
						
							|  |  |  |  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
					
						
							|  |  |  |  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
					
						
							|  |  |  |  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
					
						
							|  |  |  |  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
					
						
							|  |  |  |  * SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/string.h>
 | 
					
						
							|  |  |  | #include <scsi/scsi_device.h>
 | 
					
						
							|  |  |  | #include <scsi/scsi_transport_fc.h>
 | 
					
						
							|  |  |  | #include <scsi/fc/fc_els.h>
 | 
					
						
							|  |  |  | #include <scsi/fc/fc_fs.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "csio_hw.h"
 | 
					
						
							|  |  |  | #include "csio_lnode.h"
 | 
					
						
							|  |  |  | #include "csio_rnode.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int csio_rnode_init(struct csio_rnode *, struct csio_lnode *); | 
					
						
							|  |  |  | static void csio_rnode_exit(struct csio_rnode *); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Static machine forward declarations */ | 
					
						
							|  |  |  | static void csio_rns_uninit(struct csio_rnode *, enum csio_rn_ev); | 
					
						
							|  |  |  | static void csio_rns_ready(struct csio_rnode *, enum csio_rn_ev); | 
					
						
							|  |  |  | static void csio_rns_offline(struct csio_rnode *, enum csio_rn_ev); | 
					
						
							|  |  |  | static void csio_rns_disappeared(struct csio_rnode *, enum csio_rn_ev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* RNF event mapping */ | 
					
						
							|  |  |  | static enum csio_rn_ev fwevt_to_rnevt[] = { | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* None */ | 
					
						
							|  |  |  | 	CSIO_RNFE_LOGGED_IN,	/* PLOGI_ACC_RCVD  */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* PLOGI_RJT_RCVD  */ | 
					
						
							|  |  |  | 	CSIO_RNFE_PLOGI_RECV,	/* PLOGI_RCVD	   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_LOGO_RECV,	/* PLOGO_RCVD	   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_PRLI_DONE,	/* PRLI_ACC_RCVD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* PRLI_RJT_RCVD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_PRLI_RECV,	/* PRLI_RCVD	   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_PRLO_RECV,	/* PRLO_RCVD	   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* NPORT_ID_CHGD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_LOGO_RECV,	/* FLOGO_RCVD	   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* CLR_VIRT_LNK_RCVD */ | 
					
						
							|  |  |  | 	CSIO_RNFE_LOGGED_IN,	/* FLOGI_ACC_RCVD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* FLOGI_RJT_RCVD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_LOGGED_IN,	/* FDISC_ACC_RCVD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* FDISC_RJT_RCVD   */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* FLOGI_TMO_MAX_RETRY */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* IMPL_LOGO_ADISC_ACC */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* IMPL_LOGO_ADISC_RJT */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* IMPL_LOGO_ADISC_CNFLT */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* PRLI_TMO		*/ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* ADISC_TMO		*/ | 
					
						
							|  |  |  | 	CSIO_RNFE_NAME_MISSING,	/* RSCN_DEV_LOST  */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* SCR_ACC_RCVD	*/ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* ADISC_RJT_RCVD */ | 
					
						
							|  |  |  | 	CSIO_RNFE_NONE,		/* LOGO_SNT */ | 
					
						
							|  |  |  | 	CSIO_RNFE_LOGO_RECV,	/* PROTO_ERR_IMPL_LOGO */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CSIO_FWE_TO_RNFE(_evt)	((_evt > PROTO_ERR_IMPL_LOGO) ?		\
 | 
					
						
							|  |  |  | 						CSIO_RNFE_NONE :	\ | 
					
						
							|  |  |  | 						fwevt_to_rnevt[_evt]) | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | csio_is_rnode_ready(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return csio_match_state(rn, csio_rns_ready); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | csio_is_rnode_uninit(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return csio_match_state(rn, csio_rns_uninit); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | csio_is_rnode_wka(uint8_t rport_type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if ((rport_type == FLOGI_VFPORT) || | 
					
						
							|  |  |  | 	    (rport_type == FDISC_VFPORT) || | 
					
						
							|  |  |  | 	    (rport_type == NS_VNPORT) || | 
					
						
							|  |  |  | 	    (rport_type == FDMI_VNPORT)) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rn_lookup - Finds the rnode with the given flowid | 
					
						
							|  |  |  |  * @ln - lnode | 
					
						
							|  |  |  |  * @flowid - flowid. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Does the rnode lookup on the given lnode and flowid.If no matching entry | 
					
						
							|  |  |  |  * found, NULL is returned. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct csio_rnode * | 
					
						
							|  |  |  | csio_rn_lookup(struct csio_lnode *ln, uint32_t flowid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; | 
					
						
							|  |  |  | 	struct list_head *tmp; | 
					
						
							|  |  |  | 	struct csio_rnode *rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each(tmp, &rnhead->sm.sm_list) { | 
					
						
							|  |  |  | 		rn = (struct csio_rnode *) tmp; | 
					
						
							|  |  |  | 		if (rn->flowid == flowid) | 
					
						
							|  |  |  | 			return rn; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rn_lookup_wwpn - Finds the rnode with the given wwpn | 
					
						
							|  |  |  |  * @ln: lnode | 
					
						
							|  |  |  |  * @wwpn: wwpn | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Does the rnode lookup on the given lnode and wwpn. If no matching entry | 
					
						
							|  |  |  |  * found, NULL is returned. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct csio_rnode * | 
					
						
							|  |  |  | csio_rn_lookup_wwpn(struct csio_lnode *ln, uint8_t *wwpn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; | 
					
						
							|  |  |  | 	struct list_head *tmp; | 
					
						
							|  |  |  | 	struct csio_rnode *rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each(tmp, &rnhead->sm.sm_list) { | 
					
						
							|  |  |  | 		rn = (struct csio_rnode *) tmp; | 
					
						
							|  |  |  | 		if (!memcmp(csio_rn_wwpn(rn), wwpn, 8)) | 
					
						
							|  |  |  | 			return rn; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * csio_rnode_lookup_portid - Finds the rnode with the given portid | 
					
						
							|  |  |  |  * @ln:		lnode | 
					
						
							|  |  |  |  * @portid:	port id | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Lookup the rnode list for a given portid. If no matching entry | 
					
						
							|  |  |  |  * found, NULL is returned. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct csio_rnode * | 
					
						
							|  |  |  | csio_rnode_lookup_portid(struct csio_lnode *ln, uint32_t portid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead; | 
					
						
							|  |  |  | 	struct list_head *tmp; | 
					
						
							|  |  |  | 	struct csio_rnode *rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each(tmp, &rnhead->sm.sm_list) { | 
					
						
							|  |  |  | 		rn = (struct csio_rnode *) tmp; | 
					
						
							|  |  |  | 		if (rn->nport_id == portid) | 
					
						
							|  |  |  | 			return rn; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | csio_rn_dup_flowid(struct csio_lnode *ln, uint32_t rdev_flowid, | 
					
						
							|  |  |  | 		    uint32_t *vnp_flowid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_rnode *rnhead; | 
					
						
							|  |  |  | 	struct list_head *tmp, *tmp1; | 
					
						
							|  |  |  | 	struct csio_rnode *rn; | 
					
						
							|  |  |  | 	struct csio_lnode *ln_tmp; | 
					
						
							|  |  |  | 	struct csio_hw *hw = csio_lnode_to_hw(ln); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each(tmp1, &hw->sln_head) { | 
					
						
							|  |  |  | 		ln_tmp = (struct csio_lnode *) tmp1; | 
					
						
							|  |  |  | 		if (ln_tmp == ln) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rnhead = (struct csio_rnode *)&ln_tmp->rnhead; | 
					
						
							|  |  |  | 		list_for_each(tmp, &rnhead->sm.sm_list) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			rn = (struct csio_rnode *) tmp; | 
					
						
							|  |  |  | 			if (csio_is_rnode_ready(rn)) { | 
					
						
							|  |  |  | 				if (rn->flowid == rdev_flowid) { | 
					
						
							|  |  |  | 					*vnp_flowid = csio_ln_flowid(ln_tmp); | 
					
						
							|  |  |  | 					return 1; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct csio_rnode * | 
					
						
							|  |  |  | csio_alloc_rnode(struct csio_lnode *ln) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_hw *hw = csio_lnode_to_hw(ln); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct csio_rnode *rn = mempool_alloc(hw->rnode_mempool, GFP_ATOMIC); | 
					
						
							|  |  |  | 	if (!rn) | 
					
						
							|  |  |  | 		goto err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(rn, 0, sizeof(struct csio_rnode)); | 
					
						
							|  |  |  | 	if (csio_rnode_init(rn, ln)) | 
					
						
							|  |  |  | 		goto err_free; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CSIO_INC_STATS(ln, n_rnode_alloc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err_free: | 
					
						
							|  |  |  | 	mempool_free(rn, hw->rnode_mempool); | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  | 	CSIO_INC_STATS(ln, n_rnode_nomem); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | csio_free_rnode(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_hw *hw = csio_lnode_to_hw(csio_rnode_to_lnode(rn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csio_rnode_exit(rn); | 
					
						
							|  |  |  | 	CSIO_INC_STATS(rn->lnp, n_rnode_free); | 
					
						
							|  |  |  | 	mempool_free(rn, hw->rnode_mempool); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_get_rnode - Gets rnode with the given flowid | 
					
						
							|  |  |  |  * @ln - lnode | 
					
						
							|  |  |  |  * @flowid - flow id. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Does the rnode lookup on the given lnode and flowid. If no matching | 
					
						
							|  |  |  |  * rnode found, then new rnode with given npid is allocated and returned. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct csio_rnode * | 
					
						
							|  |  |  | csio_get_rnode(struct csio_lnode *ln, uint32_t flowid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_rnode *rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rn = csio_rn_lookup(ln, flowid); | 
					
						
							|  |  |  | 	if (!rn) { | 
					
						
							|  |  |  | 		rn = csio_alloc_rnode(ln); | 
					
						
							|  |  |  | 		if (!rn) | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rn->flowid = flowid; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rn; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_put_rnode - Frees the given rnode | 
					
						
							|  |  |  |  * @ln - lnode | 
					
						
							|  |  |  |  * @flowid - flow id. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Does the rnode lookup on the given lnode and flowid. If no matching | 
					
						
							|  |  |  |  * rnode found, then new rnode with given npid is allocated and returned. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | csio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	CSIO_DB_ASSERT(csio_is_rnode_uninit(rn) != 0); | 
					
						
							|  |  |  | 	csio_free_rnode(rn); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_confirm_rnode - confirms rnode based on wwpn. | 
					
						
							|  |  |  |  * @ln: lnode | 
					
						
							|  |  |  |  * @rdev_flowid: remote device flowid | 
					
						
							|  |  |  |  * @rdevp: remote device params | 
					
						
							|  |  |  |  * This routines searches other rnode in list having same wwpn of new rnode. | 
					
						
							|  |  |  |  * If there is a match, then matched rnode is returned and otherwise new rnode | 
					
						
							|  |  |  |  * is returned. | 
					
						
							|  |  |  |  * returns rnode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct csio_rnode * | 
					
						
							|  |  |  | csio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid, | 
					
						
							|  |  |  | 		   struct fcoe_rdev_entry *rdevp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint8_t rport_type; | 
					
						
							|  |  |  | 	struct csio_rnode *rn, *match_rn; | 
					
						
							| 
									
										
										
										
											2013-03-14 05:09:07 +00:00
										 |  |  | 	uint32_t vnp_flowid = 0; | 
					
						
							| 
									
										
										
										
											2012-11-20 18:15:40 +05:30
										 |  |  | 	__be32 *port_id; | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-20 18:15:40 +05:30
										 |  |  | 	port_id = (__be32 *)&rdevp->r_id[0]; | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 	rport_type = | 
					
						
							|  |  |  | 		FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Drop rdev event for cntrl port */ | 
					
						
							|  |  |  | 	if (rport_type == FAB_CTLR_VNPORT) { | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			    "Unhandled rport_type:%d recv in rdev evt " | 
					
						
							|  |  |  | 			    "ssni:x%x\n", rport_type, rdev_flowid); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Lookup on flowid */ | 
					
						
							|  |  |  | 	rn = csio_rn_lookup(ln, rdev_flowid); | 
					
						
							|  |  |  | 	if (!rn) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Drop events with duplicate flowid */ | 
					
						
							|  |  |  | 		if (csio_rn_dup_flowid(ln, rdev_flowid, &vnp_flowid)) { | 
					
						
							|  |  |  | 			csio_ln_warn(ln, | 
					
						
							|  |  |  | 				     "ssni:%x already active on vnpi:%x", | 
					
						
							|  |  |  | 				     rdev_flowid, vnp_flowid); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Lookup on wwpn for NPORTs */ | 
					
						
							|  |  |  | 		rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn); | 
					
						
							|  |  |  | 		if (!rn) | 
					
						
							|  |  |  | 			goto alloc_rnode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* Lookup well-known ports with nport id */ | 
					
						
							|  |  |  | 		if (csio_is_rnode_wka(rport_type)) { | 
					
						
							|  |  |  | 			match_rn = csio_rnode_lookup_portid(ln, | 
					
						
							|  |  |  | 				      ((ntohl(*port_id) >> 8) & CSIO_DID_MASK)); | 
					
						
							|  |  |  | 			if (match_rn == NULL) { | 
					
						
							|  |  |  | 				csio_rn_flowid(rn) = CSIO_INVALID_IDX; | 
					
						
							|  |  |  | 				goto alloc_rnode; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Now compare the wwpn to confirm that | 
					
						
							|  |  |  | 			 * same port relogged in. If so update the matched rn. | 
					
						
							|  |  |  | 			 * Else, go ahead and alloc a new rnode. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (!memcmp(csio_rn_wwpn(match_rn), rdevp->wwpn, 8)) { | 
					
						
							| 
									
										
										
										
											2013-03-14 05:09:07 +00:00
										 |  |  | 				if (rn == match_rn) | 
					
						
							|  |  |  | 					goto found_rnode; | 
					
						
							|  |  |  | 				csio_ln_dbg(ln, | 
					
						
							|  |  |  | 					    "nport_id:x%x and wwpn:%llx" | 
					
						
							|  |  |  | 					    " match for ssni:x%x\n", | 
					
						
							|  |  |  | 					    rn->nport_id, | 
					
						
							|  |  |  | 					    wwn_to_u64(rdevp->wwpn), | 
					
						
							|  |  |  | 					    rdev_flowid); | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 				if (csio_is_rnode_ready(rn)) { | 
					
						
							|  |  |  | 					csio_ln_warn(ln, | 
					
						
							|  |  |  | 						     "rnode is already" | 
					
						
							|  |  |  | 						     "active ssni:x%x\n", | 
					
						
							|  |  |  | 						     rdev_flowid); | 
					
						
							|  |  |  | 					CSIO_ASSERT(0); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				csio_rn_flowid(rn) = CSIO_INVALID_IDX; | 
					
						
							|  |  |  | 				rn = match_rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Update rn */ | 
					
						
							|  |  |  | 				goto found_rnode; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			csio_rn_flowid(rn) = CSIO_INVALID_IDX; | 
					
						
							|  |  |  | 			goto alloc_rnode; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* wwpn match */ | 
					
						
							|  |  |  | 		if (!memcmp(csio_rn_wwpn(rn), rdevp->wwpn, 8)) | 
					
						
							|  |  |  | 			goto found_rnode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Search for rnode that have same wwpn */ | 
					
						
							|  |  |  | 		match_rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn); | 
					
						
							|  |  |  | 		if (match_rn != NULL) { | 
					
						
							|  |  |  | 			csio_ln_dbg(ln, | 
					
						
							|  |  |  | 				"ssni:x%x changed for rport name(wwpn):%llx " | 
					
						
							|  |  |  | 				"did:x%x\n", rdev_flowid, | 
					
						
							|  |  |  | 				wwn_to_u64(rdevp->wwpn), | 
					
						
							|  |  |  | 				match_rn->nport_id); | 
					
						
							|  |  |  | 			csio_rn_flowid(rn) = CSIO_INVALID_IDX; | 
					
						
							|  |  |  | 			rn = match_rn; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			csio_ln_dbg(ln, | 
					
						
							|  |  |  | 				"rnode wwpn mismatch found ssni:x%x " | 
					
						
							|  |  |  | 				"name(wwpn):%llx\n", | 
					
						
							|  |  |  | 				rdev_flowid, | 
					
						
							|  |  |  | 				wwn_to_u64(csio_rn_wwpn(rn))); | 
					
						
							|  |  |  | 			if (csio_is_rnode_ready(rn)) { | 
					
						
							|  |  |  | 				csio_ln_warn(ln, | 
					
						
							|  |  |  | 					     "rnode is already active " | 
					
						
							|  |  |  | 					     "wwpn:%llx ssni:x%x\n", | 
					
						
							|  |  |  | 					     wwn_to_u64(csio_rn_wwpn(rn)), | 
					
						
							|  |  |  | 					     rdev_flowid); | 
					
						
							|  |  |  | 				CSIO_ASSERT(0); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			csio_rn_flowid(rn) = CSIO_INVALID_IDX; | 
					
						
							|  |  |  | 			goto alloc_rnode; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | found_rnode: | 
					
						
							|  |  |  | 	csio_ln_dbg(ln, "found rnode:%p ssni:x%x name(wwpn):%llx\n", | 
					
						
							|  |  |  | 		rn, rdev_flowid, wwn_to_u64(rdevp->wwpn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Update flowid */ | 
					
						
							|  |  |  | 	csio_rn_flowid(rn) = rdev_flowid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* update rdev entry */ | 
					
						
							|  |  |  | 	rn->rdev_entry = rdevp; | 
					
						
							|  |  |  | 	CSIO_INC_STATS(ln, n_rnode_match); | 
					
						
							|  |  |  | 	return rn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | alloc_rnode: | 
					
						
							|  |  |  | 	rn = csio_get_rnode(ln, rdev_flowid); | 
					
						
							|  |  |  | 	if (!rn) | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csio_ln_dbg(ln, "alloc rnode:%p ssni:x%x name(wwpn):%llx\n", | 
					
						
							|  |  |  | 		rn, rdev_flowid, wwn_to_u64(rdevp->wwpn)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* update rdev entry */ | 
					
						
							|  |  |  | 	rn->rdev_entry = rdevp; | 
					
						
							|  |  |  | 	return rn; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rn_verify_rparams - verify rparams. | 
					
						
							|  |  |  |  * @ln: lnode | 
					
						
							|  |  |  |  * @rn: rnode | 
					
						
							|  |  |  |  * @rdevp: remote device params | 
					
						
							|  |  |  |  * returns success if rparams are verified. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | csio_rn_verify_rparams(struct csio_lnode *ln, struct csio_rnode *rn, | 
					
						
							|  |  |  | 			struct fcoe_rdev_entry *rdevp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint8_t null[8]; | 
					
						
							|  |  |  | 	uint8_t rport_type; | 
					
						
							|  |  |  | 	uint8_t fc_class; | 
					
						
							| 
									
										
										
										
											2012-11-20 18:15:40 +05:30
										 |  |  | 	__be32 *did; | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-20 18:15:40 +05:30
										 |  |  | 	did = (__be32 *) &rdevp->r_id[0]; | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 	rport_type = | 
					
						
							|  |  |  | 		FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type); | 
					
						
							|  |  |  | 	switch (rport_type) { | 
					
						
							|  |  |  | 	case FLOGI_VFPORT: | 
					
						
							|  |  |  | 		rn->role = CSIO_RNFR_FABRIC; | 
					
						
							|  |  |  | 		if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_FLOGI) { | 
					
						
							|  |  |  | 			csio_ln_err(ln, "ssni:x%x invalid fabric portid\n", | 
					
						
							|  |  |  | 				csio_rn_flowid(rn)); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		/* NPIV support */ | 
					
						
							|  |  |  | 		if (FW_RDEV_WR_NPIV_GET(rdevp->vft_to_qos)) | 
					
						
							|  |  |  | 			ln->flags |= CSIO_LNF_NPIVSUPP; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case NS_VNPORT: | 
					
						
							|  |  |  | 		rn->role = CSIO_RNFR_NS; | 
					
						
							|  |  |  | 		if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_DIR_SERV) { | 
					
						
							|  |  |  | 			csio_ln_err(ln, "ssni:x%x invalid fabric portid\n", | 
					
						
							|  |  |  | 				csio_rn_flowid(rn)); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case REG_FC4_VNPORT: | 
					
						
							|  |  |  | 	case REG_VNPORT: | 
					
						
							|  |  |  | 		rn->role = CSIO_RNFR_NPORT; | 
					
						
							|  |  |  | 		if (rdevp->event_cause == PRLI_ACC_RCVD || | 
					
						
							|  |  |  | 			rdevp->event_cause == PRLI_RCVD) { | 
					
						
							|  |  |  | 			if (FW_RDEV_WR_TASK_RETRY_ID_GET( | 
					
						
							|  |  |  | 							rdevp->enh_disc_to_tgt)) | 
					
						
							|  |  |  | 				rn->fcp_flags |= FCP_SPPF_OVLY_ALLOW; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (FW_RDEV_WR_RETRY_GET(rdevp->enh_disc_to_tgt)) | 
					
						
							|  |  |  | 				rn->fcp_flags |= FCP_SPPF_RETRY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (FW_RDEV_WR_CONF_CMPL_GET(rdevp->enh_disc_to_tgt)) | 
					
						
							|  |  |  | 				rn->fcp_flags |= FCP_SPPF_CONF_COMPL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (FW_RDEV_WR_TGT_GET(rdevp->enh_disc_to_tgt)) | 
					
						
							|  |  |  | 				rn->role |= CSIO_RNFR_TARGET; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (FW_RDEV_WR_INI_GET(rdevp->enh_disc_to_tgt)) | 
					
						
							|  |  |  | 				rn->role |= CSIO_RNFR_INITIATOR; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case FDMI_VNPORT: | 
					
						
							|  |  |  | 	case FAB_CTLR_VNPORT: | 
					
						
							|  |  |  | 		rn->role = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		csio_ln_err(ln, "ssni:x%x invalid rport type recv x%x\n", | 
					
						
							|  |  |  | 			csio_rn_flowid(rn), rport_type); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* validate wwpn/wwnn for Name server/remote port */ | 
					
						
							|  |  |  | 	if (rport_type == REG_VNPORT || rport_type == NS_VNPORT) { | 
					
						
							|  |  |  | 		memset(null, 0, 8); | 
					
						
							|  |  |  | 		if (!memcmp(rdevp->wwnn, null, 8)) { | 
					
						
							|  |  |  | 			csio_ln_err(ln, | 
					
						
							|  |  |  | 				    "ssni:x%x invalid wwnn received from" | 
					
						
							|  |  |  | 				    " rport did:x%x\n", | 
					
						
							|  |  |  | 				    csio_rn_flowid(rn), | 
					
						
							|  |  |  | 				    (ntohl(*did) & CSIO_DID_MASK)); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!memcmp(rdevp->wwpn, null, 8)) { | 
					
						
							|  |  |  | 			csio_ln_err(ln, | 
					
						
							|  |  |  | 				    "ssni:x%x invalid wwpn received from" | 
					
						
							|  |  |  | 				    " rport did:x%x\n", | 
					
						
							|  |  |  | 				    csio_rn_flowid(rn), | 
					
						
							|  |  |  | 				    (ntohl(*did) & CSIO_DID_MASK)); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Copy wwnn, wwpn and nport id */ | 
					
						
							|  |  |  | 	rn->nport_id = (ntohl(*did) >> 8) & CSIO_DID_MASK; | 
					
						
							|  |  |  | 	memcpy(csio_rn_wwnn(rn), rdevp->wwnn, 8); | 
					
						
							|  |  |  | 	memcpy(csio_rn_wwpn(rn), rdevp->wwpn, 8); | 
					
						
							| 
									
										
										
										
											2012-11-20 18:15:40 +05:30
										 |  |  | 	rn->rn_sparm.csp.sp_bb_data = rdevp->rcv_fr_sz; | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 	fc_class = FW_RDEV_WR_CLASS_GET(rdevp->vft_to_qos); | 
					
						
							|  |  |  | 	rn->rn_sparm.clsp[fc_class - 1].cp_class = htons(FC_CPC_VALID); | 
					
						
							| 
									
										
										
										
											2012-11-20 18:15:40 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-15 22:41:18 +05:30
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | __csio_reg_rnode(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	struct csio_hw *hw = csio_lnode_to_hw(ln); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irq(&hw->lock); | 
					
						
							|  |  |  | 	csio_reg_rnode(rn); | 
					
						
							|  |  |  | 	spin_lock_irq(&hw->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rn->role & CSIO_RNFR_TARGET) | 
					
						
							|  |  |  | 		ln->n_scsi_tgts++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rn->nport_id == FC_FID_MGMT_SERV) | 
					
						
							|  |  |  | 		csio_ln_fdmi_start(ln, (void *) rn); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | __csio_unreg_rnode(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	struct csio_hw *hw = csio_lnode_to_hw(ln); | 
					
						
							|  |  |  | 	LIST_HEAD(tmp_q); | 
					
						
							|  |  |  | 	int cmpl = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!list_empty(&rn->host_cmpl_q)) { | 
					
						
							|  |  |  | 		csio_dbg(hw, "Returning completion queue I/Os\n"); | 
					
						
							|  |  |  | 		list_splice_tail_init(&rn->host_cmpl_q, &tmp_q); | 
					
						
							|  |  |  | 		cmpl = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rn->role & CSIO_RNFR_TARGET) { | 
					
						
							|  |  |  | 		ln->n_scsi_tgts--; | 
					
						
							|  |  |  | 		ln->last_scan_ntgts--; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irq(&hw->lock); | 
					
						
							|  |  |  | 	csio_unreg_rnode(rn); | 
					
						
							|  |  |  | 	spin_lock_irq(&hw->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Cleanup I/Os that were waiting for rnode to unregister */ | 
					
						
							|  |  |  | 	if (cmpl) | 
					
						
							|  |  |  | 		csio_scsi_cleanup_io_q(csio_hw_to_scsim(hw), &tmp_q); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*****************************************************************************/ | 
					
						
							|  |  |  | /* START: Rnode SM                                                           */ | 
					
						
							|  |  |  | /*****************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rns_uninit - | 
					
						
							|  |  |  |  * @rn - rnode | 
					
						
							|  |  |  |  * @evt - SM event. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | csio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CSIO_INC_STATS(rn, n_evt_sm[evt]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (evt) { | 
					
						
							|  |  |  | 	case CSIO_RNFE_LOGGED_IN: | 
					
						
							|  |  |  | 	case CSIO_RNFE_PLOGI_RECV: | 
					
						
							|  |  |  | 		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | 
					
						
							|  |  |  | 		if (!ret) { | 
					
						
							|  |  |  | 			csio_set_state(&rn->sm, csio_rns_ready); | 
					
						
							|  |  |  | 			__csio_reg_rnode(rn); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			CSIO_INC_STATS(rn, n_err_inval); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CSIO_RNFE_LOGO_RECV: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			    "ssni:x%x Ignoring event %d recv " | 
					
						
							|  |  |  | 			    "in rn state[uninit]\n", csio_rn_flowid(rn), evt); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_drop); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			    "ssni:x%x unexp event %d recv " | 
					
						
							|  |  |  | 			    "in rn state[uninit]\n", csio_rn_flowid(rn), evt); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_unexp); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rns_ready - | 
					
						
							|  |  |  |  * @rn - rnode | 
					
						
							|  |  |  |  * @evt - SM event. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | csio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CSIO_INC_STATS(rn, n_evt_sm[evt]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (evt) { | 
					
						
							|  |  |  | 	case CSIO_RNFE_LOGGED_IN: | 
					
						
							|  |  |  | 	case CSIO_RNFE_PLOGI_RECV: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			"ssni:x%x Ignoring event %d recv from did:x%x " | 
					
						
							|  |  |  | 			"in rn state[ready]\n", csio_rn_flowid(rn), evt, | 
					
						
							|  |  |  | 			rn->nport_id); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_drop); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_PRLI_DONE: | 
					
						
							|  |  |  | 	case CSIO_RNFE_PRLI_RECV: | 
					
						
							|  |  |  | 		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | 
					
						
							|  |  |  | 		if (!ret) | 
					
						
							|  |  |  | 			__csio_reg_rnode(rn); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			CSIO_INC_STATS(rn, n_err_inval); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case CSIO_RNFE_DOWN: | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_offline); | 
					
						
							|  |  |  | 		__csio_unreg_rnode(rn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* FW expected to internally aborted outstanding SCSI WRs
 | 
					
						
							|  |  |  | 		 * and return all SCSI WRs to host with status "ABORTED". | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_LOGO_RECV: | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_offline); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		__csio_unreg_rnode(rn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* FW expected to internally aborted outstanding SCSI WRs
 | 
					
						
							|  |  |  | 		 * and return all SCSI WRs to host with status "ABORTED". | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_CLOSE: | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Each rnode receives CLOSE event when driver is removed or | 
					
						
							|  |  |  | 		 * device is reset | 
					
						
							|  |  |  | 		 * Note: All outstanding IOs on remote port need to returned | 
					
						
							|  |  |  | 		 * to uppper layer with appropriate error before sending | 
					
						
							|  |  |  | 		 * CLOSE event | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_uninit); | 
					
						
							|  |  |  | 		__csio_unreg_rnode(rn); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_NAME_MISSING: | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_disappeared); | 
					
						
							|  |  |  | 		__csio_unreg_rnode(rn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * FW expected to internally aborted outstanding SCSI WRs | 
					
						
							|  |  |  | 		 * and return all SCSI WRs to host with status "ABORTED". | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			"ssni:x%x unexp event %d recv from did:x%x " | 
					
						
							|  |  |  | 			"in rn state[uninit]\n", csio_rn_flowid(rn), evt, | 
					
						
							|  |  |  | 			rn->nport_id); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_unexp); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rns_offline - | 
					
						
							|  |  |  |  * @rn - rnode | 
					
						
							|  |  |  |  * @evt - SM event. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | csio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CSIO_INC_STATS(rn, n_evt_sm[evt]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (evt) { | 
					
						
							|  |  |  | 	case CSIO_RNFE_LOGGED_IN: | 
					
						
							|  |  |  | 	case CSIO_RNFE_PLOGI_RECV: | 
					
						
							|  |  |  | 		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | 
					
						
							|  |  |  | 		if (!ret) { | 
					
						
							|  |  |  | 			csio_set_state(&rn->sm, csio_rns_ready); | 
					
						
							|  |  |  | 			__csio_reg_rnode(rn); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			CSIO_INC_STATS(rn, n_err_inval); | 
					
						
							|  |  |  | 			csio_post_event(&rn->sm, CSIO_RNFE_CLOSE); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_DOWN: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			"ssni:x%x Ignoring event %d recv from did:x%x " | 
					
						
							|  |  |  | 			"in rn state[offline]\n", csio_rn_flowid(rn), evt, | 
					
						
							|  |  |  | 			rn->nport_id); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_drop); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_CLOSE: | 
					
						
							|  |  |  | 		/* Each rnode receives CLOSE event when driver is removed or
 | 
					
						
							|  |  |  | 		 * device is reset | 
					
						
							|  |  |  | 		 * Note: All outstanding IOs on remote port need to returned | 
					
						
							|  |  |  | 		 * to uppper layer with appropriate error before sending | 
					
						
							|  |  |  | 		 * CLOSE event | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_uninit); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_NAME_MISSING: | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_disappeared); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			"ssni:x%x unexp event %d recv from did:x%x " | 
					
						
							|  |  |  | 			"in rn state[offline]\n", csio_rn_flowid(rn), evt, | 
					
						
							|  |  |  | 			rn->nport_id); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_unexp); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rns_disappeared - | 
					
						
							|  |  |  |  * @rn - rnode | 
					
						
							|  |  |  |  * @evt - SM event. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | csio_rns_disappeared(struct csio_rnode *rn, enum csio_rn_ev evt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CSIO_INC_STATS(rn, n_evt_sm[evt]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (evt) { | 
					
						
							|  |  |  | 	case CSIO_RNFE_LOGGED_IN: | 
					
						
							|  |  |  | 	case CSIO_RNFE_PLOGI_RECV: | 
					
						
							|  |  |  | 		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry); | 
					
						
							|  |  |  | 		if (!ret) { | 
					
						
							|  |  |  | 			csio_set_state(&rn->sm, csio_rns_ready); | 
					
						
							|  |  |  | 			__csio_reg_rnode(rn); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			CSIO_INC_STATS(rn, n_err_inval); | 
					
						
							|  |  |  | 			csio_post_event(&rn->sm, CSIO_RNFE_CLOSE); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_CLOSE: | 
					
						
							|  |  |  | 		/* Each rnode receives CLOSE event when driver is removed or
 | 
					
						
							|  |  |  | 		 * device is reset. | 
					
						
							|  |  |  | 		 * Note: All outstanding IOs on remote port need to returned | 
					
						
							|  |  |  | 		 * to uppper layer with appropriate error before sending | 
					
						
							|  |  |  | 		 * CLOSE event | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		csio_set_state(&rn->sm, csio_rns_uninit); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CSIO_RNFE_DOWN: | 
					
						
							|  |  |  | 	case CSIO_RNFE_NAME_MISSING: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			"ssni:x%x Ignoring event %d recv from did x%x" | 
					
						
							|  |  |  | 			"in rn state[disappeared]\n", csio_rn_flowid(rn), | 
					
						
							|  |  |  | 			evt, rn->nport_id); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		csio_ln_dbg(ln, | 
					
						
							|  |  |  | 			"ssni:x%x unexp event %d recv from did x%x" | 
					
						
							|  |  |  | 			"in rn state[disappeared]\n", csio_rn_flowid(rn), | 
					
						
							|  |  |  | 			evt, rn->nport_id); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_unexp); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*****************************************************************************/ | 
					
						
							|  |  |  | /* END: Rnode SM                                                             */ | 
					
						
							|  |  |  | /*****************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rnode_devloss_handler - Device loss event handler | 
					
						
							|  |  |  |  * @rn: rnode | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Post event to close rnode SM and free rnode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | csio_rnode_devloss_handler(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ignore if same rnode came back as online */ | 
					
						
							|  |  |  | 	if (csio_is_rnode_ready(rn)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csio_post_event(&rn->sm, CSIO_RNFE_CLOSE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Free rn if in uninit state */ | 
					
						
							|  |  |  | 	if (csio_is_rnode_uninit(rn)) | 
					
						
							|  |  |  | 		csio_put_rnode(ln, rn); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * csio_rnode_fwevt_handler - Event handler for firmware rnode events. | 
					
						
							|  |  |  |  * @rn:		rnode | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | csio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct csio_lnode *ln = csio_rnode_to_lnode(rn); | 
					
						
							|  |  |  | 	enum csio_rn_ev evt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evt = CSIO_FWE_TO_RNFE(fwevt); | 
					
						
							|  |  |  | 	if (!evt) { | 
					
						
							|  |  |  | 		csio_ln_err(ln, "ssni:x%x Unhandled FW Rdev event: %d\n", | 
					
						
							|  |  |  | 			    csio_rn_flowid(rn), fwevt); | 
					
						
							|  |  |  | 		CSIO_INC_STATS(rn, n_evt_unexp); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	CSIO_INC_STATS(rn, n_evt_fw[fwevt]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Track previous & current events for debugging */ | 
					
						
							|  |  |  | 	rn->prev_evt = rn->cur_evt; | 
					
						
							|  |  |  | 	rn->cur_evt = fwevt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Post event to rnode SM */ | 
					
						
							|  |  |  | 	csio_post_event(&rn->sm, evt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Free rn if in uninit state */ | 
					
						
							|  |  |  | 	if (csio_is_rnode_uninit(rn)) | 
					
						
							|  |  |  | 		csio_put_rnode(ln, rn); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * csio_rnode_init - Initialize rnode. | 
					
						
							|  |  |  |  * @rn: RNode | 
					
						
							|  |  |  |  * @ln: Associated lnode | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Caller is responsible for holding the lock. The lock is required | 
					
						
							|  |  |  |  * to be held for inserting the rnode in ln->rnhead list. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | csio_rnode_init(struct csio_rnode *rn, struct csio_lnode *ln) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	csio_rnode_to_lnode(rn) = ln; | 
					
						
							|  |  |  | 	csio_init_state(&rn->sm, csio_rns_uninit); | 
					
						
							|  |  |  | 	INIT_LIST_HEAD(&rn->host_cmpl_q); | 
					
						
							|  |  |  | 	csio_rn_flowid(rn) = CSIO_INVALID_IDX; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Add rnode to list of lnodes->rnhead */ | 
					
						
							|  |  |  | 	list_add_tail(&rn->sm.sm_list, &ln->rnhead); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | csio_rnode_exit(struct csio_rnode *rn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	list_del_init(&rn->sm.sm_list); | 
					
						
							|  |  |  | 	CSIO_DB_ASSERT(list_empty(&rn->host_cmpl_q)); | 
					
						
							|  |  |  | } |