119370Spst/*
219370Spst * This file is part of the Chelsio FCoE driver for Linux.
398944Sobrien *
4130803Smarcel * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved.
598944Sobrien *
619370Spst * This software is available to you under a choice of one of two
798944Sobrien * licenses.  You may choose to be licensed under the terms of the GNU
819370Spst * General Public License (GPL) Version 2, available from the file
998944Sobrien * COPYING in the main directory of this source tree, or the
1098944Sobrien * OpenIB.org BSD license below:
1198944Sobrien *
1298944Sobrien *     Redistribution and use in source and binary forms, with or
1319370Spst *     without modification, are permitted provided that the following
1498944Sobrien *     conditions are met:
1598944Sobrien *
1698944Sobrien *      - Redistributions of source code must retain the above
1798944Sobrien *        copyright notice, this list of conditions and the following
1819370Spst *        disclaimer.
1998944Sobrien *
2098944Sobrien *      - Redistributions in binary form must reproduce the above
2198944Sobrien *        copyright notice, this list of conditions and the following
2298944Sobrien *        disclaimer in the documentation and/or other materials
2398944Sobrien *        provided with the distribution.
2419370Spst *
2519370Spst * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2619370Spst * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2719370Spst * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2819370Spst * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2919370Spst * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
3019370Spst * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3119370Spst * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3219370Spst * SOFTWARE.
3319370Spst */
3419370Spst
3519370Spst#include <linux/string.h>
3619370Spst#include <scsi/scsi_device.h>
3719370Spst#include <scsi/scsi_transport_fc.h>
3819370Spst#include <scsi/fc/fc_els.h>
3998944Sobrien#include <scsi/fc/fc_fs.h>
4098944Sobrien
4198944Sobrien#include "csio_hw.h"
4298944Sobrien#include "csio_lnode.h"
43130803Smarcel#include "csio_rnode.h"
44130803Smarcel
45130803Smarcelstatic int csio_rnode_init(struct csio_rnode *, struct csio_lnode *);
4619370Spststatic void csio_rnode_exit(struct csio_rnode *);
47130803Smarcel
48130803Smarcel/* Static machine forward declarations */
49130803Smarcelstatic void csio_rns_uninit(struct csio_rnode *, enum csio_rn_ev);
50130803Smarcelstatic void csio_rns_ready(struct csio_rnode *, enum csio_rn_ev);
5119370Spststatic void csio_rns_offline(struct csio_rnode *, enum csio_rn_ev);
5219370Spststatic void csio_rns_disappeared(struct csio_rnode *, enum csio_rn_ev);
5319370Spst
5419370Spst/* RNF event mapping */
5598944Sobrienstatic enum csio_rn_ev fwevt_to_rnevt[] = {
5698944Sobrien	CSIO_RNFE_NONE,		/* None */
5798944Sobrien	CSIO_RNFE_LOGGED_IN,	/* PLOGI_ACC_RCVD  */
5898944Sobrien	CSIO_RNFE_NONE,		/* PLOGI_RJT_RCVD  */
5998944Sobrien	CSIO_RNFE_PLOGI_RECV,	/* PLOGI_RCVD	   */
6019370Spst	CSIO_RNFE_LOGO_RECV,	/* PLOGO_RCVD	   */
6119370Spst	CSIO_RNFE_PRLI_DONE,	/* PRLI_ACC_RCVD   */
6219370Spst	CSIO_RNFE_NONE,		/* PRLI_RJT_RCVD   */
6319370Spst	CSIO_RNFE_PRLI_RECV,	/* PRLI_RCVD	   */
6419370Spst	CSIO_RNFE_PRLO_RECV,	/* PRLO_RCVD	   */
6519370Spst	CSIO_RNFE_NONE,		/* NPORT_ID_CHGD   */
6619370Spst	CSIO_RNFE_LOGO_RECV,	/* FLOGO_RCVD	   */
6719370Spst	CSIO_RNFE_NONE,		/* CLR_VIRT_LNK_RCVD */
6819370Spst	CSIO_RNFE_LOGGED_IN,	/* FLOGI_ACC_RCVD   */
6919370Spst	CSIO_RNFE_NONE,		/* FLOGI_RJT_RCVD   */
7019370Spst	CSIO_RNFE_LOGGED_IN,	/* FDISC_ACC_RCVD   */
7119370Spst	CSIO_RNFE_NONE,		/* FDISC_RJT_RCVD   */
7219370Spst	CSIO_RNFE_NONE,		/* FLOGI_TMO_MAX_RETRY */
7346283Sdfr	CSIO_RNFE_NONE,		/* IMPL_LOGO_ADISC_ACC */
7446283Sdfr	CSIO_RNFE_NONE,		/* IMPL_LOGO_ADISC_RJT */
7546283Sdfr	CSIO_RNFE_NONE,		/* IMPL_LOGO_ADISC_CNFLT */
7646283Sdfr	CSIO_RNFE_NONE,		/* PRLI_TMO		*/
7719370Spst	CSIO_RNFE_NONE,		/* ADISC_TMO		*/
7819370Spst	CSIO_RNFE_NAME_MISSING,	/* RSCN_DEV_LOST  */
7919370Spst	CSIO_RNFE_NONE,		/* SCR_ACC_RCVD	*/
8019370Spst	CSIO_RNFE_NONE,		/* ADISC_RJT_RCVD */
8119370Spst	CSIO_RNFE_NONE,		/* LOGO_SNT */
8219370Spst	CSIO_RNFE_LOGO_RECV,	/* PROTO_ERR_IMPL_LOGO */
8319370Spst};
8498944Sobrien
8519370Spst#define CSIO_FWE_TO_RNFE(_evt)	((_evt > PROTO_ERR_IMPL_LOGO) ?		\
8619370Spst						CSIO_RNFE_NONE :	\
8719370Spst						fwevt_to_rnevt[_evt])
8819370Spstint
8919370Spstcsio_is_rnode_ready(struct csio_rnode *rn)
9019370Spst{
9119370Spst	return csio_match_state(rn, csio_rns_ready);
9219370Spst}
9319370Spst
9419370Spststatic int
9519370Spstcsio_is_rnode_uninit(struct csio_rnode *rn)
9619370Spst{
9719370Spst	return csio_match_state(rn, csio_rns_uninit);
9819370Spst}
9919370Spst
10019370Spststatic int
10119370Spstcsio_is_rnode_wka(uint8_t rport_type)
10219370Spst{
10319370Spst	if ((rport_type == FLOGI_VFPORT) ||
10419370Spst	    (rport_type == FDISC_VFPORT) ||
10519370Spst	    (rport_type == NS_VNPORT) ||
10619370Spst	    (rport_type == FDMI_VNPORT))
10719370Spst		return 1;
10819370Spst
10998944Sobrien	return 0;
11098944Sobrien}
11198944Sobrien
11298944Sobrien/*
11398944Sobrien * csio_rn_lookup - Finds the rnode with the given flowid
11498944Sobrien * @ln - lnode
11598944Sobrien * @flowid - flowid.
11698944Sobrien *
11798944Sobrien * Does the rnode lookup on the given lnode and flowid.If no matching entry
11898944Sobrien * found, NULL is returned.
11998944Sobrien */
12098944Sobrienstatic struct csio_rnode *
12198944Sobriencsio_rn_lookup(struct csio_lnode *ln, uint32_t flowid)
12298944Sobrien{
12319370Spst	struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
12419370Spst	struct list_head *tmp;
12519370Spst	struct csio_rnode *rn;
12619370Spst
12719370Spst	list_for_each(tmp, &rnhead->sm.sm_list) {
12819370Spst		rn = (struct csio_rnode *) tmp;
12919370Spst		if (rn->flowid == flowid)
13019370Spst			return rn;
13146283Sdfr	}
13219370Spst
13398944Sobrien	return NULL;
13419370Spst}
13598944Sobrien
13619370Spst/*
13746283Sdfr * csio_rn_lookup_wwpn - Finds the rnode with the given wwpn
13846283Sdfr * @ln: lnode
13998944Sobrien * @wwpn: wwpn
14019370Spst *
14198944Sobrien * Does the rnode lookup on the given lnode and wwpn. If no matching entry
14219370Spst * found, NULL is returned.
14398944Sobrien */
14419370Spststatic struct csio_rnode *
14598944Sobriencsio_rn_lookup_wwpn(struct csio_lnode *ln, uint8_t *wwpn)
14619370Spst{
14798944Sobrien	struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
14819370Spst	struct list_head *tmp;
14998944Sobrien	struct csio_rnode *rn;
15019370Spst
15198944Sobrien	list_for_each(tmp, &rnhead->sm.sm_list) {
15219370Spst		rn = (struct csio_rnode *) tmp;
15398944Sobrien		if (!memcmp(csio_rn_wwpn(rn), wwpn, 8))
15419370Spst			return rn;
15598944Sobrien	}
15619370Spst
15798944Sobrien	return NULL;
15819370Spst}
15998944Sobrien
16019370Spst/**
16198944Sobrien * csio_rnode_lookup_portid - Finds the rnode with the given portid
16219370Spst * @ln:		lnode
16398944Sobrien * @portid:	port id
16419370Spst *
16598944Sobrien * Lookup the rnode list for a given portid. If no matching entry
16619370Spst * found, NULL is returned.
16798944Sobrien */
16819370Spststruct csio_rnode *
16998944Sobriencsio_rnode_lookup_portid(struct csio_lnode *ln, uint32_t portid)
17019370Spst{
17198944Sobrien	struct csio_rnode *rnhead = (struct csio_rnode *) &ln->rnhead;
17219370Spst	struct list_head *tmp;
17398944Sobrien	struct csio_rnode *rn;
17498944Sobrien
17519370Spst	list_for_each(tmp, &rnhead->sm.sm_list) {
17698944Sobrien		rn = (struct csio_rnode *) tmp;
17719370Spst		if (rn->nport_id == portid)
17898944Sobrien			return rn;
17919370Spst	}
18098944Sobrien
18198944Sobrien	return NULL;
18246283Sdfr}
18319370Spst
18419370Spststatic int
18519370Spstcsio_rn_dup_flowid(struct csio_lnode *ln, uint32_t rdev_flowid,
18619370Spst		    uint32_t *vnp_flowid)
18719370Spst{
18819370Spst	struct csio_rnode *rnhead;
18919370Spst	struct list_head *tmp, *tmp1;
19019370Spst	struct csio_rnode *rn;
19119370Spst	struct csio_lnode *ln_tmp;
19219370Spst	struct csio_hw *hw = csio_lnode_to_hw(ln);
19319370Spst
19498944Sobrien	list_for_each(tmp1, &hw->sln_head) {
19519370Spst		ln_tmp = (struct csio_lnode *) tmp1;
19619370Spst		if (ln_tmp == ln)
197130803Smarcel			continue;
19819370Spst
19919370Spst		rnhead = (struct csio_rnode *)&ln_tmp->rnhead;
20019370Spst		list_for_each(tmp, &rnhead->sm.sm_list) {
20119370Spst
20219370Spst			rn = (struct csio_rnode *) tmp;
20319370Spst			if (csio_is_rnode_ready(rn)) {
20419370Spst				if (rn->flowid == rdev_flowid) {
20598944Sobrien					*vnp_flowid = csio_ln_flowid(ln_tmp);
20698944Sobrien					return 1;
20719370Spst				}
20819370Spst			}
20919370Spst		}
21019370Spst	}
21119370Spst
21219370Spst	return 0;
21319370Spst}
21419370Spst
21519370Spststatic struct csio_rnode *
21619370Spstcsio_alloc_rnode(struct csio_lnode *ln)
21719370Spst{
21819370Spst	struct csio_hw *hw = csio_lnode_to_hw(ln);
21919370Spst
22098944Sobrien	struct csio_rnode *rn = mempool_alloc(hw->rnode_mempool, GFP_ATOMIC);
22198944Sobrien	if (!rn)
22219370Spst		goto err;
22319370Spst
22419370Spst	memset(rn, 0, sizeof(struct csio_rnode));
22519370Spst	if (csio_rnode_init(rn, ln))
22619370Spst		goto err_free;
22719370Spst
22819370Spst	CSIO_INC_STATS(ln, n_rnode_alloc);
22919370Spst
23019370Spst	return rn;
23119370Spst
23219370Spsterr_free:
23319370Spst	mempool_free(rn, hw->rnode_mempool);
23419370Spsterr:
23519370Spst	CSIO_INC_STATS(ln, n_rnode_nomem);
23619370Spst	return NULL;
23719370Spst}
23819370Spst
23919370Spststatic void
24019370Spstcsio_free_rnode(struct csio_rnode *rn)
24119370Spst{
24219370Spst	struct csio_hw *hw = csio_lnode_to_hw(csio_rnode_to_lnode(rn));
24319370Spst
24419370Spst	csio_rnode_exit(rn);
24519370Spst	CSIO_INC_STATS(rn->lnp, n_rnode_free);
24619370Spst	mempool_free(rn, hw->rnode_mempool);
24719370Spst}
24819370Spst
24919370Spst/*
25019370Spst * csio_get_rnode - Gets rnode with the given flowid
25119370Spst * @ln - lnode
25298944Sobrien * @flowid - flow id.
25319370Spst *
25419370Spst * Does the rnode lookup on the given lnode and flowid. If no matching
25519370Spst * rnode found, then new rnode with given npid is allocated and returned.
25619370Spst */
25719370Spststatic struct csio_rnode *
25819370Spstcsio_get_rnode(struct csio_lnode *ln, uint32_t flowid)
25919370Spst{
26019370Spst	struct csio_rnode *rn;
26119370Spst
26219370Spst	rn = csio_rn_lookup(ln, flowid);
26319370Spst	if (!rn) {
26419370Spst		rn = csio_alloc_rnode(ln);
26519370Spst		if (!rn)
26619370Spst			return NULL;
26719370Spst
26819370Spst		rn->flowid = flowid;
26919370Spst	}
27019370Spst
27119370Spst	return rn;
27219370Spst}
27319370Spst
27419370Spst/*
27598944Sobrien * csio_put_rnode - Frees the given rnode
27619370Spst * @ln - lnode
27719370Spst * @flowid - flow id.
27819370Spst *
27919370Spst * Does the rnode lookup on the given lnode and flowid. If no matching
28019370Spst * rnode found, then new rnode with given npid is allocated and returned.
28119370Spst */
282130803Smarcelvoid
28398944Sobriencsio_put_rnode(struct csio_lnode *ln, struct csio_rnode *rn)
28419370Spst{
28519370Spst	CSIO_DB_ASSERT(csio_is_rnode_uninit(rn) != 0);
28619370Spst	csio_free_rnode(rn);
28719370Spst}
28819370Spst
28946283Sdfr/*
29046283Sdfr * csio_confirm_rnode - confirms rnode based on wwpn.
29146283Sdfr * @ln: lnode
29246283Sdfr * @rdev_flowid: remote device flowid
29319370Spst * @rdevp: remote device params
29419370Spst * This routines searches other rnode in list having same wwpn of new rnode.
29519370Spst * If there is a match, then matched rnode is returned and otherwise new rnode
29619370Spst * is returned.
29746283Sdfr * returns rnode.
29819370Spst */
29998944Sobrienstruct csio_rnode *
30046283Sdfrcsio_confirm_rnode(struct csio_lnode *ln, uint32_t rdev_flowid,
30119370Spst		   struct fcoe_rdev_entry *rdevp)
30219370Spst{
30319370Spst	uint8_t rport_type;
30419370Spst	struct csio_rnode *rn, *match_rn;
30598944Sobrien	uint32_t vnp_flowid = 0;
30698944Sobrien	__be32 *port_id;
30798944Sobrien
30898944Sobrien	port_id = (__be32 *)&rdevp->r_id[0];
30946283Sdfr	rport_type =
31019370Spst		FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type);
31119370Spst
31219370Spst	/* Drop rdev event for cntrl port */
313130803Smarcel	if (rport_type == FAB_CTLR_VNPORT) {
31446283Sdfr		csio_ln_dbg(ln,
31519370Spst			    "Unhandled rport_type:%d recv in rdev evt "
31619370Spst			    "ssni:x%x\n", rport_type, rdev_flowid);
31719370Spst		return NULL;
31819370Spst	}
31919370Spst
32019370Spst	/* Lookup on flowid */
32119370Spst	rn = csio_rn_lookup(ln, rdev_flowid);
322130803Smarcel	if (!rn) {
323130803Smarcel
32498944Sobrien		/* Drop events with duplicate flowid */
32598944Sobrien		if (csio_rn_dup_flowid(ln, rdev_flowid, &vnp_flowid)) {
32698944Sobrien			csio_ln_warn(ln,
32798944Sobrien				     "ssni:%x already active on vnpi:%x",
32898944Sobrien				     rdev_flowid, vnp_flowid);
32998944Sobrien			return NULL;
33019370Spst		}
33198944Sobrien
33298944Sobrien		/* Lookup on wwpn for NPORTs */
33398944Sobrien		rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn);
33419370Spst		if (!rn)
33598944Sobrien			goto alloc_rnode;
33619370Spst
33719370Spst	} else {
33819370Spst		/* Lookup well-known ports with nport id */
33919370Spst		if (csio_is_rnode_wka(rport_type)) {
34019370Spst			match_rn = csio_rnode_lookup_portid(ln,
34119370Spst				      ((ntohl(*port_id) >> 8) & CSIO_DID_MASK));
34219370Spst			if (match_rn == NULL) {
34319370Spst				csio_rn_flowid(rn) = CSIO_INVALID_IDX;
34419370Spst				goto alloc_rnode;
34519370Spst			}
34619370Spst
347130803Smarcel			/*
34898944Sobrien			 * Now compare the wwpn to confirm that
34919370Spst			 * same port relogged in. If so update the matched rn.
350130803Smarcel			 * Else, go ahead and alloc a new rnode.
35119370Spst			 */
35219370Spst			if (!memcmp(csio_rn_wwpn(match_rn), rdevp->wwpn, 8)) {
353130803Smarcel				if (rn == match_rn)
354130803Smarcel					goto found_rnode;
355130803Smarcel				csio_ln_dbg(ln,
35619370Spst					    "nport_id:x%x and wwpn:%llx"
357130803Smarcel					    " match for ssni:x%x\n",
35819370Spst					    rn->nport_id,
359130803Smarcel					    wwn_to_u64(rdevp->wwpn),
360130803Smarcel					    rdev_flowid);
36119370Spst				if (csio_is_rnode_ready(rn)) {
362130803Smarcel					csio_ln_warn(ln,
363130803Smarcel						     "rnode is already"
364130803Smarcel						     "active ssni:x%x\n",
365130803Smarcel						     rdev_flowid);
366130803Smarcel					CSIO_ASSERT(0);
367130803Smarcel				}
368130803Smarcel				csio_rn_flowid(rn) = CSIO_INVALID_IDX;
369130803Smarcel				rn = match_rn;
370130803Smarcel
371130803Smarcel				/* Update rn */
372130803Smarcel				goto found_rnode;
373130803Smarcel			}
374130803Smarcel			csio_rn_flowid(rn) = CSIO_INVALID_IDX;
375130803Smarcel			goto alloc_rnode;
376130803Smarcel		}
377130803Smarcel
378130803Smarcel		/* wwpn match */
37919370Spst		if (!memcmp(csio_rn_wwpn(rn), rdevp->wwpn, 8))
380130803Smarcel			goto found_rnode;
38119370Spst
38219370Spst		/* Search for rnode that have same wwpn */
38398944Sobrien		match_rn = csio_rn_lookup_wwpn(ln, rdevp->wwpn);
384130803Smarcel		if (match_rn != NULL) {
385130803Smarcel			csio_ln_dbg(ln,
38698944Sobrien				"ssni:x%x changed for rport name(wwpn):%llx "
387130803Smarcel				"did:x%x\n", rdev_flowid,
38898944Sobrien				wwn_to_u64(rdevp->wwpn),
38919370Spst				match_rn->nport_id);
39019370Spst			csio_rn_flowid(rn) = CSIO_INVALID_IDX;
39119370Spst			rn = match_rn;
39219370Spst		} else {
39319370Spst			csio_ln_dbg(ln,
39419370Spst				"rnode wwpn mismatch found ssni:x%x "
39519370Spst				"name(wwpn):%llx\n",
39619370Spst				rdev_flowid,
39719370Spst				wwn_to_u64(csio_rn_wwpn(rn)));
39819370Spst			if (csio_is_rnode_ready(rn)) {
39919370Spst				csio_ln_warn(ln,
40019370Spst					     "rnode is already active "
40119370Spst					     "wwpn:%llx ssni:x%x\n",
40219370Spst					     wwn_to_u64(csio_rn_wwpn(rn)),
40319370Spst					     rdev_flowid);
40419370Spst				CSIO_ASSERT(0);
40519370Spst			}
40619370Spst			csio_rn_flowid(rn) = CSIO_INVALID_IDX;
40719370Spst			goto alloc_rnode;
40819370Spst		}
40919370Spst	}
41019370Spst
41119370Spstfound_rnode:
41219370Spst	csio_ln_dbg(ln, "found rnode:%p ssni:x%x name(wwpn):%llx\n",
41319370Spst		rn, rdev_flowid, wwn_to_u64(rdevp->wwpn));
41419370Spst
41519370Spst	/* Update flowid */
41619370Spst	csio_rn_flowid(rn) = rdev_flowid;
41719370Spst
41819370Spst	/* update rdev entry */
41919370Spst	rn->rdev_entry = rdevp;
42019370Spst	CSIO_INC_STATS(ln, n_rnode_match);
42119370Spst	return rn;
42219370Spst
42319370Spstalloc_rnode:
42419370Spst	rn = csio_get_rnode(ln, rdev_flowid);
42519370Spst	if (!rn)
42619370Spst		return NULL;
42719370Spst
42819370Spst	csio_ln_dbg(ln, "alloc rnode:%p ssni:x%x name(wwpn):%llx\n",
42919370Spst		rn, rdev_flowid, wwn_to_u64(rdevp->wwpn));
43019370Spst
43119370Spst	/* update rdev entry */
43219370Spst	rn->rdev_entry = rdevp;
43319370Spst	return rn;
43419370Spst}
43519370Spst
43698944Sobrien/*
43798944Sobrien * csio_rn_verify_rparams - verify rparams.
43898944Sobrien * @ln: lnode
43998944Sobrien * @rn: rnode
44019370Spst * @rdevp: remote device params
44119370Spst * returns success if rparams are verified.
44219370Spst */
44398944Sobrienstatic int
44498944Sobriencsio_rn_verify_rparams(struct csio_lnode *ln, struct csio_rnode *rn,
44519370Spst			struct fcoe_rdev_entry *rdevp)
44619370Spst{
44719370Spst	uint8_t null[8];
44898944Sobrien	uint8_t rport_type;
44998944Sobrien	uint8_t fc_class;
45098944Sobrien	__be32 *did;
45198944Sobrien
45298944Sobrien	did = (__be32 *) &rdevp->r_id[0];
45398944Sobrien	rport_type =
45419370Spst		FW_RDEV_WR_RPORT_TYPE_GET(rdevp->rd_xfer_rdy_to_rport_type);
45519370Spst	switch (rport_type) {
45619370Spst	case FLOGI_VFPORT:
45719370Spst		rn->role = CSIO_RNFR_FABRIC;
45898944Sobrien		if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_FLOGI) {
45919370Spst			csio_ln_err(ln, "ssni:x%x invalid fabric portid\n",
46019370Spst				csio_rn_flowid(rn));
46119370Spst			return -EINVAL;
46219370Spst		}
46398944Sobrien		/* NPIV support */
46498944Sobrien		if (FW_RDEV_WR_NPIV_GET(rdevp->vft_to_qos))
46519370Spst			ln->flags |= CSIO_LNF_NPIVSUPP;
46619370Spst
46719370Spst		break;
46898944Sobrien
46998944Sobrien	case NS_VNPORT:
47098944Sobrien		rn->role = CSIO_RNFR_NS;
47198944Sobrien		if (((ntohl(*did) >> 8) & CSIO_DID_MASK) != FC_FID_DIR_SERV) {
47219370Spst			csio_ln_err(ln, "ssni:x%x invalid fabric portid\n",
47319370Spst				csio_rn_flowid(rn));
47419370Spst			return -EINVAL;
47519370Spst		}
47619370Spst		break;
47719370Spst
47819370Spst	case REG_FC4_VNPORT:
47919370Spst	case REG_VNPORT:
48019370Spst		rn->role = CSIO_RNFR_NPORT;
48119370Spst		if (rdevp->event_cause == PRLI_ACC_RCVD ||
48219370Spst			rdevp->event_cause == PRLI_RCVD) {
48319370Spst			if (FW_RDEV_WR_TASK_RETRY_ID_GET(
48419370Spst							rdevp->enh_disc_to_tgt))
48519370Spst				rn->fcp_flags |= FCP_SPPF_OVLY_ALLOW;
48619370Spst
48719370Spst			if (FW_RDEV_WR_RETRY_GET(rdevp->enh_disc_to_tgt))
48819370Spst				rn->fcp_flags |= FCP_SPPF_RETRY;
48998944Sobrien
49098944Sobrien			if (FW_RDEV_WR_CONF_CMPL_GET(rdevp->enh_disc_to_tgt))
49198944Sobrien				rn->fcp_flags |= FCP_SPPF_CONF_COMPL;
49298944Sobrien
49398944Sobrien			if (FW_RDEV_WR_TGT_GET(rdevp->enh_disc_to_tgt))
49498944Sobrien				rn->role |= CSIO_RNFR_TARGET;
49519370Spst
49619370Spst			if (FW_RDEV_WR_INI_GET(rdevp->enh_disc_to_tgt))
49719370Spst				rn->role |= CSIO_RNFR_INITIATOR;
49819370Spst		}
49919370Spst
50019370Spst		break;
50119370Spst
50298944Sobrien	case FDMI_VNPORT:
50346283Sdfr	case FAB_CTLR_VNPORT:
50498944Sobrien		rn->role = 0;
505130803Smarcel		break;
50619370Spst
50719370Spst	default:
50819370Spst		csio_ln_err(ln, "ssni:x%x invalid rport type recv x%x\n",
50919370Spst			csio_rn_flowid(rn), rport_type);
51019370Spst		return -EINVAL;
51119370Spst	}
51219370Spst
51319370Spst	/* validate wwpn/wwnn for Name server/remote port */
51419370Spst	if (rport_type == REG_VNPORT || rport_type == NS_VNPORT) {
51519370Spst		memset(null, 0, 8);
51619370Spst		if (!memcmp(rdevp->wwnn, null, 8)) {
51719370Spst			csio_ln_err(ln,
51898944Sobrien				    "ssni:x%x invalid wwnn received from"
51919370Spst				    " rport did:x%x\n",
52019370Spst				    csio_rn_flowid(rn),
52119370Spst				    (ntohl(*did) & CSIO_DID_MASK));
52219370Spst			return -EINVAL;
52319370Spst		}
52498944Sobrien
52598944Sobrien		if (!memcmp(rdevp->wwpn, null, 8)) {
52619370Spst			csio_ln_err(ln,
52719370Spst				    "ssni:x%x invalid wwpn received from"
52819370Spst				    " rport did:x%x\n",
52919370Spst				    csio_rn_flowid(rn),
53019370Spst				    (ntohl(*did) & CSIO_DID_MASK));
53119370Spst			return -EINVAL;
53219370Spst		}
53319370Spst
53419370Spst	}
53519370Spst
53619370Spst	/* Copy wwnn, wwpn and nport id */
53798944Sobrien	rn->nport_id = (ntohl(*did) >> 8) & CSIO_DID_MASK;
53898944Sobrien	memcpy(csio_rn_wwnn(rn), rdevp->wwnn, 8);
53919370Spst	memcpy(csio_rn_wwpn(rn), rdevp->wwpn, 8);
54098944Sobrien	rn->rn_sparm.csp.sp_bb_data = rdevp->rcv_fr_sz;
54198944Sobrien	fc_class = FW_RDEV_WR_CLASS_GET(rdevp->vft_to_qos);
54298944Sobrien	rn->rn_sparm.clsp[fc_class - 1].cp_class = htons(FC_CPC_VALID);
54398944Sobrien
54498944Sobrien	return 0;
54598944Sobrien}
54698944Sobrien
54798944Sobrienstatic void
54898944Sobrien__csio_reg_rnode(struct csio_rnode *rn)
54998944Sobrien{
55098944Sobrien	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
55198944Sobrien	struct csio_hw *hw = csio_lnode_to_hw(ln);
55298944Sobrien
55398944Sobrien	spin_unlock_irq(&hw->lock);
55498944Sobrien	csio_reg_rnode(rn);
55598944Sobrien	spin_lock_irq(&hw->lock);
55698944Sobrien
55798944Sobrien	if (rn->role & CSIO_RNFR_TARGET)
55898944Sobrien		ln->n_scsi_tgts++;
55998944Sobrien
56098944Sobrien	if (rn->nport_id == FC_FID_MGMT_SERV)
56198944Sobrien		csio_ln_fdmi_start(ln, (void *) rn);
56298944Sobrien}
56398944Sobrien
56498944Sobrienstatic void
56598944Sobrien__csio_unreg_rnode(struct csio_rnode *rn)
56698944Sobrien{
56798944Sobrien	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
56898944Sobrien	struct csio_hw *hw = csio_lnode_to_hw(ln);
56998944Sobrien	LIST_HEAD(tmp_q);
57098944Sobrien	int cmpl = 0;
57198944Sobrien
57298944Sobrien	if (!list_empty(&rn->host_cmpl_q)) {
57398944Sobrien		csio_dbg(hw, "Returning completion queue I/Os\n");
57498944Sobrien		list_splice_tail_init(&rn->host_cmpl_q, &tmp_q);
57598944Sobrien		cmpl = 1;
57698944Sobrien	}
57798944Sobrien
57898944Sobrien	if (rn->role & CSIO_RNFR_TARGET) {
57998944Sobrien		ln->n_scsi_tgts--;
58098944Sobrien		ln->last_scan_ntgts--;
58198944Sobrien	}
58298944Sobrien
58398944Sobrien	spin_unlock_irq(&hw->lock);
58498944Sobrien	csio_unreg_rnode(rn);
58598944Sobrien	spin_lock_irq(&hw->lock);
58698944Sobrien
58798944Sobrien	/* Cleanup I/Os that were waiting for rnode to unregister */
58898944Sobrien	if (cmpl)
58998944Sobrien		csio_scsi_cleanup_io_q(csio_hw_to_scsim(hw), &tmp_q);
59098944Sobrien
59198944Sobrien}
59298944Sobrien
59398944Sobrien/*****************************************************************************/
59498944Sobrien/* START: Rnode SM                                                           */
59598944Sobrien/*****************************************************************************/
59698944Sobrien
59719370Spst/*
59819370Spst * csio_rns_uninit -
59919370Spst * @rn - rnode
60019370Spst * @evt - SM event.
60146283Sdfr *
60298944Sobrien */
60398944Sobrienstatic void
60498944Sobriencsio_rns_uninit(struct csio_rnode *rn, enum csio_rn_ev evt)
60598944Sobrien{
60619370Spst	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
60798944Sobrien	int ret = 0;
60898944Sobrien
60946283Sdfr	CSIO_INC_STATS(rn, n_evt_sm[evt]);
61046283Sdfr
61146283Sdfr	switch (evt) {
61246283Sdfr	case CSIO_RNFE_LOGGED_IN:
61346283Sdfr	case CSIO_RNFE_PLOGI_RECV:
61498944Sobrien		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
61546283Sdfr		if (!ret) {
61646283Sdfr			csio_set_state(&rn->sm, csio_rns_ready);
61746283Sdfr			__csio_reg_rnode(rn);
61846283Sdfr		} else {
61919370Spst			CSIO_INC_STATS(rn, n_err_inval);
62019370Spst		}
62119370Spst		break;
62219370Spst	case CSIO_RNFE_LOGO_RECV:
62319370Spst		csio_ln_dbg(ln,
62419370Spst			    "ssni:x%x Ignoring event %d recv "
62519370Spst			    "in rn state[uninit]\n", csio_rn_flowid(rn), evt);
62619370Spst		CSIO_INC_STATS(rn, n_evt_drop);
62719370Spst		break;
62846283Sdfr	default:
62946283Sdfr		csio_ln_dbg(ln,
63019370Spst			    "ssni:x%x unexp event %d recv "
63119370Spst			    "in rn state[uninit]\n", csio_rn_flowid(rn), evt);
63219370Spst		CSIO_INC_STATS(rn, n_evt_unexp);
63346283Sdfr		break;
634130803Smarcel	}
635130803Smarcel}
63619370Spst
637130803Smarcel/*
63819370Spst * csio_rns_ready -
63919370Spst * @rn - rnode
64019370Spst * @evt - SM event.
64119370Spst *
64219370Spst */
64319370Spststatic void
64419370Spstcsio_rns_ready(struct csio_rnode *rn, enum csio_rn_ev evt)
64519370Spst{
64619370Spst	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
64719370Spst	int ret = 0;
64819370Spst
649130803Smarcel	CSIO_INC_STATS(rn, n_evt_sm[evt]);
650130803Smarcel
65119370Spst	switch (evt) {
652130803Smarcel	case CSIO_RNFE_LOGGED_IN:
65319370Spst	case CSIO_RNFE_PLOGI_RECV:
65419370Spst		csio_ln_dbg(ln,
65519370Spst			"ssni:x%x Ignoring event %d recv from did:x%x "
65698944Sobrien			"in rn state[ready]\n", csio_rn_flowid(rn), evt,
65719370Spst			rn->nport_id);
65819370Spst		CSIO_INC_STATS(rn, n_evt_drop);
65919370Spst		break;
66019370Spst
66119370Spst	case CSIO_RNFE_PRLI_DONE:
66219370Spst	case CSIO_RNFE_PRLI_RECV:
66319370Spst		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
66419370Spst		if (!ret)
66519370Spst			__csio_reg_rnode(rn);
66619370Spst		else
66798944Sobrien			CSIO_INC_STATS(rn, n_err_inval);
66819370Spst
66998944Sobrien		break;
67019370Spst	case CSIO_RNFE_DOWN:
67198944Sobrien		csio_set_state(&rn->sm, csio_rns_offline);
67298944Sobrien		__csio_unreg_rnode(rn);
67319370Spst
67419370Spst		/* FW expected to internally aborted outstanding SCSI WRs
67519370Spst		 * and return all SCSI WRs to host with status "ABORTED".
67619370Spst		 */
67746283Sdfr		break;
67846283Sdfr
67919370Spst	case CSIO_RNFE_LOGO_RECV:
68098944Sobrien		csio_set_state(&rn->sm, csio_rns_offline);
68198944Sobrien
68298944Sobrien		__csio_unreg_rnode(rn);
68398944Sobrien
68419370Spst		/* FW expected to internally aborted outstanding SCSI WRs
68598944Sobrien		 * and return all SCSI WRs to host with status "ABORTED".
68698944Sobrien		 */
68798944Sobrien		break;
68898944Sobrien
68919370Spst	case CSIO_RNFE_CLOSE:
69098944Sobrien		/*
69198944Sobrien		 * Each rnode receives CLOSE event when driver is removed or
69298944Sobrien		 * device is reset
69398944Sobrien		 * Note: All outstanding IOs on remote port need to returned
69419370Spst		 * to uppper layer with appropriate error before sending
69598944Sobrien		 * CLOSE event
69619370Spst		 */
69719370Spst		csio_set_state(&rn->sm, csio_rns_uninit);
69819370Spst		__csio_unreg_rnode(rn);
69919370Spst		break;
70019370Spst
70198944Sobrien	case CSIO_RNFE_NAME_MISSING:
70219370Spst		csio_set_state(&rn->sm, csio_rns_disappeared);
70398944Sobrien		__csio_unreg_rnode(rn);
70498944Sobrien
70598944Sobrien		/*
70698944Sobrien		 * FW expected to internally aborted outstanding SCSI WRs
70798944Sobrien		 * and return all SCSI WRs to host with status "ABORTED".
70898944Sobrien		 */
70998944Sobrien
71098944Sobrien		break;
71198944Sobrien
71298944Sobrien	default:
71398944Sobrien		csio_ln_dbg(ln,
71498944Sobrien			"ssni:x%x unexp event %d recv from did:x%x "
71598944Sobrien			"in rn state[uninit]\n", csio_rn_flowid(rn), evt,
71646283Sdfr			rn->nport_id);
71719370Spst		CSIO_INC_STATS(rn, n_evt_unexp);
71819370Spst		break;
71919370Spst	}
72019370Spst}
72119370Spst
72219370Spst/*
72319370Spst * csio_rns_offline -
72498944Sobrien * @rn - rnode
72519370Spst * @evt - SM event.
72619370Spst *
72719370Spst */
72819370Spststatic void
72919370Spstcsio_rns_offline(struct csio_rnode *rn, enum csio_rn_ev evt)
73019370Spst{
73119370Spst	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
73219370Spst	int ret = 0;
73319370Spst
73419370Spst	CSIO_INC_STATS(rn, n_evt_sm[evt]);
73519370Spst
73698944Sobrien	switch (evt) {
73719370Spst	case CSIO_RNFE_LOGGED_IN:
73819370Spst	case CSIO_RNFE_PLOGI_RECV:
73919370Spst		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
74019370Spst		if (!ret) {
74119370Spst			csio_set_state(&rn->sm, csio_rns_ready);
74219370Spst			__csio_reg_rnode(rn);
74319370Spst		} else {
74419370Spst			CSIO_INC_STATS(rn, n_err_inval);
74519370Spst			csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
74619370Spst		}
74719370Spst		break;
74819370Spst
74919370Spst	case CSIO_RNFE_DOWN:
75019370Spst		csio_ln_dbg(ln,
75119370Spst			"ssni:x%x Ignoring event %d recv from did:x%x "
75219370Spst			"in rn state[offline]\n", csio_rn_flowid(rn), evt,
75319370Spst			rn->nport_id);
75419370Spst		CSIO_INC_STATS(rn, n_evt_drop);
75519370Spst		break;
75619370Spst
75746283Sdfr	case CSIO_RNFE_CLOSE:
75846283Sdfr		/* Each rnode receives CLOSE event when driver is removed or
75919370Spst		 * device is reset
76019370Spst		 * Note: All outstanding IOs on remote port need to returned
76119370Spst		 * to uppper layer with appropriate error before sending
76219370Spst		 * CLOSE event
76319370Spst		 */
76419370Spst		csio_set_state(&rn->sm, csio_rns_uninit);
76519370Spst		break;
76619370Spst
76719370Spst	case CSIO_RNFE_NAME_MISSING:
76898944Sobrien		csio_set_state(&rn->sm, csio_rns_disappeared);
76919370Spst		break;
770130803Smarcel
771130803Smarcel	default:
772130803Smarcel		csio_ln_dbg(ln,
77319370Spst			"ssni:x%x unexp event %d recv from did:x%x "
774130803Smarcel			"in rn state[offline]\n", csio_rn_flowid(rn), evt,
775130803Smarcel			rn->nport_id);
77619370Spst		CSIO_INC_STATS(rn, n_evt_unexp);
77719370Spst		break;
77819370Spst	}
77919370Spst}
78019370Spst
78146283Sdfr/*
78219370Spst * csio_rns_disappeared -
78319370Spst * @rn - rnode
78419370Spst * @evt - SM event.
78519370Spst *
78619370Spst */
78719370Spststatic void
78846283Sdfrcsio_rns_disappeared(struct csio_rnode *rn, enum csio_rn_ev evt)
78946283Sdfr{
79046283Sdfr	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
79119370Spst	int ret = 0;
79219370Spst
79319370Spst	CSIO_INC_STATS(rn, n_evt_sm[evt]);
79419370Spst
79519370Spst	switch (evt) {
79619370Spst	case CSIO_RNFE_LOGGED_IN:
79719370Spst	case CSIO_RNFE_PLOGI_RECV:
79819370Spst		ret = csio_rn_verify_rparams(ln, rn, rn->rdev_entry);
79919370Spst		if (!ret) {
80019370Spst			csio_set_state(&rn->sm, csio_rns_ready);
80119370Spst			__csio_reg_rnode(rn);
80219370Spst		} else {
80319370Spst			CSIO_INC_STATS(rn, n_err_inval);
80419370Spst			csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
80519370Spst		}
80619370Spst		break;
80719370Spst
80819370Spst	case CSIO_RNFE_CLOSE:
80919370Spst		/* Each rnode receives CLOSE event when driver is removed or
81019370Spst		 * device is reset.
81119370Spst		 * Note: All outstanding IOs on remote port need to returned
81219370Spst		 * to uppper layer with appropriate error before sending
81319370Spst		 * CLOSE event
81419370Spst		 */
81519370Spst		csio_set_state(&rn->sm, csio_rns_uninit);
81619370Spst		break;
81719370Spst
81819370Spst	case CSIO_RNFE_DOWN:
81919370Spst	case CSIO_RNFE_NAME_MISSING:
82019370Spst		csio_ln_dbg(ln,
82119370Spst			"ssni:x%x Ignoring event %d recv from did x%x"
82219370Spst			"in rn state[disappeared]\n", csio_rn_flowid(rn),
82346283Sdfr			evt, rn->nport_id);
82446283Sdfr		break;
82546283Sdfr
82646283Sdfr	default:
82746283Sdfr		csio_ln_dbg(ln,
82898944Sobrien			"ssni:x%x unexp event %d recv from did x%x"
82998944Sobrien			"in rn state[disappeared]\n", csio_rn_flowid(rn),
83098944Sobrien			evt, rn->nport_id);
83198944Sobrien		CSIO_INC_STATS(rn, n_evt_unexp);
83298944Sobrien		break;
83398944Sobrien	}
83498944Sobrien}
83598944Sobrien
83646283Sdfr/*****************************************************************************/
83746283Sdfr/* END: Rnode SM                                                             */
83846283Sdfr/*****************************************************************************/
83946283Sdfr
84046283Sdfr/*
84198944Sobrien * csio_rnode_devloss_handler - Device loss event handler
84219370Spst * @rn: rnode
84319370Spst *
84419370Spst * Post event to close rnode SM and free rnode.
84519370Spst */
84619370Spstvoid
84719370Spstcsio_rnode_devloss_handler(struct csio_rnode *rn)
84819370Spst{
84998944Sobrien	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
85019370Spst
85119370Spst	/* ignore if same rnode came back as online */
85219370Spst	if (csio_is_rnode_ready(rn))
85319370Spst		return;
85419370Spst
85519370Spst	csio_post_event(&rn->sm, CSIO_RNFE_CLOSE);
85619370Spst
85719370Spst	/* Free rn if in uninit state */
85819370Spst	if (csio_is_rnode_uninit(rn))
85919370Spst		csio_put_rnode(ln, rn);
86019370Spst}
86119370Spst
86298944Sobrien/**
86398944Sobrien * csio_rnode_fwevt_handler - Event handler for firmware rnode events.
86498944Sobrien * @rn:		rnode
86519370Spst * @fwevt:	firmware event to handle
86619370Spst */
86798944Sobrienvoid
86819370Spstcsio_rnode_fwevt_handler(struct csio_rnode *rn, uint8_t fwevt)
86919370Spst{
870130803Smarcel	struct csio_lnode *ln = csio_rnode_to_lnode(rn);
871130803Smarcel	enum csio_rn_ev evt;
87298944Sobrien
87319370Spst	evt = CSIO_FWE_TO_RNFE(fwevt);
87419370Spst	if (!evt) {
87519370Spst		csio_ln_err(ln, "ssni:x%x Unhandled FW Rdev event: %d\n",
87619370Spst			    csio_rn_flowid(rn), fwevt);
87719370Spst		CSIO_INC_STATS(rn, n_evt_unexp);
87819370Spst		return;
87919370Spst	}
88019370Spst	CSIO_INC_STATS(rn, n_evt_fw[fwevt]);
88119370Spst
88219370Spst	/* Track previous & current events for debugging */
88319370Spst	rn->prev_evt = rn->cur_evt;
88419370Spst	rn->cur_evt = fwevt;
88519370Spst
88619370Spst	/* Post event to rnode SM */
88719370Spst	csio_post_event(&rn->sm, evt);
88819370Spst
88919370Spst	/* Free rn if in uninit state */
89019370Spst	if (csio_is_rnode_uninit(rn))
89119370Spst		csio_put_rnode(ln, rn);
89219370Spst}
89319370Spst
89419370Spst/*
89519370Spst * csio_rnode_init - Initialize rnode.
89619370Spst * @rn: RNode
89798944Sobrien * @ln: Associated lnode
89819370Spst *
89919370Spst * Caller is responsible for holding the lock. The lock is required
90019370Spst * to be held for inserting the rnode in ln->rnhead list.
90119370Spst */
90219370Spststatic int
90319370Spstcsio_rnode_init(struct csio_rnode *rn, struct csio_lnode *ln)
90419370Spst{
90598944Sobrien	csio_rnode_to_lnode(rn) = ln;
90619370Spst	csio_init_state(&rn->sm, csio_rns_uninit);
90719370Spst	INIT_LIST_HEAD(&rn->host_cmpl_q);
90819370Spst	csio_rn_flowid(rn) = CSIO_INVALID_IDX;
90919370Spst
91019370Spst	/* Add rnode to list of lnodes->rnhead */
91119370Spst	list_add_tail(&rn->sm.sm_list, &ln->rnhead);
91219370Spst
91319370Spst	return 0;
91419370Spst}
91519370Spst
91698944Sobrienstatic void
91798944Sobriencsio_rnode_exit(struct csio_rnode *rn)
91819370Spst{
91919370Spst	list_del_init(&rn->sm.sm_list);
92019370Spst	CSIO_DB_ASSERT(list_empty(&rn->host_cmpl_q));
92119370Spst}
92298944Sobrien