1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
242e9a92fSRobert Love /*
342e9a92fSRobert Love * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
442e9a92fSRobert Love *
542e9a92fSRobert Love * Maintained at www.Open-FCoE.org
642e9a92fSRobert Love */
742e9a92fSRobert Love
842e9a92fSRobert Love /*
942e9a92fSRobert Love * RPORT GENERAL INFO
1042e9a92fSRobert Love *
1142e9a92fSRobert Love * This file contains all processing regarding fc_rports. It contains the
1242e9a92fSRobert Love * rport state machine and does all rport interaction with the transport class.
1342e9a92fSRobert Love * There should be no other places in libfc that interact directly with the
1442e9a92fSRobert Love * transport class in regards to adding and deleting rports.
1542e9a92fSRobert Love *
1642e9a92fSRobert Love * fc_rport's represent N_Port's within the fabric.
1742e9a92fSRobert Love */
1842e9a92fSRobert Love
1942e9a92fSRobert Love /*
2042e9a92fSRobert Love * RPORT LOCKING
2142e9a92fSRobert Love *
2242e9a92fSRobert Love * The rport should never hold the rport mutex and then attempt to acquire
2342e9a92fSRobert Love * either the lport or disc mutexes. The rport's mutex is considered lesser
2442e9a92fSRobert Love * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for
25732bee7aSUwe Kleine-König * more comments on the hierarchy.
2642e9a92fSRobert Love *
2742e9a92fSRobert Love * The locking strategy is similar to the lport's strategy. The lock protects
2842e9a92fSRobert Love * the rport's states and is held and released by the entry points to the rport
2942e9a92fSRobert Love * block. All _enter_* functions correspond to rport states and expect the rport
3042e9a92fSRobert Love * mutex to be locked before calling them. This means that rports only handle
3142e9a92fSRobert Love * one request or response at a time, since they're not critical for the I/O
3242e9a92fSRobert Love * path this potential over-use of the mutex is acceptable.
3342e9a92fSRobert Love */
3442e9a92fSRobert Love
354d2095ccSHannes Reinecke /*
364d2095ccSHannes Reinecke * RPORT REFERENCE COUNTING
374d2095ccSHannes Reinecke *
384d2095ccSHannes Reinecke * A rport reference should be taken when:
394d2095ccSHannes Reinecke * - an rport is allocated
404d2095ccSHannes Reinecke * - a workqueue item is scheduled
414d2095ccSHannes Reinecke * - an ELS request is send
424d2095ccSHannes Reinecke * The reference should be dropped when:
434d2095ccSHannes Reinecke * - the workqueue function has finished
444d2095ccSHannes Reinecke * - the ELS response is handled
454d2095ccSHannes Reinecke * - an rport is removed
464d2095ccSHannes Reinecke */
474d2095ccSHannes Reinecke
4842e9a92fSRobert Love #include <linux/kernel.h>
4942e9a92fSRobert Love #include <linux/spinlock.h>
5042e9a92fSRobert Love #include <linux/interrupt.h>
515a0e3ad6STejun Heo #include <linux/slab.h>
5242e9a92fSRobert Love #include <linux/rcupdate.h>
5342e9a92fSRobert Love #include <linux/timer.h>
5442e9a92fSRobert Love #include <linux/workqueue.h>
5509703660SPaul Gortmaker #include <linux/export.h>
56b2d09103SIngo Molnar #include <linux/rculist.h>
57b2d09103SIngo Molnar
5842e9a92fSRobert Love #include <asm/unaligned.h>
5942e9a92fSRobert Love
6042e9a92fSRobert Love #include <scsi/libfc.h>
6142e9a92fSRobert Love
62e31ac898SArnd Bergmann #include "fc_encode.h"
638866a5d9SRobert Love #include "fc_libfc.h"
648866a5d9SRobert Love
6555204909SRandy Dunlap static struct workqueue_struct *rport_event_queue;
6642e9a92fSRobert Love
67a7b12a27SJoe Eykholt static void fc_rport_enter_flogi(struct fc_rport_priv *);
689fb9d328SJoe Eykholt static void fc_rport_enter_plogi(struct fc_rport_priv *);
699fb9d328SJoe Eykholt static void fc_rport_enter_prli(struct fc_rport_priv *);
709fb9d328SJoe Eykholt static void fc_rport_enter_rtv(struct fc_rport_priv *);
719fb9d328SJoe Eykholt static void fc_rport_enter_ready(struct fc_rport_priv *);
729fb9d328SJoe Eykholt static void fc_rport_enter_logo(struct fc_rport_priv *);
73370c3bd0SJoe Eykholt static void fc_rport_enter_adisc(struct fc_rport_priv *);
7442e9a92fSRobert Love
7592261156SJoe Eykholt static void fc_rport_recv_plogi_req(struct fc_lport *, struct fc_frame *);
7692261156SJoe Eykholt static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *);
7792261156SJoe Eykholt static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *);
7892261156SJoe Eykholt static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *);
7942e9a92fSRobert Love static void fc_rport_timeout(struct work_struct *);
809f9504a7SHannes Reinecke static void fc_rport_error(struct fc_rport_priv *, int);
819f9504a7SHannes Reinecke static void fc_rport_error_retry(struct fc_rport_priv *, int);
8242e9a92fSRobert Love static void fc_rport_work(struct work_struct *);
8342e9a92fSRobert Love
8442e9a92fSRobert Love static const char *fc_rport_state_names[] = {
8542e9a92fSRobert Love [RPORT_ST_INIT] = "Init",
86a7b12a27SJoe Eykholt [RPORT_ST_FLOGI] = "FLOGI",
87a7b12a27SJoe Eykholt [RPORT_ST_PLOGI_WAIT] = "PLOGI_WAIT",
8842e9a92fSRobert Love [RPORT_ST_PLOGI] = "PLOGI",
8942e9a92fSRobert Love [RPORT_ST_PRLI] = "PRLI",
9042e9a92fSRobert Love [RPORT_ST_RTV] = "RTV",
9142e9a92fSRobert Love [RPORT_ST_READY] = "Ready",
92370c3bd0SJoe Eykholt [RPORT_ST_ADISC] = "ADISC",
9314194054SJoe Eykholt [RPORT_ST_DELETE] = "Delete",
9442e9a92fSRobert Love };
9542e9a92fSRobert Love
969e9d0452SJoe Eykholt /**
973a3b42bfSRobert Love * fc_rport_lookup() - Lookup a remote port by port_id
983a3b42bfSRobert Love * @lport: The local port to lookup the remote port on
993a3b42bfSRobert Love * @port_id: The remote port ID to look up
10042e90414SJoe Eykholt *
101baa6719fSHannes Reinecke * The reference count of the fc_rport_priv structure is
102baa6719fSHannes Reinecke * increased by one.
1038025b5dbSJoe Eykholt */
fc_rport_lookup(const struct fc_lport * lport,u32 port_id)104e87b7777SHannes Reinecke struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
1058025b5dbSJoe Eykholt u32 port_id)
1068025b5dbSJoe Eykholt {
107baa6719fSHannes Reinecke struct fc_rport_priv *rdata = NULL, *tmp_rdata;
1088025b5dbSJoe Eykholt
109baa6719fSHannes Reinecke rcu_read_lock();
110baa6719fSHannes Reinecke list_for_each_entry_rcu(tmp_rdata, &lport->disc.rports, peers)
111baa6719fSHannes Reinecke if (tmp_rdata->ids.port_id == port_id &&
112baa6719fSHannes Reinecke kref_get_unless_zero(&tmp_rdata->kref)) {
113baa6719fSHannes Reinecke rdata = tmp_rdata;
114baa6719fSHannes Reinecke break;
115baa6719fSHannes Reinecke }
116baa6719fSHannes Reinecke rcu_read_unlock();
1178025b5dbSJoe Eykholt return rdata;
1188025b5dbSJoe Eykholt }
119e87b7777SHannes Reinecke EXPORT_SYMBOL(fc_rport_lookup);
1208025b5dbSJoe Eykholt
1218025b5dbSJoe Eykholt /**
1229737e6a7SRobert Love * fc_rport_create() - Create a new remote port
1233a3b42bfSRobert Love * @lport: The local port this remote port will be associated with
124f636acaeSLee Jones * @port_id: The identifiers for the new remote port
1253a3b42bfSRobert Love *
1263a3b42bfSRobert Love * The remote port will start in the INIT state.
1279e9d0452SJoe Eykholt */
fc_rport_create(struct fc_lport * lport,u32 port_id)1282580064bSHannes Reinecke struct fc_rport_priv *fc_rport_create(struct fc_lport *lport, u32 port_id)
12942e9a92fSRobert Love {
130ab28f1fdSJoe Eykholt struct fc_rport_priv *rdata;
131023358b1SHannes Reinecke size_t rport_priv_size = sizeof(*rdata);
13242e9a92fSRobert Love
133ee35624eSHannes Reinecke lockdep_assert_held(&lport->disc.disc_mutex);
134ee35624eSHannes Reinecke
135e87b7777SHannes Reinecke rdata = fc_rport_lookup(lport, port_id);
13671f2bf85SJaved Hasan if (rdata) {
13771f2bf85SJaved Hasan kref_put(&rdata->kref, fc_rport_destroy);
13819f97e3cSJoe Eykholt return rdata;
13971f2bf85SJaved Hasan }
14019f97e3cSJoe Eykholt
141023358b1SHannes Reinecke if (lport->rport_priv_size > 0)
142023358b1SHannes Reinecke rport_priv_size = lport->rport_priv_size;
143023358b1SHannes Reinecke rdata = kzalloc(rport_priv_size, GFP_KERNEL);
1449e9d0452SJoe Eykholt if (!rdata)
14542e9a92fSRobert Love return NULL;
14642e9a92fSRobert Love
1479737e6a7SRobert Love rdata->ids.node_name = -1;
1489737e6a7SRobert Love rdata->ids.port_name = -1;
1499737e6a7SRobert Love rdata->ids.port_id = port_id;
1509737e6a7SRobert Love rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
1519737e6a7SRobert Love
152f211fa51SJoe Eykholt kref_init(&rdata->kref);
15342e9a92fSRobert Love mutex_init(&rdata->rp_mutex);
154795d86f5SJoe Eykholt rdata->local_port = lport;
15542e9a92fSRobert Love rdata->rp_state = RPORT_ST_INIT;
15642e9a92fSRobert Love rdata->event = RPORT_EV_NONE;
15742e9a92fSRobert Love rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
158795d86f5SJoe Eykholt rdata->e_d_tov = lport->e_d_tov;
159795d86f5SJoe Eykholt rdata->r_a_tov = lport->r_a_tov;
160f211fa51SJoe Eykholt rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
16142e9a92fSRobert Love INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
16242e9a92fSRobert Love INIT_WORK(&rdata->event_work, fc_rport_work);
16375a2792dSBhanu Prakash Gollapudi if (port_id != FC_FID_DIR_SERV) {
16475a2792dSBhanu Prakash Gollapudi rdata->lld_event_callback = lport->tt.rport_event_callback;
16542e90414SJoe Eykholt list_add_rcu(&rdata->peers, &lport->disc.rports);
16675a2792dSBhanu Prakash Gollapudi }
1679fb9d328SJoe Eykholt return rdata;
16842e9a92fSRobert Love }
1692580064bSHannes Reinecke EXPORT_SYMBOL(fc_rport_create);
17042e9a92fSRobert Love
17142e9a92fSRobert Love /**
1723a3b42bfSRobert Love * fc_rport_destroy() - Free a remote port after last reference is released
1733a3b42bfSRobert Love * @kref: The remote port's kref
174f211fa51SJoe Eykholt */
fc_rport_destroy(struct kref * kref)175944ef968SHannes Reinecke void fc_rport_destroy(struct kref *kref)
176f211fa51SJoe Eykholt {
177f211fa51SJoe Eykholt struct fc_rport_priv *rdata;
178f211fa51SJoe Eykholt
179f211fa51SJoe Eykholt rdata = container_of(kref, struct fc_rport_priv, kref);
1808497a24aSLai Jiangshan kfree_rcu(rdata, rcu);
181f211fa51SJoe Eykholt }
182944ef968SHannes Reinecke EXPORT_SYMBOL(fc_rport_destroy);
183f211fa51SJoe Eykholt
184f211fa51SJoe Eykholt /**
1853a3b42bfSRobert Love * fc_rport_state() - Return a string identifying the remote port's state
1863a3b42bfSRobert Love * @rdata: The remote port
18742e9a92fSRobert Love */
fc_rport_state(struct fc_rport_priv * rdata)1889fb9d328SJoe Eykholt static const char *fc_rport_state(struct fc_rport_priv *rdata)
18942e9a92fSRobert Love {
19042e9a92fSRobert Love const char *cp;
19142e9a92fSRobert Love
19242e9a92fSRobert Love cp = fc_rport_state_names[rdata->rp_state];
19342e9a92fSRobert Love if (!cp)
19442e9a92fSRobert Love cp = "Unknown";
19542e9a92fSRobert Love return cp;
19642e9a92fSRobert Love }
19742e9a92fSRobert Love
19842e9a92fSRobert Love /**
1993a3b42bfSRobert Love * fc_set_rport_loss_tmo() - Set the remote port loss timeout
2003a3b42bfSRobert Love * @rport: The remote port that gets a new timeout value
2013a3b42bfSRobert Love * @timeout: The new timeout value (in seconds)
20242e9a92fSRobert Love */
fc_set_rport_loss_tmo(struct fc_rport * rport,u32 timeout)20342e9a92fSRobert Love void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
20442e9a92fSRobert Love {
20542e9a92fSRobert Love if (timeout)
20673b43764SMike Christie rport->dev_loss_tmo = timeout;
20742e9a92fSRobert Love else
20873b43764SMike Christie rport->dev_loss_tmo = 1;
20942e9a92fSRobert Love }
21042e9a92fSRobert Love EXPORT_SYMBOL(fc_set_rport_loss_tmo);
21142e9a92fSRobert Love
21242e9a92fSRobert Love /**
2133a3b42bfSRobert Love * fc_plogi_get_maxframe() - Get the maximum payload from the common service
2143a3b42bfSRobert Love * parameters in a FLOGI frame
215a7b12a27SJoe Eykholt * @flp: The FLOGI or PLOGI payload
2163a3b42bfSRobert Love * @maxval: The maximum frame size upper limit; this may be less than what
2173a3b42bfSRobert Love * is in the service parameters
21842e9a92fSRobert Love */
fc_plogi_get_maxframe(struct fc_els_flogi * flp,unsigned int maxval)219b2ab99c9SRobert Love static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
220b2ab99c9SRobert Love unsigned int maxval)
22142e9a92fSRobert Love {
22242e9a92fSRobert Love unsigned int mfs;
22342e9a92fSRobert Love
22442e9a92fSRobert Love /*
22542e9a92fSRobert Love * Get max payload from the common service parameters and the
22642e9a92fSRobert Love * class 3 receive data field size.
22742e9a92fSRobert Love */
22842e9a92fSRobert Love mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK;
22942e9a92fSRobert Love if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval)
23042e9a92fSRobert Love maxval = mfs;
23142e9a92fSRobert Love mfs = ntohs(flp->fl_cssp[3 - 1].cp_rdfs);
23242e9a92fSRobert Love if (mfs >= FC_SP_MIN_MAX_PAYLOAD && mfs < maxval)
23342e9a92fSRobert Love maxval = mfs;
23442e9a92fSRobert Love return maxval;
23542e9a92fSRobert Love }
23642e9a92fSRobert Love
23742e9a92fSRobert Love /**
2383a3b42bfSRobert Love * fc_rport_state_enter() - Change the state of a remote port
2393a3b42bfSRobert Love * @rdata: The remote port whose state should change
2403a3b42bfSRobert Love * @new: The new state
24142e9a92fSRobert Love */
fc_rport_state_enter(struct fc_rport_priv * rdata,enum fc_rport_state new)2429fb9d328SJoe Eykholt static void fc_rport_state_enter(struct fc_rport_priv *rdata,
24342e9a92fSRobert Love enum fc_rport_state new)
24442e9a92fSRobert Love {
245ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
246ee35624eSHannes Reinecke
24742e9a92fSRobert Love if (rdata->rp_state != new)
24842e9a92fSRobert Love rdata->retries = 0;
24942e9a92fSRobert Love rdata->rp_state = new;
25042e9a92fSRobert Love }
25142e9a92fSRobert Love
2523a3b42bfSRobert Love /**
2533a3b42bfSRobert Love * fc_rport_work() - Handler for remote port events in the rport_event_queue
2543a3b42bfSRobert Love * @work: Handle to the remote port being dequeued
2554d2095ccSHannes Reinecke *
2564d2095ccSHannes Reinecke * Reference counting: drops kref on return
2573a3b42bfSRobert Love */
fc_rport_work(struct work_struct * work)25842e9a92fSRobert Love static void fc_rport_work(struct work_struct *work)
25942e9a92fSRobert Love {
260571f824cSAbhijeet Joglekar u32 port_id;
261ab28f1fdSJoe Eykholt struct fc_rport_priv *rdata =
262ab28f1fdSJoe Eykholt container_of(work, struct fc_rport_priv, event_work);
2633a3b42bfSRobert Love struct fc_rport_libfc_priv *rpriv;
26442e9a92fSRobert Love enum fc_rport_event event;
26542e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
26642e9a92fSRobert Love struct fc_rport_operations *rport_ops;
267629f4427SJoe Eykholt struct fc_rport_identifiers ids;
268f211fa51SJoe Eykholt struct fc_rport *rport;
26996ad8464SJoe Eykholt struct fc4_prov *prov;
27096ad8464SJoe Eykholt u8 type;
27142e9a92fSRobert Love
27242e9a92fSRobert Love mutex_lock(&rdata->rp_mutex);
27342e9a92fSRobert Love event = rdata->event;
27442e9a92fSRobert Love rport_ops = rdata->ops;
275f211fa51SJoe Eykholt rport = rdata->rport;
27642e9a92fSRobert Love
2779e9d0452SJoe Eykholt FC_RPORT_DBG(rdata, "work event %u\n", event);
2789e9d0452SJoe Eykholt
279629f4427SJoe Eykholt switch (event) {
2804c0f62b5SJoe Eykholt case RPORT_EV_READY:
281f211fa51SJoe Eykholt ids = rdata->ids;
2825f7ea3b7SJoe Eykholt rdata->event = RPORT_EV_NONE;
283f034260dSJoe Eykholt rdata->major_retries = 0;
2849e9d0452SJoe Eykholt kref_get(&rdata->kref);
28542e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
28642e9a92fSRobert Love
28757d3ec7eSHannes Reinecke if (!rport) {
28857d3ec7eSHannes Reinecke FC_RPORT_DBG(rdata, "No rport!\n");
2899e9d0452SJoe Eykholt rport = fc_remote_port_add(lport->host, 0, &ids);
29057d3ec7eSHannes Reinecke }
2919e9d0452SJoe Eykholt if (!rport) {
2929e9d0452SJoe Eykholt FC_RPORT_DBG(rdata, "Failed to add the rport\n");
293c96c792aSHannes Reinecke fc_rport_logoff(rdata);
294944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
2959e9d0452SJoe Eykholt return;
2969e9d0452SJoe Eykholt }
2979e9d0452SJoe Eykholt mutex_lock(&rdata->rp_mutex);
2989e9d0452SJoe Eykholt if (rdata->rport)
2999e9d0452SJoe Eykholt FC_RPORT_DBG(rdata, "rport already allocated\n");
3009e9d0452SJoe Eykholt rdata->rport = rport;
3019e9d0452SJoe Eykholt rport->maxframe_size = rdata->maxframe_size;
3029e9d0452SJoe Eykholt rport->supported_classes = rdata->supported_classes;
3039e9d0452SJoe Eykholt
3043a3b42bfSRobert Love rpriv = rport->dd_data;
3053a3b42bfSRobert Love rpriv->local_port = lport;
3063a3b42bfSRobert Love rpriv->rp_state = rdata->rp_state;
3073a3b42bfSRobert Love rpriv->flags = rdata->flags;
3083a3b42bfSRobert Love rpriv->e_d_tov = rdata->e_d_tov;
3093a3b42bfSRobert Love rpriv->r_a_tov = rdata->r_a_tov;
3109e9d0452SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
3119e9d0452SJoe Eykholt
3128345592bSJoe Eykholt if (rport_ops && rport_ops->event_callback) {
3139e9d0452SJoe Eykholt FC_RPORT_DBG(rdata, "callback ev %d\n", event);
3149fb9d328SJoe Eykholt rport_ops->event_callback(lport, rdata, event);
3159e9d0452SJoe Eykholt }
31675a2792dSBhanu Prakash Gollapudi if (rdata->lld_event_callback) {
31775a2792dSBhanu Prakash Gollapudi FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
31875a2792dSBhanu Prakash Gollapudi rdata->lld_event_callback(lport, rdata, event);
31975a2792dSBhanu Prakash Gollapudi }
320944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
321629f4427SJoe Eykholt break;
322629f4427SJoe Eykholt
323629f4427SJoe Eykholt case RPORT_EV_FAILED:
324629f4427SJoe Eykholt case RPORT_EV_LOGO:
325629f4427SJoe Eykholt case RPORT_EV_STOP:
32696ad8464SJoe Eykholt if (rdata->prli_count) {
32796ad8464SJoe Eykholt mutex_lock(&fc_prov_mutex);
32896ad8464SJoe Eykholt for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
32996ad8464SJoe Eykholt prov = fc_passive_prov[type];
33096ad8464SJoe Eykholt if (prov && prov->prlo)
33196ad8464SJoe Eykholt prov->prlo(rdata);
33296ad8464SJoe Eykholt }
33396ad8464SJoe Eykholt mutex_unlock(&fc_prov_mutex);
33496ad8464SJoe Eykholt }
3359e9d0452SJoe Eykholt port_id = rdata->ids.port_id;
33642e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
3379e9d0452SJoe Eykholt
3388345592bSJoe Eykholt if (rport_ops && rport_ops->event_callback) {
3399e9d0452SJoe Eykholt FC_RPORT_DBG(rdata, "callback ev %d\n", event);
3409fb9d328SJoe Eykholt rport_ops->event_callback(lport, rdata, event);
3419e9d0452SJoe Eykholt }
34275a2792dSBhanu Prakash Gollapudi if (rdata->lld_event_callback) {
34375a2792dSBhanu Prakash Gollapudi FC_RPORT_DBG(rdata, "lld callback ev %d\n", event);
34475a2792dSBhanu Prakash Gollapudi rdata->lld_event_callback(lport, rdata, event);
34575a2792dSBhanu Prakash Gollapudi }
3464d2095ccSHannes Reinecke if (cancel_delayed_work_sync(&rdata->retry_work))
347944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
3489e9d0452SJoe Eykholt
3499e9d0452SJoe Eykholt /*
3509e9d0452SJoe Eykholt * Reset any outstanding exchanges before freeing rport.
3519e9d0452SJoe Eykholt */
352571f824cSAbhijeet Joglekar lport->tt.exch_mgr_reset(lport, 0, port_id);
353571f824cSAbhijeet Joglekar lport->tt.exch_mgr_reset(lport, port_id, 0);
3549e9d0452SJoe Eykholt
3559e9d0452SJoe Eykholt if (rport) {
3563a3b42bfSRobert Love rpriv = rport->dd_data;
3573a3b42bfSRobert Love rpriv->rp_state = RPORT_ST_DELETE;
3589e9d0452SJoe Eykholt mutex_lock(&rdata->rp_mutex);
3599e9d0452SJoe Eykholt rdata->rport = NULL;
3609e9d0452SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
3619e9d0452SJoe Eykholt fc_remote_port_delete(rport);
362571f824cSAbhijeet Joglekar }
3634b2164d4SJoe Eykholt
364b4a9c7edSJoe Eykholt mutex_lock(&rdata->rp_mutex);
3654b2164d4SJoe Eykholt if (rdata->rp_state == RPORT_ST_DELETE) {
3664b2164d4SJoe Eykholt if (port_id == FC_FID_DIR_SERV) {
3674b2164d4SJoe Eykholt rdata->event = RPORT_EV_NONE;
3684b2164d4SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
369944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
370f034260dSJoe Eykholt } else if ((rdata->flags & FC_RP_STARTED) &&
371f034260dSJoe Eykholt rdata->major_retries <
372f034260dSJoe Eykholt lport->max_rport_retry_count) {
373f034260dSJoe Eykholt rdata->major_retries++;
3744b2164d4SJoe Eykholt rdata->event = RPORT_EV_NONE;
375b4a9c7edSJoe Eykholt FC_RPORT_DBG(rdata, "work restart\n");
376a7b12a27SJoe Eykholt fc_rport_enter_flogi(rdata);
377b4a9c7edSJoe Eykholt mutex_unlock(&rdata->rp_mutex);
3784b2164d4SJoe Eykholt } else {
379d1b3f51eSSatish Kharat mutex_unlock(&rdata->rp_mutex);
3804b2164d4SJoe Eykholt FC_RPORT_DBG(rdata, "work delete\n");
381a407c593SHannes Reinecke mutex_lock(&lport->disc.disc_mutex);
38242e90414SJoe Eykholt list_del_rcu(&rdata->peers);
383a407c593SHannes Reinecke mutex_unlock(&lport->disc.disc_mutex);
384944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
3854b2164d4SJoe Eykholt }
3864b2164d4SJoe Eykholt } else {
3874b2164d4SJoe Eykholt /*
3884b2164d4SJoe Eykholt * Re-open for events. Reissue READY event if ready.
3894b2164d4SJoe Eykholt */
3904b2164d4SJoe Eykholt rdata->event = RPORT_EV_NONE;
39157d3ec7eSHannes Reinecke if (rdata->rp_state == RPORT_ST_READY) {
39257d3ec7eSHannes Reinecke FC_RPORT_DBG(rdata, "work reopen\n");
3934b2164d4SJoe Eykholt fc_rport_enter_ready(rdata);
39457d3ec7eSHannes Reinecke }
3954b2164d4SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
3964b2164d4SJoe Eykholt }
397629f4427SJoe Eykholt break;
398629f4427SJoe Eykholt
399629f4427SJoe Eykholt default:
40042e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
401629f4427SJoe Eykholt break;
402629f4427SJoe Eykholt }
403944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
40442e9a92fSRobert Love }
40542e9a92fSRobert Love
40642e9a92fSRobert Love /**
40734f42a07SRobert Love * fc_rport_login() - Start the remote port login state machine
4083a3b42bfSRobert Love * @rdata: The remote port to be logged in to
40942e9a92fSRobert Love *
41005d7d3b0SHannes Reinecke * Initiates the RP state machine. It is called from the LP module.
41105d7d3b0SHannes Reinecke * This function will issue the following commands to the N_Port
41205d7d3b0SHannes Reinecke * identified by the FC ID provided.
41305d7d3b0SHannes Reinecke *
41405d7d3b0SHannes Reinecke * - PLOGI
41505d7d3b0SHannes Reinecke * - PRLI
41605d7d3b0SHannes Reinecke * - RTV
41705d7d3b0SHannes Reinecke *
41842e9a92fSRobert Love * Locking Note: Called without the rport lock held. This
41942e9a92fSRobert Love * function will hold the rport lock, call an _enter_*
42042e9a92fSRobert Love * function and then unlock the rport.
421370c3bd0SJoe Eykholt *
422370c3bd0SJoe Eykholt * This indicates the intent to be logged into the remote port.
423370c3bd0SJoe Eykholt * If it appears we are already logged in, ADISC is used to verify
424370c3bd0SJoe Eykholt * the setup.
42542e9a92fSRobert Love */
fc_rport_login(struct fc_rport_priv * rdata)42605d7d3b0SHannes Reinecke int fc_rport_login(struct fc_rport_priv *rdata)
42742e9a92fSRobert Love {
42842e9a92fSRobert Love mutex_lock(&rdata->rp_mutex);
42942e9a92fSRobert Love
43006ee2571SHannes Reinecke if (rdata->flags & FC_RP_STARTED) {
43106ee2571SHannes Reinecke FC_RPORT_DBG(rdata, "port already started\n");
43206ee2571SHannes Reinecke mutex_unlock(&rdata->rp_mutex);
43306ee2571SHannes Reinecke return 0;
43406ee2571SHannes Reinecke }
43506ee2571SHannes Reinecke
4364b2164d4SJoe Eykholt rdata->flags |= FC_RP_STARTED;
437370c3bd0SJoe Eykholt switch (rdata->rp_state) {
438370c3bd0SJoe Eykholt case RPORT_ST_READY:
439370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "ADISC port\n");
440370c3bd0SJoe Eykholt fc_rport_enter_adisc(rdata);
441370c3bd0SJoe Eykholt break;
442b4a9c7edSJoe Eykholt case RPORT_ST_DELETE:
443b4a9c7edSJoe Eykholt FC_RPORT_DBG(rdata, "Restart deleted port\n");
444b4a9c7edSJoe Eykholt break;
445e5a20009SHannes Reinecke case RPORT_ST_INIT:
4469fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Login to port\n");
447a7b12a27SJoe Eykholt fc_rport_enter_flogi(rdata);
448370c3bd0SJoe Eykholt break;
449e5a20009SHannes Reinecke default:
450e5a20009SHannes Reinecke FC_RPORT_DBG(rdata, "Login in progress, state %s\n",
451e5a20009SHannes Reinecke fc_rport_state(rdata));
452e5a20009SHannes Reinecke break;
453370c3bd0SJoe Eykholt }
45442e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
45542e9a92fSRobert Love
45642e9a92fSRobert Love return 0;
45742e9a92fSRobert Love }
45805d7d3b0SHannes Reinecke EXPORT_SYMBOL(fc_rport_login);
45942e9a92fSRobert Love
46042e9a92fSRobert Love /**
4613a3b42bfSRobert Love * fc_rport_enter_delete() - Schedule a remote port to be deleted
4623a3b42bfSRobert Love * @rdata: The remote port to be deleted
4633a3b42bfSRobert Love * @event: The event to report as the reason for deletion
4645f7ea3b7SJoe Eykholt *
4655f7ea3b7SJoe Eykholt * Allow state change into DELETE only once.
4665f7ea3b7SJoe Eykholt *
4675f7ea3b7SJoe Eykholt * Call queue_work only if there's no event already pending.
4685f7ea3b7SJoe Eykholt * Set the new event so that the old pending event will not occur.
4695f7ea3b7SJoe Eykholt * Since we have the mutex, even if fc_rport_work() is already started,
4705f7ea3b7SJoe Eykholt * it'll see the new event.
4714d2095ccSHannes Reinecke *
4724d2095ccSHannes Reinecke * Reference counting: does not modify kref
4735f7ea3b7SJoe Eykholt */
fc_rport_enter_delete(struct fc_rport_priv * rdata,enum fc_rport_event event)4749fb9d328SJoe Eykholt static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
4755f7ea3b7SJoe Eykholt enum fc_rport_event event)
4765f7ea3b7SJoe Eykholt {
477ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
478ee35624eSHannes Reinecke
4795f7ea3b7SJoe Eykholt if (rdata->rp_state == RPORT_ST_DELETE)
4805f7ea3b7SJoe Eykholt return;
4815f7ea3b7SJoe Eykholt
4829fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Delete port\n");
4835f7ea3b7SJoe Eykholt
4849fb9d328SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_DELETE);
4855f7ea3b7SJoe Eykholt
486823a6540SJaved Hasan if (rdata->event == RPORT_EV_NONE) {
4874d2095ccSHannes Reinecke kref_get(&rdata->kref);
488823a6540SJaved Hasan if (!queue_work(rport_event_queue, &rdata->event_work))
489944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
490823a6540SJaved Hasan }
4914d2095ccSHannes Reinecke
4925f7ea3b7SJoe Eykholt rdata->event = event;
4935f7ea3b7SJoe Eykholt }
4945f7ea3b7SJoe Eykholt
4955f7ea3b7SJoe Eykholt /**
4963a3b42bfSRobert Love * fc_rport_logoff() - Logoff and remove a remote port
4973a3b42bfSRobert Love * @rdata: The remote port to be logged off of
49842e9a92fSRobert Love *
49942e9a92fSRobert Love * Locking Note: Called without the rport lock held. This
50042e9a92fSRobert Love * function will hold the rport lock, call an _enter_*
50142e9a92fSRobert Love * function and then unlock the rport.
50242e9a92fSRobert Love */
fc_rport_logoff(struct fc_rport_priv * rdata)503c96c792aSHannes Reinecke int fc_rport_logoff(struct fc_rport_priv *rdata)
50442e9a92fSRobert Love {
505649eb869SHannes Reinecke struct fc_lport *lport = rdata->local_port;
506649eb869SHannes Reinecke u32 port_id = rdata->ids.port_id;
507649eb869SHannes Reinecke
50842e9a92fSRobert Love mutex_lock(&rdata->rp_mutex);
50942e9a92fSRobert Love
5109fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Remove port\n");
51142e9a92fSRobert Love
5124b2164d4SJoe Eykholt rdata->flags &= ~FC_RP_STARTED;
51314194054SJoe Eykholt if (rdata->rp_state == RPORT_ST_DELETE) {
5149fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
515b4c6f546SAbhijeet Joglekar goto out;
516b4c6f546SAbhijeet Joglekar }
517649eb869SHannes Reinecke /*
518649eb869SHannes Reinecke * FC-LS states:
519649eb869SHannes Reinecke * To explicitly Logout, the initiating Nx_Port shall terminate
520649eb869SHannes Reinecke * other open Sequences that it initiated with the destination
521649eb869SHannes Reinecke * Nx_Port prior to performing Logout.
522649eb869SHannes Reinecke */
523649eb869SHannes Reinecke lport->tt.exch_mgr_reset(lport, 0, port_id);
524649eb869SHannes Reinecke lport->tt.exch_mgr_reset(lport, port_id, 0);
525649eb869SHannes Reinecke
5269fb9d328SJoe Eykholt fc_rport_enter_logo(rdata);
52742e9a92fSRobert Love
52842e9a92fSRobert Love /*
52914194054SJoe Eykholt * Change the state to Delete so that we discard
53042e9a92fSRobert Love * the response.
53142e9a92fSRobert Love */
5329fb9d328SJoe Eykholt fc_rport_enter_delete(rdata, RPORT_EV_STOP);
533b4c6f546SAbhijeet Joglekar out:
534b4a9c7edSJoe Eykholt mutex_unlock(&rdata->rp_mutex);
53542e9a92fSRobert Love return 0;
53642e9a92fSRobert Love }
537c96c792aSHannes Reinecke EXPORT_SYMBOL(fc_rport_logoff);
53842e9a92fSRobert Love
53942e9a92fSRobert Love /**
5403a3b42bfSRobert Love * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state
5413a3b42bfSRobert Love * @rdata: The remote port that is ready
54242e9a92fSRobert Love *
5434d2095ccSHannes Reinecke * Reference counting: schedules workqueue, does not modify kref
54442e9a92fSRobert Love */
fc_rport_enter_ready(struct fc_rport_priv * rdata)5459fb9d328SJoe Eykholt static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
54642e9a92fSRobert Love {
547ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
548ee35624eSHannes Reinecke
5499fb9d328SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_READY);
55042e9a92fSRobert Love
5519fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Port is Ready\n");
55242e9a92fSRobert Love
5534d2095ccSHannes Reinecke kref_get(&rdata->kref);
5544d2095ccSHannes Reinecke if (rdata->event == RPORT_EV_NONE &&
5554d2095ccSHannes Reinecke !queue_work(rport_event_queue, &rdata->event_work))
556944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
5574d2095ccSHannes Reinecke
5584c0f62b5SJoe Eykholt rdata->event = RPORT_EV_READY;
55942e9a92fSRobert Love }
56042e9a92fSRobert Love
56142e9a92fSRobert Love /**
5623a3b42bfSRobert Love * fc_rport_timeout() - Handler for the retry_work timer
5633a3b42bfSRobert Love * @work: Handle to the remote port that has timed out
56442e9a92fSRobert Love *
56542e9a92fSRobert Love * Locking Note: Called without the rport lock held. This
56642e9a92fSRobert Love * function will hold the rport lock, call an _enter_*
56742e9a92fSRobert Love * function and then unlock the rport.
5684d2095ccSHannes Reinecke *
5694d2095ccSHannes Reinecke * Reference counting: Drops kref on return.
57042e9a92fSRobert Love */
fc_rport_timeout(struct work_struct * work)57142e9a92fSRobert Love static void fc_rport_timeout(struct work_struct *work)
57242e9a92fSRobert Love {
573ab28f1fdSJoe Eykholt struct fc_rport_priv *rdata =
574ab28f1fdSJoe Eykholt container_of(work, struct fc_rport_priv, retry_work.work);
57542e9a92fSRobert Love
57642e9a92fSRobert Love mutex_lock(&rdata->rp_mutex);
57757d3ec7eSHannes Reinecke FC_RPORT_DBG(rdata, "Port timeout, state %s\n", fc_rport_state(rdata));
57842e9a92fSRobert Love
57942e9a92fSRobert Love switch (rdata->rp_state) {
580a7b12a27SJoe Eykholt case RPORT_ST_FLOGI:
581a7b12a27SJoe Eykholt fc_rport_enter_flogi(rdata);
582a7b12a27SJoe Eykholt break;
58342e9a92fSRobert Love case RPORT_ST_PLOGI:
5849fb9d328SJoe Eykholt fc_rport_enter_plogi(rdata);
58542e9a92fSRobert Love break;
58642e9a92fSRobert Love case RPORT_ST_PRLI:
5879fb9d328SJoe Eykholt fc_rport_enter_prli(rdata);
58842e9a92fSRobert Love break;
58942e9a92fSRobert Love case RPORT_ST_RTV:
5909fb9d328SJoe Eykholt fc_rport_enter_rtv(rdata);
59142e9a92fSRobert Love break;
592370c3bd0SJoe Eykholt case RPORT_ST_ADISC:
593370c3bd0SJoe Eykholt fc_rport_enter_adisc(rdata);
594370c3bd0SJoe Eykholt break;
595a7b12a27SJoe Eykholt case RPORT_ST_PLOGI_WAIT:
59642e9a92fSRobert Love case RPORT_ST_READY:
59742e9a92fSRobert Love case RPORT_ST_INIT:
59814194054SJoe Eykholt case RPORT_ST_DELETE:
59942e9a92fSRobert Love break;
60042e9a92fSRobert Love }
60142e9a92fSRobert Love
60242e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
603944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
60442e9a92fSRobert Love }
60542e9a92fSRobert Love
60642e9a92fSRobert Love /**
60734f42a07SRobert Love * fc_rport_error() - Error handler, called once retries have been exhausted
6083a3b42bfSRobert Love * @rdata: The remote port the error is happened on
6099f9504a7SHannes Reinecke * @err: The error code
61042e9a92fSRobert Love *
6114d2095ccSHannes Reinecke * Reference counting: does not modify kref
61242e9a92fSRobert Love */
fc_rport_error(struct fc_rport_priv * rdata,int err)6139f9504a7SHannes Reinecke static void fc_rport_error(struct fc_rport_priv *rdata, int err)
61442e9a92fSRobert Love {
615d391966aSHannes Reinecke struct fc_lport *lport = rdata->local_port;
616d391966aSHannes Reinecke
617ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
618ee35624eSHannes Reinecke
6199f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "Error %d in state %s, retries %d\n",
6209f9504a7SHannes Reinecke -err, fc_rport_state(rdata), rdata->retries);
62142e9a92fSRobert Love
62242e9a92fSRobert Love switch (rdata->rp_state) {
623a7b12a27SJoe Eykholt case RPORT_ST_FLOGI:
6244b2164d4SJoe Eykholt rdata->flags &= ~FC_RP_STARTED;
6259fb9d328SJoe Eykholt fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
62642e9a92fSRobert Love break;
627d391966aSHannes Reinecke case RPORT_ST_PLOGI:
628d391966aSHannes Reinecke if (lport->point_to_multipoint) {
629d391966aSHannes Reinecke rdata->flags &= ~FC_RP_STARTED;
630d391966aSHannes Reinecke fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
631d391966aSHannes Reinecke } else
632d391966aSHannes Reinecke fc_rport_enter_logo(rdata);
633d391966aSHannes Reinecke break;
63442e9a92fSRobert Love case RPORT_ST_RTV:
6359fb9d328SJoe Eykholt fc_rport_enter_ready(rdata);
63642e9a92fSRobert Love break;
637370c3bd0SJoe Eykholt case RPORT_ST_PRLI:
638c6e085c8SJaved Hasan fc_rport_enter_plogi(rdata);
639c6e085c8SJaved Hasan break;
640370c3bd0SJoe Eykholt case RPORT_ST_ADISC:
641370c3bd0SJoe Eykholt fc_rport_enter_logo(rdata);
642370c3bd0SJoe Eykholt break;
643a7b12a27SJoe Eykholt case RPORT_ST_PLOGI_WAIT:
64414194054SJoe Eykholt case RPORT_ST_DELETE:
64542e9a92fSRobert Love case RPORT_ST_READY:
64642e9a92fSRobert Love case RPORT_ST_INIT:
64742e9a92fSRobert Love break;
64842e9a92fSRobert Love }
64942e9a92fSRobert Love }
6506755db1cSChris Leech
6516755db1cSChris Leech /**
6523a3b42bfSRobert Love * fc_rport_error_retry() - Handler for remote port state retries
6533a3b42bfSRobert Love * @rdata: The remote port whose state is to be retried
6549f9504a7SHannes Reinecke * @err: The error code
6556755db1cSChris Leech *
6566755db1cSChris Leech * If the error was an exchange timeout retry immediately,
6576755db1cSChris Leech * otherwise wait for E_D_TOV.
6586755db1cSChris Leech *
6594d2095ccSHannes Reinecke * Reference counting: increments kref when scheduling retry_work
6606755db1cSChris Leech */
fc_rport_error_retry(struct fc_rport_priv * rdata,int err)6619f9504a7SHannes Reinecke static void fc_rport_error_retry(struct fc_rport_priv *rdata, int err)
6626755db1cSChris Leech {
663a50cc9ecSHannes Reinecke unsigned long delay = msecs_to_jiffies(rdata->e_d_tov);
6646755db1cSChris Leech
665ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
666ee35624eSHannes Reinecke
6676755db1cSChris Leech /* make sure this isn't an FC_EX_CLOSED error, never retry those */
6689f9504a7SHannes Reinecke if (err == -FC_EX_CLOSED)
66928a4af1eSHillf Danton goto out;
6706755db1cSChris Leech
671a3666955SAbhijeet Joglekar if (rdata->retries < rdata->local_port->max_rport_retry_count) {
6729f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "Error %d in state %s, retrying\n",
6739f9504a7SHannes Reinecke err, fc_rport_state(rdata));
6746755db1cSChris Leech rdata->retries++;
6756755db1cSChris Leech /* no additional delay on exchange timeouts */
6769f9504a7SHannes Reinecke if (err == -FC_EX_TIMEOUT)
6776755db1cSChris Leech delay = 0;
6784d2095ccSHannes Reinecke kref_get(&rdata->kref);
6794d2095ccSHannes Reinecke if (!schedule_delayed_work(&rdata->retry_work, delay))
680944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
6816755db1cSChris Leech return;
68242e9a92fSRobert Love }
6836755db1cSChris Leech
68428a4af1eSHillf Danton out:
6859f9504a7SHannes Reinecke fc_rport_error(rdata, err);
68642e9a92fSRobert Love }
68742e9a92fSRobert Love
68842e9a92fSRobert Love /**
689a7b12a27SJoe Eykholt * fc_rport_login_complete() - Handle parameters and completion of p-mp login.
690a7b12a27SJoe Eykholt * @rdata: The remote port which we logged into or which logged into us.
691a7b12a27SJoe Eykholt * @fp: The FLOGI or PLOGI request or response frame
692a7b12a27SJoe Eykholt *
693a7b12a27SJoe Eykholt * Returns non-zero error if a problem is detected with the frame.
694a7b12a27SJoe Eykholt * Does not free the frame.
695a7b12a27SJoe Eykholt *
696a7b12a27SJoe Eykholt * This is only used in point-to-multipoint mode for FIP currently.
697a7b12a27SJoe Eykholt */
fc_rport_login_complete(struct fc_rport_priv * rdata,struct fc_frame * fp)698a7b12a27SJoe Eykholt static int fc_rport_login_complete(struct fc_rport_priv *rdata,
699a7b12a27SJoe Eykholt struct fc_frame *fp)
700a7b12a27SJoe Eykholt {
701a7b12a27SJoe Eykholt struct fc_lport *lport = rdata->local_port;
702a7b12a27SJoe Eykholt struct fc_els_flogi *flogi;
703a7b12a27SJoe Eykholt unsigned int e_d_tov;
704a7b12a27SJoe Eykholt u16 csp_flags;
705a7b12a27SJoe Eykholt
706a7b12a27SJoe Eykholt flogi = fc_frame_payload_get(fp, sizeof(*flogi));
707a7b12a27SJoe Eykholt if (!flogi)
708a7b12a27SJoe Eykholt return -EINVAL;
709a7b12a27SJoe Eykholt
710a7b12a27SJoe Eykholt csp_flags = ntohs(flogi->fl_csp.sp_features);
711a7b12a27SJoe Eykholt
712a7b12a27SJoe Eykholt if (fc_frame_payload_op(fp) == ELS_FLOGI) {
713a7b12a27SJoe Eykholt if (csp_flags & FC_SP_FT_FPORT) {
714a7b12a27SJoe Eykholt FC_RPORT_DBG(rdata, "Fabric bit set in FLOGI\n");
715a7b12a27SJoe Eykholt return -EINVAL;
716a7b12a27SJoe Eykholt }
717a7b12a27SJoe Eykholt } else {
718a7b12a27SJoe Eykholt
719a7b12a27SJoe Eykholt /*
720a7b12a27SJoe Eykholt * E_D_TOV is not valid on an incoming FLOGI request.
721a7b12a27SJoe Eykholt */
722a7b12a27SJoe Eykholt e_d_tov = ntohl(flogi->fl_csp.sp_e_d_tov);
723a7b12a27SJoe Eykholt if (csp_flags & FC_SP_FT_EDTR)
724a7b12a27SJoe Eykholt e_d_tov /= 1000000;
725a7b12a27SJoe Eykholt if (e_d_tov > rdata->e_d_tov)
726a7b12a27SJoe Eykholt rdata->e_d_tov = e_d_tov;
727a7b12a27SJoe Eykholt }
728a7b12a27SJoe Eykholt rdata->maxframe_size = fc_plogi_get_maxframe(flogi, lport->mfs);
729a7b12a27SJoe Eykholt return 0;
730a7b12a27SJoe Eykholt }
731a7b12a27SJoe Eykholt
732a7b12a27SJoe Eykholt /**
733a7b12a27SJoe Eykholt * fc_rport_flogi_resp() - Handle response to FLOGI request for p-mp mode
734a7b12a27SJoe Eykholt * @sp: The sequence that the FLOGI was on
735a7b12a27SJoe Eykholt * @fp: The FLOGI response frame
736a7b12a27SJoe Eykholt * @rp_arg: The remote port that received the FLOGI response
737a7b12a27SJoe Eykholt */
fc_rport_flogi_resp(struct fc_seq * sp,struct fc_frame * fp,void * rp_arg)738c6b21c93SBart Van Assche static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
739a7b12a27SJoe Eykholt void *rp_arg)
740a7b12a27SJoe Eykholt {
741a7b12a27SJoe Eykholt struct fc_rport_priv *rdata = rp_arg;
742a7b12a27SJoe Eykholt struct fc_lport *lport = rdata->local_port;
743a7b12a27SJoe Eykholt struct fc_els_flogi *flogi;
744a7b12a27SJoe Eykholt unsigned int r_a_tov;
7459f9504a7SHannes Reinecke u8 opcode;
7469f9504a7SHannes Reinecke int err = 0;
747a7b12a27SJoe Eykholt
7489f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "Received a FLOGI %s\n",
7499f9504a7SHannes Reinecke IS_ERR(fp) ? "error" : fc_els_resp_type(fp));
750a7b12a27SJoe Eykholt
751a7b12a27SJoe Eykholt if (fp == ERR_PTR(-FC_EX_CLOSED))
7520e9e3d3bSHillf Danton goto put;
753a7b12a27SJoe Eykholt
754a7b12a27SJoe Eykholt mutex_lock(&rdata->rp_mutex);
755a7b12a27SJoe Eykholt
756a7b12a27SJoe Eykholt if (rdata->rp_state != RPORT_ST_FLOGI) {
757a7b12a27SJoe Eykholt FC_RPORT_DBG(rdata, "Received a FLOGI response, but in state "
758a7b12a27SJoe Eykholt "%s\n", fc_rport_state(rdata));
759a7b12a27SJoe Eykholt if (IS_ERR(fp))
760a7b12a27SJoe Eykholt goto err;
761a7b12a27SJoe Eykholt goto out;
762a7b12a27SJoe Eykholt }
763a7b12a27SJoe Eykholt
764a7b12a27SJoe Eykholt if (IS_ERR(fp)) {
7659f9504a7SHannes Reinecke fc_rport_error(rdata, PTR_ERR(fp));
766a7b12a27SJoe Eykholt goto err;
767a7b12a27SJoe Eykholt }
7689f9504a7SHannes Reinecke opcode = fc_frame_payload_op(fp);
7699f9504a7SHannes Reinecke if (opcode == ELS_LS_RJT) {
7709f9504a7SHannes Reinecke struct fc_els_ls_rjt *rjt;
771a7b12a27SJoe Eykholt
7729f9504a7SHannes Reinecke rjt = fc_frame_payload_get(fp, sizeof(*rjt));
7739f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "FLOGI ELS rejected, reason %x expl %x\n",
7749f9504a7SHannes Reinecke rjt->er_reason, rjt->er_explan);
7759f9504a7SHannes Reinecke err = -FC_EX_ELS_RJT;
776a7b12a27SJoe Eykholt goto bad;
7779f9504a7SHannes Reinecke } else if (opcode != ELS_LS_ACC) {
7789f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "FLOGI ELS invalid opcode %x\n", opcode);
7799f9504a7SHannes Reinecke err = -FC_EX_ELS_RJT;
780a7b12a27SJoe Eykholt goto bad;
7819f9504a7SHannes Reinecke }
7829f9504a7SHannes Reinecke if (fc_rport_login_complete(rdata, fp)) {
7839f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "FLOGI failed, no login\n");
7849f9504a7SHannes Reinecke err = -FC_EX_INV_LOGIN;
7859f9504a7SHannes Reinecke goto bad;
7869f9504a7SHannes Reinecke }
787a7b12a27SJoe Eykholt
788a7b12a27SJoe Eykholt flogi = fc_frame_payload_get(fp, sizeof(*flogi));
789f89b8d67SHannes Reinecke if (!flogi) {
7909f9504a7SHannes Reinecke err = -FC_EX_ALLOC_ERR;
791a7b12a27SJoe Eykholt goto bad;
792f89b8d67SHannes Reinecke }
793a7b12a27SJoe Eykholt r_a_tov = ntohl(flogi->fl_csp.sp_r_a_tov);
794a7b12a27SJoe Eykholt if (r_a_tov > rdata->r_a_tov)
795a7b12a27SJoe Eykholt rdata->r_a_tov = r_a_tov;
796a7b12a27SJoe Eykholt
797a7b12a27SJoe Eykholt if (rdata->ids.port_name < lport->wwpn)
798a7b12a27SJoe Eykholt fc_rport_enter_plogi(rdata);
799a7b12a27SJoe Eykholt else
800a7b12a27SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
801a7b12a27SJoe Eykholt out:
802a7b12a27SJoe Eykholt fc_frame_free(fp);
803a7b12a27SJoe Eykholt err:
804a7b12a27SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
8050e9e3d3bSHillf Danton put:
806944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
807a7b12a27SJoe Eykholt return;
808a7b12a27SJoe Eykholt bad:
8099f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "Bad FLOGI response\n");
8109f9504a7SHannes Reinecke fc_rport_error_retry(rdata, err);
811a7b12a27SJoe Eykholt goto out;
812a7b12a27SJoe Eykholt }
813a7b12a27SJoe Eykholt
814a7b12a27SJoe Eykholt /**
815a7b12a27SJoe Eykholt * fc_rport_enter_flogi() - Send a FLOGI request to the remote port for p-mp
816a7b12a27SJoe Eykholt * @rdata: The remote port to send a FLOGI to
817a7b12a27SJoe Eykholt *
8184d2095ccSHannes Reinecke * Reference counting: increments kref when sending ELS
819a7b12a27SJoe Eykholt */
fc_rport_enter_flogi(struct fc_rport_priv * rdata)820a7b12a27SJoe Eykholt static void fc_rport_enter_flogi(struct fc_rport_priv *rdata)
821a7b12a27SJoe Eykholt {
822a7b12a27SJoe Eykholt struct fc_lport *lport = rdata->local_port;
823a7b12a27SJoe Eykholt struct fc_frame *fp;
824a7b12a27SJoe Eykholt
825ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
826ee35624eSHannes Reinecke
827a7b12a27SJoe Eykholt if (!lport->point_to_multipoint)
828a7b12a27SJoe Eykholt return fc_rport_enter_plogi(rdata);
829a7b12a27SJoe Eykholt
830a7b12a27SJoe Eykholt FC_RPORT_DBG(rdata, "Entered FLOGI state from %s state\n",
831a7b12a27SJoe Eykholt fc_rport_state(rdata));
832a7b12a27SJoe Eykholt
833a7b12a27SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_FLOGI);
834a7b12a27SJoe Eykholt
835a7b12a27SJoe Eykholt fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
836a7b12a27SJoe Eykholt if (!fp)
8379f9504a7SHannes Reinecke return fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
838a7b12a27SJoe Eykholt
8394d2095ccSHannes Reinecke kref_get(&rdata->kref);
840a7b12a27SJoe Eykholt if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_FLOGI,
841a7b12a27SJoe Eykholt fc_rport_flogi_resp, rdata,
8424d2095ccSHannes Reinecke 2 * lport->r_a_tov)) {
8439f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
844944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
8454d2095ccSHannes Reinecke }
846a7b12a27SJoe Eykholt }
847a7b12a27SJoe Eykholt
848a7b12a27SJoe Eykholt /**
849a7b12a27SJoe Eykholt * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode
850a7b12a27SJoe Eykholt * @lport: The local port that received the PLOGI request
851a7b12a27SJoe Eykholt * @rx_fp: The PLOGI request frame
8524d2095ccSHannes Reinecke *
8534d2095ccSHannes Reinecke * Reference counting: drops kref on return
854a7b12a27SJoe Eykholt */
fc_rport_recv_flogi_req(struct fc_lport * lport,struct fc_frame * rx_fp)855a7b12a27SJoe Eykholt static void fc_rport_recv_flogi_req(struct fc_lport *lport,
85692261156SJoe Eykholt struct fc_frame *rx_fp)
857a7b12a27SJoe Eykholt {
858a7b12a27SJoe Eykholt struct fc_els_flogi *flp;
859a7b12a27SJoe Eykholt struct fc_rport_priv *rdata;
860a7b12a27SJoe Eykholt struct fc_frame *fp = rx_fp;
861a7b12a27SJoe Eykholt struct fc_seq_els_data rjt_data;
86224f089e2SJoe Eykholt u32 sid;
863a7b12a27SJoe Eykholt
864251748a9SJoe Eykholt sid = fc_frame_sid(fp);
865a7b12a27SJoe Eykholt
866a7b12a27SJoe Eykholt FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n");
867a7b12a27SJoe Eykholt
868a7b12a27SJoe Eykholt if (!lport->point_to_multipoint) {
869a7b12a27SJoe Eykholt rjt_data.reason = ELS_RJT_UNSUP;
870a7b12a27SJoe Eykholt rjt_data.explan = ELS_EXPL_NONE;
871a7b12a27SJoe Eykholt goto reject;
872a7b12a27SJoe Eykholt }
873a7b12a27SJoe Eykholt
874a7b12a27SJoe Eykholt flp = fc_frame_payload_get(fp, sizeof(*flp));
875a7b12a27SJoe Eykholt if (!flp) {
876a7b12a27SJoe Eykholt rjt_data.reason = ELS_RJT_LOGIC;
877a7b12a27SJoe Eykholt rjt_data.explan = ELS_EXPL_INV_LEN;
878a7b12a27SJoe Eykholt goto reject;
879a7b12a27SJoe Eykholt }
880a7b12a27SJoe Eykholt
881e87b7777SHannes Reinecke rdata = fc_rport_lookup(lport, sid);
882a7b12a27SJoe Eykholt if (!rdata) {
883a7b12a27SJoe Eykholt rjt_data.reason = ELS_RJT_FIP;
884a7b12a27SJoe Eykholt rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
885a7b12a27SJoe Eykholt goto reject;
886a7b12a27SJoe Eykholt }
887a7b12a27SJoe Eykholt mutex_lock(&rdata->rp_mutex);
888a7b12a27SJoe Eykholt
889a7b12a27SJoe Eykholt FC_RPORT_DBG(rdata, "Received FLOGI in %s state\n",
890a7b12a27SJoe Eykholt fc_rport_state(rdata));
891a7b12a27SJoe Eykholt
892a7b12a27SJoe Eykholt switch (rdata->rp_state) {
893a7b12a27SJoe Eykholt case RPORT_ST_INIT:
89448058481SKiran Patil /*
89548058481SKiran Patil * If received the FLOGI request on RPORT which is INIT state
89648058481SKiran Patil * (means not transition to FLOGI either fc_rport timeout
89748058481SKiran Patil * function didn;t trigger or this end hasn;t received
89848058481SKiran Patil * beacon yet from other end. In that case only, allow RPORT
89948058481SKiran Patil * state machine to continue, otherwise fall through which
90048058481SKiran Patil * causes the code to send reject response.
90148058481SKiran Patil * NOTE; Not checking for FIP->state such as VNMP_UP or
90248058481SKiran Patil * VNMP_CLAIM because if FIP state is not one of those,
90348058481SKiran Patil * RPORT wouldn;t have created and 'rport_lookup' would have
90448058481SKiran Patil * failed anyway in that case.
90548058481SKiran Patil */
90648058481SKiran Patil break;
907a7b12a27SJoe Eykholt case RPORT_ST_DELETE:
908a7b12a27SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
909a7b12a27SJoe Eykholt rjt_data.reason = ELS_RJT_FIP;
910a7b12a27SJoe Eykholt rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
911baa6719fSHannes Reinecke goto reject_put;
912a7b12a27SJoe Eykholt case RPORT_ST_FLOGI:
913a7b12a27SJoe Eykholt case RPORT_ST_PLOGI_WAIT:
914a7b12a27SJoe Eykholt case RPORT_ST_PLOGI:
915a7b12a27SJoe Eykholt break;
916a7b12a27SJoe Eykholt case RPORT_ST_PRLI:
917a7b12a27SJoe Eykholt case RPORT_ST_RTV:
918a7b12a27SJoe Eykholt case RPORT_ST_READY:
919a7b12a27SJoe Eykholt case RPORT_ST_ADISC:
920a7b12a27SJoe Eykholt /*
921a7b12a27SJoe Eykholt * Set the remote port to be deleted and to then restart.
922a7b12a27SJoe Eykholt * This queues work to be sure exchanges are reset.
923a7b12a27SJoe Eykholt */
924a7b12a27SJoe Eykholt fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
925a7b12a27SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
926a7b12a27SJoe Eykholt rjt_data.reason = ELS_RJT_BUSY;
927a7b12a27SJoe Eykholt rjt_data.explan = ELS_EXPL_NONE;
928baa6719fSHannes Reinecke goto reject_put;
929a7b12a27SJoe Eykholt }
930a7b12a27SJoe Eykholt if (fc_rport_login_complete(rdata, fp)) {
931a7b12a27SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
932a7b12a27SJoe Eykholt rjt_data.reason = ELS_RJT_LOGIC;
933a7b12a27SJoe Eykholt rjt_data.explan = ELS_EXPL_NONE;
934baa6719fSHannes Reinecke goto reject_put;
935a7b12a27SJoe Eykholt }
936a7b12a27SJoe Eykholt
937a7b12a27SJoe Eykholt fp = fc_frame_alloc(lport, sizeof(*flp));
938a7b12a27SJoe Eykholt if (!fp)
939a7b12a27SJoe Eykholt goto out;
940a7b12a27SJoe Eykholt
941a7b12a27SJoe Eykholt fc_flogi_fill(lport, fp);
942a7b12a27SJoe Eykholt flp = fc_frame_payload_get(fp, sizeof(*flp));
943a7b12a27SJoe Eykholt flp->fl_cmd = ELS_LS_ACC;
944a7b12a27SJoe Eykholt
94524f089e2SJoe Eykholt fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
94624f089e2SJoe Eykholt lport->tt.frame_send(lport, fp);
947a7b12a27SJoe Eykholt
948f89b8d67SHannes Reinecke /*
949f89b8d67SHannes Reinecke * Do not proceed with the state machine if our
950f89b8d67SHannes Reinecke * FLOGI has crossed with an FLOGI from the
951f89b8d67SHannes Reinecke * remote port; wait for the FLOGI response instead.
952f89b8d67SHannes Reinecke */
953f89b8d67SHannes Reinecke if (rdata->rp_state != RPORT_ST_FLOGI) {
954a7b12a27SJoe Eykholt if (rdata->ids.port_name < lport->wwpn)
955a7b12a27SJoe Eykholt fc_rport_enter_plogi(rdata);
956a7b12a27SJoe Eykholt else
957a7b12a27SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
958f89b8d67SHannes Reinecke }
959a7b12a27SJoe Eykholt out:
960a7b12a27SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
961944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
96224f089e2SJoe Eykholt fc_frame_free(rx_fp);
963a7b12a27SJoe Eykholt return;
964a7b12a27SJoe Eykholt
965baa6719fSHannes Reinecke reject_put:
966944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
967a7b12a27SJoe Eykholt reject:
9687ab24dd1SHannes Reinecke fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
96924f089e2SJoe Eykholt fc_frame_free(rx_fp);
970a7b12a27SJoe Eykholt }
971a7b12a27SJoe Eykholt
972a7b12a27SJoe Eykholt /**
973a7b12a27SJoe Eykholt * fc_rport_plogi_resp() - Handler for ELS PLOGI responses
9743a3b42bfSRobert Love * @sp: The sequence the PLOGI is on
9753a3b42bfSRobert Love * @fp: The PLOGI response frame
9763a3b42bfSRobert Love * @rdata_arg: The remote port that sent the PLOGI response
97742e9a92fSRobert Love *
97842e9a92fSRobert Love * Locking Note: This function will be called without the rport lock
97942e9a92fSRobert Love * held, but it will lock, call an _enter_* function or fc_rport_error
98042e9a92fSRobert Love * and then unlock the rport.
98142e9a92fSRobert Love */
fc_rport_plogi_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)98242e9a92fSRobert Love static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
9839fb9d328SJoe Eykholt void *rdata_arg)
98442e9a92fSRobert Love {
9859fb9d328SJoe Eykholt struct fc_rport_priv *rdata = rdata_arg;
98642e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
987a29e7646SRobert Love struct fc_els_flogi *plp = NULL;
98842e9a92fSRobert Love u16 csp_seq;
98942e9a92fSRobert Love u16 cssp_seq;
99042e9a92fSRobert Love u8 op;
99142e9a92fSRobert Love
992f657d299SJoe Eykholt FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
99342e9a92fSRobert Love
994785141c6SChad Dupuis if (fp == ERR_PTR(-FC_EX_CLOSED))
995785141c6SChad Dupuis goto put;
996785141c6SChad Dupuis
997785141c6SChad Dupuis mutex_lock(&rdata->rp_mutex);
998785141c6SChad Dupuis
99942e9a92fSRobert Love if (rdata->rp_state != RPORT_ST_PLOGI) {
10009fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Received a PLOGI response, but in state "
10019fb9d328SJoe Eykholt "%s\n", fc_rport_state(rdata));
100276f6804eSAbhijeet Joglekar if (IS_ERR(fp))
100376f6804eSAbhijeet Joglekar goto err;
100442e9a92fSRobert Love goto out;
100542e9a92fSRobert Love }
100642e9a92fSRobert Love
100776f6804eSAbhijeet Joglekar if (IS_ERR(fp)) {
10089f9504a7SHannes Reinecke fc_rport_error_retry(rdata, PTR_ERR(fp));
100976f6804eSAbhijeet Joglekar goto err;
101076f6804eSAbhijeet Joglekar }
101176f6804eSAbhijeet Joglekar
101242e9a92fSRobert Love op = fc_frame_payload_op(fp);
101342e9a92fSRobert Love if (op == ELS_LS_ACC &&
101442e9a92fSRobert Love (plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
1015f211fa51SJoe Eykholt rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
1016f211fa51SJoe Eykholt rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
101742e9a92fSRobert Love
101875a2792dSBhanu Prakash Gollapudi /* save plogi response sp_features for further reference */
101975a2792dSBhanu Prakash Gollapudi rdata->sp_features = ntohs(plp->fl_csp.sp_features);
102075a2792dSBhanu Prakash Gollapudi
1021a7b12a27SJoe Eykholt if (lport->point_to_multipoint)
1022a7b12a27SJoe Eykholt fc_rport_login_complete(rdata, fp);
102342e9a92fSRobert Love csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
102442e9a92fSRobert Love cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
102542e9a92fSRobert Love if (cssp_seq < csp_seq)
102642e9a92fSRobert Love csp_seq = cssp_seq;
102742e9a92fSRobert Love rdata->max_seq = csp_seq;
1028f211fa51SJoe Eykholt rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs);
10299fb9d328SJoe Eykholt fc_rport_enter_prli(rdata);
10309f9504a7SHannes Reinecke } else {
10319f9504a7SHannes Reinecke struct fc_els_ls_rjt *rjt;
103242e9a92fSRobert Love
10339f9504a7SHannes Reinecke rjt = fc_frame_payload_get(fp, sizeof(*rjt));
1034aad1271aSThomas Abraham if (!rjt)
1035aad1271aSThomas Abraham FC_RPORT_DBG(rdata, "PLOGI bad response\n");
1036aad1271aSThomas Abraham else
10379f9504a7SHannes Reinecke FC_RPORT_DBG(rdata, "PLOGI ELS rejected, reason %x expl %x\n",
10389f9504a7SHannes Reinecke rjt->er_reason, rjt->er_explan);
10399f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_ELS_RJT);
10409f9504a7SHannes Reinecke }
104142e9a92fSRobert Love out:
104242e9a92fSRobert Love fc_frame_free(fp);
104342e9a92fSRobert Love err:
104442e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
1045785141c6SChad Dupuis put:
1046944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
104742e9a92fSRobert Love }
104842e9a92fSRobert Love
1049e0335f67SMark Rustad static bool
fc_rport_compatible_roles(struct fc_lport * lport,struct fc_rport_priv * rdata)1050e0335f67SMark Rustad fc_rport_compatible_roles(struct fc_lport *lport, struct fc_rport_priv *rdata)
1051e0335f67SMark Rustad {
1052e0335f67SMark Rustad if (rdata->ids.roles == FC_PORT_ROLE_UNKNOWN)
1053e0335f67SMark Rustad return true;
1054e0335f67SMark Rustad if ((rdata->ids.roles & FC_PORT_ROLE_FCP_TARGET) &&
1055e0335f67SMark Rustad (lport->service_params & FCP_SPPF_INIT_FCN))
1056e0335f67SMark Rustad return true;
1057e0335f67SMark Rustad if ((rdata->ids.roles & FC_PORT_ROLE_FCP_INITIATOR) &&
1058e0335f67SMark Rustad (lport->service_params & FCP_SPPF_TARG_FCN))
1059e0335f67SMark Rustad return true;
1060e0335f67SMark Rustad return false;
1061e0335f67SMark Rustad }
1062e0335f67SMark Rustad
106342e9a92fSRobert Love /**
10643a3b42bfSRobert Love * fc_rport_enter_plogi() - Send Port Login (PLOGI) request
10653a3b42bfSRobert Love * @rdata: The remote port to send a PLOGI to
106642e9a92fSRobert Love *
10674d2095ccSHannes Reinecke * Reference counting: increments kref when sending ELS
106842e9a92fSRobert Love */
fc_rport_enter_plogi(struct fc_rport_priv * rdata)10699fb9d328SJoe Eykholt static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
107042e9a92fSRobert Love {
107142e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
107242e9a92fSRobert Love struct fc_frame *fp;
107342e9a92fSRobert Love
1074ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1075ee35624eSHannes Reinecke
1076e0335f67SMark Rustad if (!fc_rport_compatible_roles(lport, rdata)) {
1077e0335f67SMark Rustad FC_RPORT_DBG(rdata, "PLOGI suppressed for incompatible role\n");
1078e0335f67SMark Rustad fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
1079e0335f67SMark Rustad return;
1080e0335f67SMark Rustad }
1081e0335f67SMark Rustad
10829fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",
10839fb9d328SJoe Eykholt fc_rport_state(rdata));
108442e9a92fSRobert Love
10859fb9d328SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_PLOGI);
108642e9a92fSRobert Love
1087f211fa51SJoe Eykholt rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
108842e9a92fSRobert Love fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
108942e9a92fSRobert Love if (!fp) {
1090a7b12a27SJoe Eykholt FC_RPORT_DBG(rdata, "%s frame alloc failed\n", __func__);
10919f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
109242e9a92fSRobert Love return;
109342e9a92fSRobert Love }
109442e9a92fSRobert Love rdata->e_d_tov = lport->e_d_tov;
109542e9a92fSRobert Love
10964d2095ccSHannes Reinecke kref_get(&rdata->kref);
1097f211fa51SJoe Eykholt if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
1098b94f8951SJoe Eykholt fc_rport_plogi_resp, rdata,
10994d2095ccSHannes Reinecke 2 * lport->r_a_tov)) {
11009f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
1101944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
11024d2095ccSHannes Reinecke }
110342e9a92fSRobert Love }
110442e9a92fSRobert Love
110542e9a92fSRobert Love /**
110634f42a07SRobert Love * fc_rport_prli_resp() - Process Login (PRLI) response handler
11073a3b42bfSRobert Love * @sp: The sequence the PRLI response was on
11083a3b42bfSRobert Love * @fp: The PRLI response frame
11093a3b42bfSRobert Love * @rdata_arg: The remote port that sent the PRLI response
111042e9a92fSRobert Love *
111142e9a92fSRobert Love * Locking Note: This function will be called without the rport lock
111242e9a92fSRobert Love * held, but it will lock, call an _enter_* function or fc_rport_error
111342e9a92fSRobert Love * and then unlock the rport.
111442e9a92fSRobert Love */
fc_rport_prli_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)111542e9a92fSRobert Love static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
11169fb9d328SJoe Eykholt void *rdata_arg)
111742e9a92fSRobert Love {
11189fb9d328SJoe Eykholt struct fc_rport_priv *rdata = rdata_arg;
111942e9a92fSRobert Love struct {
112042e9a92fSRobert Love struct fc_els_prli prli;
112142e9a92fSRobert Love struct fc_els_spp spp;
112242e9a92fSRobert Love } *pp;
1123925cedaeSJoe Eykholt struct fc_els_spp temp_spp;
112457d3ec7eSHannes Reinecke struct fc_els_ls_rjt *rjt;
1125925cedaeSJoe Eykholt struct fc4_prov *prov;
112642e9a92fSRobert Love u32 roles = FC_RPORT_ROLE_UNKNOWN;
112742e9a92fSRobert Love u32 fcp_parm = 0;
112842e9a92fSRobert Love u8 op;
1129386b97b4SHannes Reinecke enum fc_els_spp_resp resp_code;
113042e9a92fSRobert Love
1131f657d299SJoe Eykholt FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
113242e9a92fSRobert Love
1133785141c6SChad Dupuis if (fp == ERR_PTR(-FC_EX_CLOSED))
1134785141c6SChad Dupuis goto put;
1135785141c6SChad Dupuis
1136785141c6SChad Dupuis mutex_lock(&rdata->rp_mutex);
1137785141c6SChad Dupuis
113842e9a92fSRobert Love if (rdata->rp_state != RPORT_ST_PRLI) {
11399fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Received a PRLI response, but in state "
11409fb9d328SJoe Eykholt "%s\n", fc_rport_state(rdata));
114176f6804eSAbhijeet Joglekar if (IS_ERR(fp))
114276f6804eSAbhijeet Joglekar goto err;
114342e9a92fSRobert Love goto out;
114442e9a92fSRobert Love }
114542e9a92fSRobert Love
114676f6804eSAbhijeet Joglekar if (IS_ERR(fp)) {
11479f9504a7SHannes Reinecke fc_rport_error_retry(rdata, PTR_ERR(fp));
114876f6804eSAbhijeet Joglekar goto err;
114976f6804eSAbhijeet Joglekar }
115076f6804eSAbhijeet Joglekar
11516bd054cbSRobert Love /* reinitialize remote port roles */
11526bd054cbSRobert Love rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
11536bd054cbSRobert Love
115442e9a92fSRobert Love op = fc_frame_payload_op(fp);
115542e9a92fSRobert Love if (op == ELS_LS_ACC) {
115642e9a92fSRobert Love pp = fc_frame_payload_get(fp, sizeof(*pp));
11570b4aafc3SHannes Reinecke if (!pp) {
11580b4aafc3SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_SEQ_ERR);
1159618461c0SBhanu Prakash Gollapudi goto out;
11600b4aafc3SHannes Reinecke }
1161618461c0SBhanu Prakash Gollapudi
1162618461c0SBhanu Prakash Gollapudi resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK);
1163386b97b4SHannes Reinecke FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x spp_type 0x%x\n",
1164386b97b4SHannes Reinecke pp->spp.spp_flags, pp->spp.spp_type);
1165*b27c4577SJaved Hasan
116675a2792dSBhanu Prakash Gollapudi rdata->spp_type = pp->spp.spp_type;
1167618461c0SBhanu Prakash Gollapudi if (resp_code != FC_SPP_RESP_ACK) {
1168618461c0SBhanu Prakash Gollapudi if (resp_code == FC_SPP_RESP_CONF)
11699f9504a7SHannes Reinecke fc_rport_error(rdata, -FC_EX_SEQ_ERR);
1170618461c0SBhanu Prakash Gollapudi else
11719f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_SEQ_ERR);
1172618461c0SBhanu Prakash Gollapudi goto out;
1173618461c0SBhanu Prakash Gollapudi }
11740b4aafc3SHannes Reinecke if (pp->prli.prli_spp_len < sizeof(pp->spp)) {
11750b4aafc3SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_SEQ_ERR);
1176618461c0SBhanu Prakash Gollapudi goto out;
11770b4aafc3SHannes Reinecke }
1178618461c0SBhanu Prakash Gollapudi
117942e9a92fSRobert Love fcp_parm = ntohl(pp->spp.spp_params);
118042e9a92fSRobert Love if (fcp_parm & FCP_SPPF_RETRY)
118142e9a92fSRobert Love rdata->flags |= FC_RP_FLAGS_RETRY;
118275a2792dSBhanu Prakash Gollapudi if (fcp_parm & FCP_SPPF_CONF_COMPL)
118375a2792dSBhanu Prakash Gollapudi rdata->flags |= FC_RP_FLAGS_CONF_REQ;
118442e9a92fSRobert Love
1185386b97b4SHannes Reinecke /*
1186386b97b4SHannes Reinecke * Call prli provider if we should act as a target
1187386b97b4SHannes Reinecke */
1188*b27c4577SJaved Hasan if (rdata->spp_type < FC_FC4_PROV_SIZE) {
1189386b97b4SHannes Reinecke prov = fc_passive_prov[rdata->spp_type];
1190925cedaeSJoe Eykholt if (prov) {
1191925cedaeSJoe Eykholt memset(&temp_spp, 0, sizeof(temp_spp));
1192925cedaeSJoe Eykholt prov->prli(rdata, pp->prli.prli_spp_len,
1193925cedaeSJoe Eykholt &pp->spp, &temp_spp);
1194925cedaeSJoe Eykholt }
1195*b27c4577SJaved Hasan }
1196386b97b4SHannes Reinecke /*
1197386b97b4SHannes Reinecke * Check if the image pair could be established
1198386b97b4SHannes Reinecke */
1199386b97b4SHannes Reinecke if (rdata->spp_type != FC_TYPE_FCP ||
1200386b97b4SHannes Reinecke !(pp->spp.spp_flags & FC_SPP_EST_IMG_PAIR)) {
1201386b97b4SHannes Reinecke /*
1202386b97b4SHannes Reinecke * Nope; we can't use this port as a target.
1203386b97b4SHannes Reinecke */
1204386b97b4SHannes Reinecke fcp_parm &= ~FCP_SPPF_TARG_FCN;
1205386b97b4SHannes Reinecke }
1206f211fa51SJoe Eykholt rdata->supported_classes = FC_COS_CLASS3;
120742e9a92fSRobert Love if (fcp_parm & FCP_SPPF_INIT_FCN)
120842e9a92fSRobert Love roles |= FC_RPORT_ROLE_FCP_INITIATOR;
120942e9a92fSRobert Love if (fcp_parm & FCP_SPPF_TARG_FCN)
121042e9a92fSRobert Love roles |= FC_RPORT_ROLE_FCP_TARGET;
121142e9a92fSRobert Love
1212f211fa51SJoe Eykholt rdata->ids.roles = roles;
12139fb9d328SJoe Eykholt fc_rport_enter_rtv(rdata);
121442e9a92fSRobert Love
121542e9a92fSRobert Love } else {
121657d3ec7eSHannes Reinecke rjt = fc_frame_payload_get(fp, sizeof(*rjt));
1217aad1271aSThomas Abraham if (!rjt)
1218aad1271aSThomas Abraham FC_RPORT_DBG(rdata, "PRLI bad response\n");
121945e544bfSJaved Hasan else {
122057d3ec7eSHannes Reinecke FC_RPORT_DBG(rdata, "PRLI ELS rejected, reason %x expl %x\n",
122157d3ec7eSHannes Reinecke rjt->er_reason, rjt->er_explan);
122245e544bfSJaved Hasan if (rjt->er_reason == ELS_RJT_UNAB &&
122345e544bfSJaved Hasan rjt->er_explan == ELS_EXPL_PLOGI_REQD) {
122445e544bfSJaved Hasan fc_rport_enter_plogi(rdata);
122545e544bfSJaved Hasan goto out;
122645e544bfSJaved Hasan }
122745e544bfSJaved Hasan }
12289f9504a7SHannes Reinecke fc_rport_error_retry(rdata, FC_EX_ELS_RJT);
122942e9a92fSRobert Love }
123042e9a92fSRobert Love
123142e9a92fSRobert Love out:
123242e9a92fSRobert Love fc_frame_free(fp);
123342e9a92fSRobert Love err:
123442e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
1235785141c6SChad Dupuis put:
1236944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
123742e9a92fSRobert Love }
123842e9a92fSRobert Love
123942e9a92fSRobert Love /**
12403a3b42bfSRobert Love * fc_rport_enter_prli() - Send Process Login (PRLI) request
12413a3b42bfSRobert Love * @rdata: The remote port to send the PRLI request to
124242e9a92fSRobert Love *
12434d2095ccSHannes Reinecke * Reference counting: increments kref when sending ELS
124442e9a92fSRobert Love */
fc_rport_enter_prli(struct fc_rport_priv * rdata)12459fb9d328SJoe Eykholt static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
124642e9a92fSRobert Love {
124742e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
124842e9a92fSRobert Love struct {
124942e9a92fSRobert Love struct fc_els_prli prli;
125042e9a92fSRobert Love struct fc_els_spp spp;
125142e9a92fSRobert Love } *pp;
125242e9a92fSRobert Love struct fc_frame *fp;
1253925cedaeSJoe Eykholt struct fc4_prov *prov;
125442e9a92fSRobert Love
1255ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1256ee35624eSHannes Reinecke
12573ac6f98fSJoe Eykholt /*
12583ac6f98fSJoe Eykholt * If the rport is one of the well known addresses
12593ac6f98fSJoe Eykholt * we skip PRLI and RTV and go straight to READY.
12603ac6f98fSJoe Eykholt */
12613ac6f98fSJoe Eykholt if (rdata->ids.port_id >= FC_FID_DOM_MGR) {
12623ac6f98fSJoe Eykholt fc_rport_enter_ready(rdata);
12633ac6f98fSJoe Eykholt return;
12643ac6f98fSJoe Eykholt }
12653ac6f98fSJoe Eykholt
1266386b97b4SHannes Reinecke /*
1267386b97b4SHannes Reinecke * And if the local port does not support the initiator function
1268386b97b4SHannes Reinecke * there's no need to send a PRLI, either.
1269386b97b4SHannes Reinecke */
1270386b97b4SHannes Reinecke if (!(lport->service_params & FCP_SPPF_INIT_FCN)) {
1271386b97b4SHannes Reinecke fc_rport_enter_ready(rdata);
1272386b97b4SHannes Reinecke return;
1273386b97b4SHannes Reinecke }
1274386b97b4SHannes Reinecke
12759fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n",
12769fb9d328SJoe Eykholt fc_rport_state(rdata));
127742e9a92fSRobert Love
12789fb9d328SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_PRLI);
127942e9a92fSRobert Love
128042e9a92fSRobert Love fp = fc_frame_alloc(lport, sizeof(*pp));
128142e9a92fSRobert Love if (!fp) {
12829f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
128342e9a92fSRobert Love return;
128442e9a92fSRobert Love }
128542e9a92fSRobert Love
1286925cedaeSJoe Eykholt fc_prli_fill(lport, fp);
1287925cedaeSJoe Eykholt
1288925cedaeSJoe Eykholt prov = fc_passive_prov[FC_TYPE_FCP];
1289925cedaeSJoe Eykholt if (prov) {
1290925cedaeSJoe Eykholt pp = fc_frame_payload_get(fp, sizeof(*pp));
1291925cedaeSJoe Eykholt prov->prli(rdata, sizeof(pp->spp), NULL, &pp->spp);
1292925cedaeSJoe Eykholt }
1293925cedaeSJoe Eykholt
1294925cedaeSJoe Eykholt fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rdata->ids.port_id,
1295925cedaeSJoe Eykholt fc_host_port_id(lport->host), FC_TYPE_ELS,
1296925cedaeSJoe Eykholt FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
1297925cedaeSJoe Eykholt
1298f211fa51SJoe Eykholt kref_get(&rdata->kref);
12993afd2d15SHannes Reinecke if (!fc_exch_seq_send(lport, fp, fc_rport_prli_resp,
13004d2095ccSHannes Reinecke NULL, rdata, 2 * lport->r_a_tov)) {
13019f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
1302944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
13034d2095ccSHannes Reinecke }
130442e9a92fSRobert Love }
130542e9a92fSRobert Love
130642e9a92fSRobert Love /**
13077c5a51b8SHannes Reinecke * fc_rport_rtv_resp() - Handler for Request Timeout Value (RTV) responses
13083a3b42bfSRobert Love * @sp: The sequence the RTV was on
13093a3b42bfSRobert Love * @fp: The RTV response frame
13103a3b42bfSRobert Love * @rdata_arg: The remote port that sent the RTV response
131142e9a92fSRobert Love *
131242e9a92fSRobert Love * Many targets don't seem to support this.
131342e9a92fSRobert Love *
131442e9a92fSRobert Love * Locking Note: This function will be called without the rport lock
131542e9a92fSRobert Love * held, but it will lock, call an _enter_* function or fc_rport_error
131642e9a92fSRobert Love * and then unlock the rport.
131742e9a92fSRobert Love */
fc_rport_rtv_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)131842e9a92fSRobert Love static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
13199fb9d328SJoe Eykholt void *rdata_arg)
132042e9a92fSRobert Love {
13219fb9d328SJoe Eykholt struct fc_rport_priv *rdata = rdata_arg;
132242e9a92fSRobert Love u8 op;
132342e9a92fSRobert Love
1324f657d299SJoe Eykholt FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
132542e9a92fSRobert Love
1326785141c6SChad Dupuis if (fp == ERR_PTR(-FC_EX_CLOSED))
1327785141c6SChad Dupuis goto put;
1328785141c6SChad Dupuis
1329785141c6SChad Dupuis mutex_lock(&rdata->rp_mutex);
1330785141c6SChad Dupuis
133142e9a92fSRobert Love if (rdata->rp_state != RPORT_ST_RTV) {
13329fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Received a RTV response, but in state "
13339fb9d328SJoe Eykholt "%s\n", fc_rport_state(rdata));
133476f6804eSAbhijeet Joglekar if (IS_ERR(fp))
133576f6804eSAbhijeet Joglekar goto err;
133642e9a92fSRobert Love goto out;
133742e9a92fSRobert Love }
133842e9a92fSRobert Love
133976f6804eSAbhijeet Joglekar if (IS_ERR(fp)) {
13409f9504a7SHannes Reinecke fc_rport_error(rdata, PTR_ERR(fp));
134176f6804eSAbhijeet Joglekar goto err;
134276f6804eSAbhijeet Joglekar }
134376f6804eSAbhijeet Joglekar
134442e9a92fSRobert Love op = fc_frame_payload_op(fp);
134542e9a92fSRobert Love if (op == ELS_LS_ACC) {
134642e9a92fSRobert Love struct fc_els_rtv_acc *rtv;
134742e9a92fSRobert Love u32 toq;
134842e9a92fSRobert Love u32 tov;
134942e9a92fSRobert Love
135042e9a92fSRobert Love rtv = fc_frame_payload_get(fp, sizeof(*rtv));
135142e9a92fSRobert Love if (rtv) {
135242e9a92fSRobert Love toq = ntohl(rtv->rtv_toq);
135342e9a92fSRobert Love tov = ntohl(rtv->rtv_r_a_tov);
135442e9a92fSRobert Love if (tov == 0)
135542e9a92fSRobert Love tov = 1;
135676e72ad1SHannes Reinecke if (tov > rdata->r_a_tov)
135742e9a92fSRobert Love rdata->r_a_tov = tov;
135842e9a92fSRobert Love tov = ntohl(rtv->rtv_e_d_tov);
135942e9a92fSRobert Love if (toq & FC_ELS_RTV_EDRES)
136042e9a92fSRobert Love tov /= 1000000;
136142e9a92fSRobert Love if (tov == 0)
136242e9a92fSRobert Love tov = 1;
136376e72ad1SHannes Reinecke if (tov > rdata->e_d_tov)
136442e9a92fSRobert Love rdata->e_d_tov = tov;
136542e9a92fSRobert Love }
136642e9a92fSRobert Love }
136742e9a92fSRobert Love
13689fb9d328SJoe Eykholt fc_rport_enter_ready(rdata);
136942e9a92fSRobert Love
137042e9a92fSRobert Love out:
137142e9a92fSRobert Love fc_frame_free(fp);
137242e9a92fSRobert Love err:
137342e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
1374785141c6SChad Dupuis put:
1375944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
137642e9a92fSRobert Love }
137742e9a92fSRobert Love
137842e9a92fSRobert Love /**
13793a3b42bfSRobert Love * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request
13803a3b42bfSRobert Love * @rdata: The remote port to send the RTV request to
138142e9a92fSRobert Love *
13824d2095ccSHannes Reinecke * Reference counting: increments kref when sending ELS
138342e9a92fSRobert Love */
fc_rport_enter_rtv(struct fc_rport_priv * rdata)13849fb9d328SJoe Eykholt static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
138542e9a92fSRobert Love {
138642e9a92fSRobert Love struct fc_frame *fp;
138742e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
138842e9a92fSRobert Love
1389ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1390ee35624eSHannes Reinecke
13919fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Port entered RTV state from %s state\n",
13929fb9d328SJoe Eykholt fc_rport_state(rdata));
139342e9a92fSRobert Love
13949fb9d328SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_RTV);
139542e9a92fSRobert Love
139642e9a92fSRobert Love fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
139742e9a92fSRobert Love if (!fp) {
13989f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
139942e9a92fSRobert Love return;
140042e9a92fSRobert Love }
140142e9a92fSRobert Love
14024d2095ccSHannes Reinecke kref_get(&rdata->kref);
1403f211fa51SJoe Eykholt if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
1404b94f8951SJoe Eykholt fc_rport_rtv_resp, rdata,
14054d2095ccSHannes Reinecke 2 * lport->r_a_tov)) {
14069f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
1407944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
14084d2095ccSHannes Reinecke }
140942e9a92fSRobert Love }
141042e9a92fSRobert Love
141142e9a92fSRobert Love /**
14127c5a51b8SHannes Reinecke * fc_rport_recv_rtv_req() - Handler for Read Timeout Value (RTV) requests
14137c5a51b8SHannes Reinecke * @rdata: The remote port that sent the RTV request
14147c5a51b8SHannes Reinecke * @in_fp: The RTV request frame
14157c5a51b8SHannes Reinecke */
fc_rport_recv_rtv_req(struct fc_rport_priv * rdata,struct fc_frame * in_fp)14167c5a51b8SHannes Reinecke static void fc_rport_recv_rtv_req(struct fc_rport_priv *rdata,
14177c5a51b8SHannes Reinecke struct fc_frame *in_fp)
14187c5a51b8SHannes Reinecke {
14197c5a51b8SHannes Reinecke struct fc_lport *lport = rdata->local_port;
14207c5a51b8SHannes Reinecke struct fc_frame *fp;
14217c5a51b8SHannes Reinecke struct fc_els_rtv_acc *rtv;
14227c5a51b8SHannes Reinecke struct fc_seq_els_data rjt_data;
14237c5a51b8SHannes Reinecke
1424ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1425ee35624eSHannes Reinecke lockdep_assert_held(&lport->lp_mutex);
1426ee35624eSHannes Reinecke
14277c5a51b8SHannes Reinecke FC_RPORT_DBG(rdata, "Received RTV request\n");
14287c5a51b8SHannes Reinecke
14297c5a51b8SHannes Reinecke fp = fc_frame_alloc(lport, sizeof(*rtv));
14307c5a51b8SHannes Reinecke if (!fp) {
14317c5a51b8SHannes Reinecke rjt_data.reason = ELS_RJT_UNAB;
1432dd6e1f71SGustavo A. R. Silva rjt_data.explan = ELS_EXPL_INSUF_RES;
14337ab24dd1SHannes Reinecke fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
14347c5a51b8SHannes Reinecke goto drop;
14357c5a51b8SHannes Reinecke }
14367c5a51b8SHannes Reinecke rtv = fc_frame_payload_get(fp, sizeof(*rtv));
14377c5a51b8SHannes Reinecke rtv->rtv_cmd = ELS_LS_ACC;
14387c5a51b8SHannes Reinecke rtv->rtv_r_a_tov = htonl(lport->r_a_tov);
14397c5a51b8SHannes Reinecke rtv->rtv_e_d_tov = htonl(lport->e_d_tov);
14407c5a51b8SHannes Reinecke rtv->rtv_toq = 0;
14417c5a51b8SHannes Reinecke fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
14427c5a51b8SHannes Reinecke lport->tt.frame_send(lport, fp);
14437c5a51b8SHannes Reinecke drop:
14447c5a51b8SHannes Reinecke fc_frame_free(in_fp);
14457c5a51b8SHannes Reinecke }
14467c5a51b8SHannes Reinecke
14477c5a51b8SHannes Reinecke /**
1448079ecd8cSJoe Eykholt * fc_rport_logo_resp() - Handler for logout (LOGO) responses
1449079ecd8cSJoe Eykholt * @sp: The sequence the LOGO was on
1450079ecd8cSJoe Eykholt * @fp: The LOGO response frame
1451f636acaeSLee Jones * @rdata_arg: The remote port
1452079ecd8cSJoe Eykholt */
fc_rport_logo_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)1453079ecd8cSJoe Eykholt static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
14544d2095ccSHannes Reinecke void *rdata_arg)
1455079ecd8cSJoe Eykholt {
14564d2095ccSHannes Reinecke struct fc_rport_priv *rdata = rdata_arg;
14574d2095ccSHannes Reinecke struct fc_lport *lport = rdata->local_port;
1458079ecd8cSJoe Eykholt
1459079ecd8cSJoe Eykholt FC_RPORT_ID_DBG(lport, fc_seq_exch(sp)->did,
1460079ecd8cSJoe Eykholt "Received a LOGO %s\n", fc_els_resp_type(fp));
14614d2095ccSHannes Reinecke if (!IS_ERR(fp))
1462079ecd8cSJoe Eykholt fc_frame_free(fp);
1463944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
1464079ecd8cSJoe Eykholt }
1465079ecd8cSJoe Eykholt
1466079ecd8cSJoe Eykholt /**
14673a3b42bfSRobert Love * fc_rport_enter_logo() - Send a logout (LOGO) request
14683a3b42bfSRobert Love * @rdata: The remote port to send the LOGO request to
146942e9a92fSRobert Love *
14704d2095ccSHannes Reinecke * Reference counting: increments kref when sending ELS
147142e9a92fSRobert Love */
fc_rport_enter_logo(struct fc_rport_priv * rdata)14729fb9d328SJoe Eykholt static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
147342e9a92fSRobert Love {
147442e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
147542e9a92fSRobert Love struct fc_frame *fp;
147642e9a92fSRobert Love
1477ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1478ee35624eSHannes Reinecke
1479079ecd8cSJoe Eykholt FC_RPORT_DBG(rdata, "Port sending LOGO from %s state\n",
14809fb9d328SJoe Eykholt fc_rport_state(rdata));
148142e9a92fSRobert Love
148242e9a92fSRobert Love fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
1483079ecd8cSJoe Eykholt if (!fp)
148442e9a92fSRobert Love return;
14854d2095ccSHannes Reinecke kref_get(&rdata->kref);
14864d2095ccSHannes Reinecke if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
14874d2095ccSHannes Reinecke fc_rport_logo_resp, rdata, 0))
1488944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
148942e9a92fSRobert Love }
149042e9a92fSRobert Love
149142e9a92fSRobert Love /**
14920dbea7c1SLee Jones * fc_rport_adisc_resp() - Handler for Address Discovery (ADISC) responses
14933a3b42bfSRobert Love * @sp: The sequence the ADISC response was on
14943a3b42bfSRobert Love * @fp: The ADISC response frame
14953a3b42bfSRobert Love * @rdata_arg: The remote port that sent the ADISC response
1496370c3bd0SJoe Eykholt *
1497370c3bd0SJoe Eykholt * Locking Note: This function will be called without the rport lock
1498370c3bd0SJoe Eykholt * held, but it will lock, call an _enter_* function or fc_rport_error
1499370c3bd0SJoe Eykholt * and then unlock the rport.
1500370c3bd0SJoe Eykholt */
fc_rport_adisc_resp(struct fc_seq * sp,struct fc_frame * fp,void * rdata_arg)1501370c3bd0SJoe Eykholt static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
1502370c3bd0SJoe Eykholt void *rdata_arg)
1503370c3bd0SJoe Eykholt {
1504370c3bd0SJoe Eykholt struct fc_rport_priv *rdata = rdata_arg;
1505370c3bd0SJoe Eykholt struct fc_els_adisc *adisc;
1506370c3bd0SJoe Eykholt u8 op;
1507370c3bd0SJoe Eykholt
1508370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "Received a ADISC response\n");
1509370c3bd0SJoe Eykholt
1510785141c6SChad Dupuis if (fp == ERR_PTR(-FC_EX_CLOSED))
1511785141c6SChad Dupuis goto put;
1512785141c6SChad Dupuis
1513785141c6SChad Dupuis mutex_lock(&rdata->rp_mutex);
1514785141c6SChad Dupuis
1515370c3bd0SJoe Eykholt if (rdata->rp_state != RPORT_ST_ADISC) {
1516370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n",
1517370c3bd0SJoe Eykholt fc_rport_state(rdata));
1518370c3bd0SJoe Eykholt if (IS_ERR(fp))
1519370c3bd0SJoe Eykholt goto err;
1520370c3bd0SJoe Eykholt goto out;
1521370c3bd0SJoe Eykholt }
1522370c3bd0SJoe Eykholt
1523370c3bd0SJoe Eykholt if (IS_ERR(fp)) {
15249f9504a7SHannes Reinecke fc_rport_error(rdata, PTR_ERR(fp));
1525370c3bd0SJoe Eykholt goto err;
1526370c3bd0SJoe Eykholt }
1527370c3bd0SJoe Eykholt
1528370c3bd0SJoe Eykholt /*
1529370c3bd0SJoe Eykholt * If address verification failed. Consider us logged out of the rport.
1530370c3bd0SJoe Eykholt * Since the rport is still in discovery, we want to be
1531370c3bd0SJoe Eykholt * logged in, so go to PLOGI state. Otherwise, go back to READY.
1532370c3bd0SJoe Eykholt */
1533370c3bd0SJoe Eykholt op = fc_frame_payload_op(fp);
1534370c3bd0SJoe Eykholt adisc = fc_frame_payload_get(fp, sizeof(*adisc));
1535370c3bd0SJoe Eykholt if (op != ELS_LS_ACC || !adisc ||
1536370c3bd0SJoe Eykholt ntoh24(adisc->adisc_port_id) != rdata->ids.port_id ||
1537370c3bd0SJoe Eykholt get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name ||
1538370c3bd0SJoe Eykholt get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) {
1539370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "ADISC error or mismatch\n");
1540a7b12a27SJoe Eykholt fc_rport_enter_flogi(rdata);
1541370c3bd0SJoe Eykholt } else {
1542370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "ADISC OK\n");
1543370c3bd0SJoe Eykholt fc_rport_enter_ready(rdata);
1544370c3bd0SJoe Eykholt }
1545370c3bd0SJoe Eykholt out:
1546370c3bd0SJoe Eykholt fc_frame_free(fp);
1547370c3bd0SJoe Eykholt err:
1548370c3bd0SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
1549785141c6SChad Dupuis put:
1550944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
1551370c3bd0SJoe Eykholt }
1552370c3bd0SJoe Eykholt
1553370c3bd0SJoe Eykholt /**
15543a3b42bfSRobert Love * fc_rport_enter_adisc() - Send Address Discover (ADISC) request
15553a3b42bfSRobert Love * @rdata: The remote port to send the ADISC request to
1556370c3bd0SJoe Eykholt *
15574d2095ccSHannes Reinecke * Reference counting: increments kref when sending ELS
1558370c3bd0SJoe Eykholt */
fc_rport_enter_adisc(struct fc_rport_priv * rdata)1559370c3bd0SJoe Eykholt static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
1560370c3bd0SJoe Eykholt {
1561370c3bd0SJoe Eykholt struct fc_lport *lport = rdata->local_port;
1562370c3bd0SJoe Eykholt struct fc_frame *fp;
1563370c3bd0SJoe Eykholt
1564ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1565ee35624eSHannes Reinecke
1566370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "sending ADISC from %s state\n",
1567370c3bd0SJoe Eykholt fc_rport_state(rdata));
1568370c3bd0SJoe Eykholt
1569370c3bd0SJoe Eykholt fc_rport_state_enter(rdata, RPORT_ST_ADISC);
1570370c3bd0SJoe Eykholt
1571370c3bd0SJoe Eykholt fp = fc_frame_alloc(lport, sizeof(struct fc_els_adisc));
1572370c3bd0SJoe Eykholt if (!fp) {
15739f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_ALLOC_ERR);
1574370c3bd0SJoe Eykholt return;
1575370c3bd0SJoe Eykholt }
15764d2095ccSHannes Reinecke kref_get(&rdata->kref);
1577370c3bd0SJoe Eykholt if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
1578b94f8951SJoe Eykholt fc_rport_adisc_resp, rdata,
15794d2095ccSHannes Reinecke 2 * lport->r_a_tov)) {
15809f9504a7SHannes Reinecke fc_rport_error_retry(rdata, -FC_EX_XMIT_ERR);
1581944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
15824d2095ccSHannes Reinecke }
1583370c3bd0SJoe Eykholt }
1584370c3bd0SJoe Eykholt
1585370c3bd0SJoe Eykholt /**
15863a3b42bfSRobert Love * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
15873a3b42bfSRobert Love * @rdata: The remote port that sent the ADISC request
15883a3b42bfSRobert Love * @in_fp: The ADISC request frame
15898abbe3a4SJoe Eykholt */
fc_rport_recv_adisc_req(struct fc_rport_priv * rdata,struct fc_frame * in_fp)15908abbe3a4SJoe Eykholt static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
159192261156SJoe Eykholt struct fc_frame *in_fp)
15928abbe3a4SJoe Eykholt {
15938abbe3a4SJoe Eykholt struct fc_lport *lport = rdata->local_port;
15948abbe3a4SJoe Eykholt struct fc_frame *fp;
15958abbe3a4SJoe Eykholt struct fc_els_adisc *adisc;
15968abbe3a4SJoe Eykholt struct fc_seq_els_data rjt_data;
15978abbe3a4SJoe Eykholt
1598ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1599ee35624eSHannes Reinecke lockdep_assert_held(&lport->lp_mutex);
1600ee35624eSHannes Reinecke
16018abbe3a4SJoe Eykholt FC_RPORT_DBG(rdata, "Received ADISC request\n");
16028abbe3a4SJoe Eykholt
16038abbe3a4SJoe Eykholt adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
16048abbe3a4SJoe Eykholt if (!adisc) {
16058abbe3a4SJoe Eykholt rjt_data.reason = ELS_RJT_PROT;
16068abbe3a4SJoe Eykholt rjt_data.explan = ELS_EXPL_INV_LEN;
16077ab24dd1SHannes Reinecke fc_seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
16088abbe3a4SJoe Eykholt goto drop;
16098abbe3a4SJoe Eykholt }
16108abbe3a4SJoe Eykholt
16118abbe3a4SJoe Eykholt fp = fc_frame_alloc(lport, sizeof(*adisc));
16128abbe3a4SJoe Eykholt if (!fp)
16138abbe3a4SJoe Eykholt goto drop;
16148abbe3a4SJoe Eykholt fc_adisc_fill(lport, fp);
16158abbe3a4SJoe Eykholt adisc = fc_frame_payload_get(fp, sizeof(*adisc));
16168abbe3a4SJoe Eykholt adisc->adisc_cmd = ELS_LS_ACC;
161724f089e2SJoe Eykholt fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
161824f089e2SJoe Eykholt lport->tt.frame_send(lport, fp);
16198abbe3a4SJoe Eykholt drop:
16208abbe3a4SJoe Eykholt fc_frame_free(in_fp);
16218abbe3a4SJoe Eykholt }
16228abbe3a4SJoe Eykholt
16238abbe3a4SJoe Eykholt /**
162463e27fb8SYi Zou * fc_rport_recv_rls_req() - Handle received Read Link Status request
162563e27fb8SYi Zou * @rdata: The remote port that sent the RLS request
162663e27fb8SYi Zou * @rx_fp: The PRLI request frame
162763e27fb8SYi Zou */
fc_rport_recv_rls_req(struct fc_rport_priv * rdata,struct fc_frame * rx_fp)162863e27fb8SYi Zou static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
162992261156SJoe Eykholt struct fc_frame *rx_fp)
163063e27fb8SYi Zou
163163e27fb8SYi Zou {
163263e27fb8SYi Zou struct fc_lport *lport = rdata->local_port;
163363e27fb8SYi Zou struct fc_frame *fp;
163463e27fb8SYi Zou struct fc_els_rls *rls;
163563e27fb8SYi Zou struct fc_els_rls_resp *rsp;
163663e27fb8SYi Zou struct fc_els_lesb *lesb;
163763e27fb8SYi Zou struct fc_seq_els_data rjt_data;
163863e27fb8SYi Zou struct fc_host_statistics *hst;
163963e27fb8SYi Zou
1640ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1641ee35624eSHannes Reinecke
164263e27fb8SYi Zou FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
164363e27fb8SYi Zou fc_rport_state(rdata));
164463e27fb8SYi Zou
164563e27fb8SYi Zou rls = fc_frame_payload_get(rx_fp, sizeof(*rls));
164663e27fb8SYi Zou if (!rls) {
164763e27fb8SYi Zou rjt_data.reason = ELS_RJT_PROT;
164863e27fb8SYi Zou rjt_data.explan = ELS_EXPL_INV_LEN;
164963e27fb8SYi Zou goto out_rjt;
165063e27fb8SYi Zou }
165163e27fb8SYi Zou
165263e27fb8SYi Zou fp = fc_frame_alloc(lport, sizeof(*rsp));
165363e27fb8SYi Zou if (!fp) {
165463e27fb8SYi Zou rjt_data.reason = ELS_RJT_UNAB;
165563e27fb8SYi Zou rjt_data.explan = ELS_EXPL_INSUF_RES;
165663e27fb8SYi Zou goto out_rjt;
165763e27fb8SYi Zou }
165863e27fb8SYi Zou
165963e27fb8SYi Zou rsp = fc_frame_payload_get(fp, sizeof(*rsp));
166063e27fb8SYi Zou memset(rsp, 0, sizeof(*rsp));
166163e27fb8SYi Zou rsp->rls_cmd = ELS_LS_ACC;
166263e27fb8SYi Zou lesb = &rsp->rls_lesb;
166363e27fb8SYi Zou if (lport->tt.get_lesb) {
166463e27fb8SYi Zou /* get LESB from LLD if it supports it */
166563e27fb8SYi Zou lport->tt.get_lesb(lport, lesb);
166663e27fb8SYi Zou } else {
166763e27fb8SYi Zou fc_get_host_stats(lport->host);
166863e27fb8SYi Zou hst = &lport->host_stats;
166963e27fb8SYi Zou lesb->lesb_link_fail = htonl(hst->link_failure_count);
167063e27fb8SYi Zou lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count);
167163e27fb8SYi Zou lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count);
167263e27fb8SYi Zou lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count);
167363e27fb8SYi Zou lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count);
167463e27fb8SYi Zou lesb->lesb_inv_crc = htonl(hst->invalid_crc_count);
167563e27fb8SYi Zou }
167663e27fb8SYi Zou
167724f089e2SJoe Eykholt fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
167824f089e2SJoe Eykholt lport->tt.frame_send(lport, fp);
167963e27fb8SYi Zou goto out;
168063e27fb8SYi Zou
168163e27fb8SYi Zou out_rjt:
16827ab24dd1SHannes Reinecke fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
168363e27fb8SYi Zou out:
168463e27fb8SYi Zou fc_frame_free(rx_fp);
168563e27fb8SYi Zou }
168663e27fb8SYi Zou
168763e27fb8SYi Zou /**
16883a3b42bfSRobert Love * fc_rport_recv_els_req() - Handler for validated ELS requests
16893a3b42bfSRobert Love * @lport: The local port that received the ELS request
16903a3b42bfSRobert Love * @fp: The ELS request frame
169183fe6a93SJoe Eykholt *
169283fe6a93SJoe Eykholt * Handle incoming ELS requests that require port login.
169383fe6a93SJoe Eykholt * The ELS opcode has already been validated by the caller.
169442e9a92fSRobert Love *
16954d2095ccSHannes Reinecke * Reference counting: does not modify kref
169642e9a92fSRobert Love */
fc_rport_recv_els_req(struct fc_lport * lport,struct fc_frame * fp)169792261156SJoe Eykholt static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
169842e9a92fSRobert Love {
1699131203a1SJoe Eykholt struct fc_rport_priv *rdata;
170042e9a92fSRobert Love struct fc_seq_els_data els_data;
170142e9a92fSRobert Love
1702ee35624eSHannes Reinecke lockdep_assert_held(&lport->lp_mutex);
1703ee35624eSHannes Reinecke
1704e87b7777SHannes Reinecke rdata = fc_rport_lookup(lport, fc_frame_sid(fp));
170557d3ec7eSHannes Reinecke if (!rdata) {
170657d3ec7eSHannes Reinecke FC_RPORT_ID_DBG(lport, fc_frame_sid(fp),
170757d3ec7eSHannes Reinecke "Received ELS 0x%02x from non-logged-in port\n",
170857d3ec7eSHannes Reinecke fc_frame_payload_op(fp));
170983fe6a93SJoe Eykholt goto reject;
171057d3ec7eSHannes Reinecke }
1711baa6719fSHannes Reinecke
1712131203a1SJoe Eykholt mutex_lock(&rdata->rp_mutex);
1713131203a1SJoe Eykholt
171483fe6a93SJoe Eykholt switch (rdata->rp_state) {
171583fe6a93SJoe Eykholt case RPORT_ST_PRLI:
171683fe6a93SJoe Eykholt case RPORT_ST_RTV:
171783fe6a93SJoe Eykholt case RPORT_ST_READY:
1718370c3bd0SJoe Eykholt case RPORT_ST_ADISC:
171983fe6a93SJoe Eykholt break;
17208acf1b50SHannes Reinecke case RPORT_ST_PLOGI:
17218acf1b50SHannes Reinecke if (fc_frame_payload_op(fp) == ELS_PRLI) {
17228acf1b50SHannes Reinecke FC_RPORT_DBG(rdata, "Reject ELS PRLI "
17238acf1b50SHannes Reinecke "while in state %s\n",
17248acf1b50SHannes Reinecke fc_rport_state(rdata));
17258acf1b50SHannes Reinecke mutex_unlock(&rdata->rp_mutex);
1726944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
17278acf1b50SHannes Reinecke goto busy;
17288acf1b50SHannes Reinecke }
1729df561f66SGustavo A. R. Silva fallthrough;
173083fe6a93SJoe Eykholt default:
173157d3ec7eSHannes Reinecke FC_RPORT_DBG(rdata,
173257d3ec7eSHannes Reinecke "Reject ELS 0x%02x while in state %s\n",
173357d3ec7eSHannes Reinecke fc_frame_payload_op(fp), fc_rport_state(rdata));
173483fe6a93SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
1735944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
173683fe6a93SJoe Eykholt goto reject;
173783fe6a93SJoe Eykholt }
173883fe6a93SJoe Eykholt
173983fe6a93SJoe Eykholt switch (fc_frame_payload_op(fp)) {
174042e9a92fSRobert Love case ELS_PRLI:
174192261156SJoe Eykholt fc_rport_recv_prli_req(rdata, fp);
174242e9a92fSRobert Love break;
174342e9a92fSRobert Love case ELS_PRLO:
174492261156SJoe Eykholt fc_rport_recv_prlo_req(rdata, fp);
174542e9a92fSRobert Love break;
17468abbe3a4SJoe Eykholt case ELS_ADISC:
174792261156SJoe Eykholt fc_rport_recv_adisc_req(rdata, fp);
17488abbe3a4SJoe Eykholt break;
174942e9a92fSRobert Love case ELS_RRQ:
17507ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_RRQ, NULL);
175192261156SJoe Eykholt fc_frame_free(fp);
175242e9a92fSRobert Love break;
175342e9a92fSRobert Love case ELS_REC:
17547ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_REC, NULL);
175592261156SJoe Eykholt fc_frame_free(fp);
175642e9a92fSRobert Love break;
175763e27fb8SYi Zou case ELS_RLS:
175892261156SJoe Eykholt fc_rport_recv_rls_req(rdata, fp);
175963e27fb8SYi Zou break;
17607c5a51b8SHannes Reinecke case ELS_RTV:
17617c5a51b8SHannes Reinecke fc_rport_recv_rtv_req(rdata, fp);
17627c5a51b8SHannes Reinecke break;
176342e9a92fSRobert Love default:
176483fe6a93SJoe Eykholt fc_frame_free(fp); /* can't happen */
176542e9a92fSRobert Love break;
176642e9a92fSRobert Love }
176742e9a92fSRobert Love
176842e9a92fSRobert Love mutex_unlock(&rdata->rp_mutex);
1769944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
177083fe6a93SJoe Eykholt return;
177183fe6a93SJoe Eykholt
177283fe6a93SJoe Eykholt reject:
177392261156SJoe Eykholt els_data.reason = ELS_RJT_UNAB;
177492261156SJoe Eykholt els_data.explan = ELS_EXPL_PLOGI_REQD;
17757ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
177683fe6a93SJoe Eykholt fc_frame_free(fp);
17778acf1b50SHannes Reinecke return;
17788acf1b50SHannes Reinecke
17798acf1b50SHannes Reinecke busy:
17808acf1b50SHannes Reinecke els_data.reason = ELS_RJT_BUSY;
17818acf1b50SHannes Reinecke els_data.explan = ELS_EXPL_NONE;
17827ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
17838acf1b50SHannes Reinecke fc_frame_free(fp);
17848acf1b50SHannes Reinecke return;
178583fe6a93SJoe Eykholt }
178683fe6a93SJoe Eykholt
178783fe6a93SJoe Eykholt /**
17883a3b42bfSRobert Love * fc_rport_recv_req() - Handler for requests
17893a3b42bfSRobert Love * @lport: The local port that received the request
179092261156SJoe Eykholt * @fp: The request frame
179183fe6a93SJoe Eykholt *
17924d2095ccSHannes Reinecke * Reference counting: does not modify kref
179383fe6a93SJoe Eykholt */
fc_rport_recv_req(struct fc_lport * lport,struct fc_frame * fp)1794e76ee65fSHannes Reinecke void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
179583fe6a93SJoe Eykholt {
179683fe6a93SJoe Eykholt struct fc_seq_els_data els_data;
179783fe6a93SJoe Eykholt
1798ee35624eSHannes Reinecke lockdep_assert_held(&lport->lp_mutex);
1799ee35624eSHannes Reinecke
180083fe6a93SJoe Eykholt /*
1801a7b12a27SJoe Eykholt * Handle FLOGI, PLOGI and LOGO requests separately, since they
180283fe6a93SJoe Eykholt * don't require prior login.
180383fe6a93SJoe Eykholt * Check for unsupported opcodes first and reject them.
180483fe6a93SJoe Eykholt * For some ops, it would be incorrect to reject with "PLOGI required".
180583fe6a93SJoe Eykholt */
180683fe6a93SJoe Eykholt switch (fc_frame_payload_op(fp)) {
1807a7b12a27SJoe Eykholt case ELS_FLOGI:
180892261156SJoe Eykholt fc_rport_recv_flogi_req(lport, fp);
1809a7b12a27SJoe Eykholt break;
181083fe6a93SJoe Eykholt case ELS_PLOGI:
181192261156SJoe Eykholt fc_rport_recv_plogi_req(lport, fp);
181283fe6a93SJoe Eykholt break;
181383fe6a93SJoe Eykholt case ELS_LOGO:
181492261156SJoe Eykholt fc_rport_recv_logo_req(lport, fp);
181583fe6a93SJoe Eykholt break;
181683fe6a93SJoe Eykholt case ELS_PRLI:
181783fe6a93SJoe Eykholt case ELS_PRLO:
18188abbe3a4SJoe Eykholt case ELS_ADISC:
181983fe6a93SJoe Eykholt case ELS_RRQ:
182083fe6a93SJoe Eykholt case ELS_REC:
182163e27fb8SYi Zou case ELS_RLS:
18227c5a51b8SHannes Reinecke case ELS_RTV:
182392261156SJoe Eykholt fc_rport_recv_els_req(lport, fp);
182483fe6a93SJoe Eykholt break;
182583fe6a93SJoe Eykholt default:
182683fe6a93SJoe Eykholt els_data.reason = ELS_RJT_UNSUP;
182783fe6a93SJoe Eykholt els_data.explan = ELS_EXPL_NONE;
18287ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
182992261156SJoe Eykholt fc_frame_free(fp);
183083fe6a93SJoe Eykholt break;
183183fe6a93SJoe Eykholt }
183242e9a92fSRobert Love }
1833e76ee65fSHannes Reinecke EXPORT_SYMBOL(fc_rport_recv_req);
183442e9a92fSRobert Love
183542e9a92fSRobert Love /**
18363a3b42bfSRobert Love * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
18373a3b42bfSRobert Love * @lport: The local port that received the PLOGI request
18383a3b42bfSRobert Love * @rx_fp: The PLOGI request frame
183942e9a92fSRobert Love *
18404d2095ccSHannes Reinecke * Reference counting: increments kref on return
184142e9a92fSRobert Love */
fc_rport_recv_plogi_req(struct fc_lport * lport,struct fc_frame * rx_fp)18423ac6f98fSJoe Eykholt static void fc_rport_recv_plogi_req(struct fc_lport *lport,
184392261156SJoe Eykholt struct fc_frame *rx_fp)
184442e9a92fSRobert Love {
18453ac6f98fSJoe Eykholt struct fc_disc *disc;
18463ac6f98fSJoe Eykholt struct fc_rport_priv *rdata;
184742e9a92fSRobert Love struct fc_frame *fp = rx_fp;
184842e9a92fSRobert Love struct fc_els_flogi *pl;
184942e9a92fSRobert Love struct fc_seq_els_data rjt_data;
185024f089e2SJoe Eykholt u32 sid;
18513ac6f98fSJoe Eykholt
18529a26653bSHannes Reinecke lockdep_assert_held(&lport->lp_mutex);
1853ee35624eSHannes Reinecke
1854251748a9SJoe Eykholt sid = fc_frame_sid(fp);
18553ac6f98fSJoe Eykholt
18563ac6f98fSJoe Eykholt FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
18573ac6f98fSJoe Eykholt
185842e9a92fSRobert Love pl = fc_frame_payload_get(fp, sizeof(*pl));
185942e9a92fSRobert Love if (!pl) {
18603ac6f98fSJoe Eykholt FC_RPORT_ID_DBG(lport, sid, "Received PLOGI too short\n");
18613ac6f98fSJoe Eykholt rjt_data.reason = ELS_RJT_PROT;
18623ac6f98fSJoe Eykholt rjt_data.explan = ELS_EXPL_INV_LEN;
18633ac6f98fSJoe Eykholt goto reject;
186442e9a92fSRobert Love }
18653ac6f98fSJoe Eykholt
18663ac6f98fSJoe Eykholt disc = &lport->disc;
18673ac6f98fSJoe Eykholt mutex_lock(&disc->disc_mutex);
18682580064bSHannes Reinecke rdata = fc_rport_create(lport, sid);
18693ac6f98fSJoe Eykholt if (!rdata) {
18703ac6f98fSJoe Eykholt mutex_unlock(&disc->disc_mutex);
18713ac6f98fSJoe Eykholt rjt_data.reason = ELS_RJT_UNAB;
18723ac6f98fSJoe Eykholt rjt_data.explan = ELS_EXPL_INSUF_RES;
18733ac6f98fSJoe Eykholt goto reject;
18743ac6f98fSJoe Eykholt }
18753ac6f98fSJoe Eykholt
18763ac6f98fSJoe Eykholt mutex_lock(&rdata->rp_mutex);
18773ac6f98fSJoe Eykholt mutex_unlock(&disc->disc_mutex);
18783ac6f98fSJoe Eykholt
18793ac6f98fSJoe Eykholt rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
18803ac6f98fSJoe Eykholt rdata->ids.node_name = get_unaligned_be64(&pl->fl_wwnn);
188142e9a92fSRobert Love
188242e9a92fSRobert Love /*
18833ac6f98fSJoe Eykholt * If the rport was just created, possibly due to the incoming PLOGI,
188442e9a92fSRobert Love * set the state appropriately and accept the PLOGI.
188542e9a92fSRobert Love *
188642e9a92fSRobert Love * If we had also sent a PLOGI, and if the received PLOGI is from a
188742e9a92fSRobert Love * higher WWPN, we accept it, otherwise an LS_RJT is sent with reason
188842e9a92fSRobert Love * "command already in progress".
188942e9a92fSRobert Love *
189042e9a92fSRobert Love * XXX TBD: If the session was ready before, the PLOGI should result in
189142e9a92fSRobert Love * all outstanding exchanges being reset.
189242e9a92fSRobert Love */
189342e9a92fSRobert Love switch (rdata->rp_state) {
189442e9a92fSRobert Love case RPORT_ST_INIT:
18953ac6f98fSJoe Eykholt FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
189642e9a92fSRobert Love break;
1897a7b12a27SJoe Eykholt case RPORT_ST_PLOGI_WAIT:
1898a7b12a27SJoe Eykholt FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI_WAIT state\n");
1899a7b12a27SJoe Eykholt break;
190042e9a92fSRobert Love case RPORT_ST_PLOGI:
19013ac6f98fSJoe Eykholt FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
19023ac6f98fSJoe Eykholt if (rdata->ids.port_name < lport->wwpn) {
19033ac6f98fSJoe Eykholt mutex_unlock(&rdata->rp_mutex);
19043ac6f98fSJoe Eykholt rjt_data.reason = ELS_RJT_INPROG;
19053ac6f98fSJoe Eykholt rjt_data.explan = ELS_EXPL_NONE;
19063ac6f98fSJoe Eykholt goto reject;
19073ac6f98fSJoe Eykholt }
190842e9a92fSRobert Love break;
190942e9a92fSRobert Love case RPORT_ST_PRLI:
1910b4a9c7edSJoe Eykholt case RPORT_ST_RTV:
191142e9a92fSRobert Love case RPORT_ST_READY:
1912370c3bd0SJoe Eykholt case RPORT_ST_ADISC:
1913370c3bd0SJoe Eykholt FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
1914370c3bd0SJoe Eykholt "- ignored for now\n", rdata->rp_state);
1915370c3bd0SJoe Eykholt /* XXX TBD - should reset */
191642e9a92fSRobert Love break;
1917a7b12a27SJoe Eykholt case RPORT_ST_FLOGI:
191814194054SJoe Eykholt case RPORT_ST_DELETE:
1919b4a9c7edSJoe Eykholt FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
1920b4a9c7edSJoe Eykholt fc_rport_state(rdata));
1921b4a9c7edSJoe Eykholt mutex_unlock(&rdata->rp_mutex);
1922b4a9c7edSJoe Eykholt rjt_data.reason = ELS_RJT_BUSY;
1923b4a9c7edSJoe Eykholt rjt_data.explan = ELS_EXPL_NONE;
1924b4a9c7edSJoe Eykholt goto reject;
192542e9a92fSRobert Love }
1926e0335f67SMark Rustad if (!fc_rport_compatible_roles(lport, rdata)) {
1927e0335f67SMark Rustad FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n");
1928e0335f67SMark Rustad mutex_unlock(&rdata->rp_mutex);
1929e0335f67SMark Rustad rjt_data.reason = ELS_RJT_LOGIC;
1930e0335f67SMark Rustad rjt_data.explan = ELS_EXPL_NONE;
1931e0335f67SMark Rustad goto reject;
1932e0335f67SMark Rustad }
193342e9a92fSRobert Love
193442e9a92fSRobert Love /*
193542e9a92fSRobert Love * Get session payload size from incoming PLOGI.
193642e9a92fSRobert Love */
19373ac6f98fSJoe Eykholt rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
193842e9a92fSRobert Love
193942e9a92fSRobert Love /*
19403ac6f98fSJoe Eykholt * Send LS_ACC. If this fails, the originator should retry.
194142e9a92fSRobert Love */
19423ac6f98fSJoe Eykholt fp = fc_frame_alloc(lport, sizeof(*pl));
19433ac6f98fSJoe Eykholt if (!fp)
19443ac6f98fSJoe Eykholt goto out;
19453ac6f98fSJoe Eykholt
19463ac6f98fSJoe Eykholt fc_plogi_fill(lport, fp, ELS_LS_ACC);
194724f089e2SJoe Eykholt fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
194824f089e2SJoe Eykholt lport->tt.frame_send(lport, fp);
19499fb9d328SJoe Eykholt fc_rport_enter_prli(rdata);
19503ac6f98fSJoe Eykholt out:
19513ac6f98fSJoe Eykholt mutex_unlock(&rdata->rp_mutex);
195224f089e2SJoe Eykholt fc_frame_free(rx_fp);
19533ac6f98fSJoe Eykholt return;
19543ac6f98fSJoe Eykholt
19553ac6f98fSJoe Eykholt reject:
19567ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
19573ac6f98fSJoe Eykholt fc_frame_free(fp);
195842e9a92fSRobert Love }
195942e9a92fSRobert Love
196042e9a92fSRobert Love /**
19613a3b42bfSRobert Love * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
19623a3b42bfSRobert Love * @rdata: The remote port that sent the PRLI request
19633a3b42bfSRobert Love * @rx_fp: The PRLI request frame
196442e9a92fSRobert Love */
fc_rport_recv_prli_req(struct fc_rport_priv * rdata,struct fc_frame * rx_fp)19659fb9d328SJoe Eykholt static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
196692261156SJoe Eykholt struct fc_frame *rx_fp)
196742e9a92fSRobert Love {
196842e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
196942e9a92fSRobert Love struct fc_frame *fp;
197042e9a92fSRobert Love struct {
197142e9a92fSRobert Love struct fc_els_prli prli;
197242e9a92fSRobert Love struct fc_els_spp spp;
197342e9a92fSRobert Love } *pp;
197442e9a92fSRobert Love struct fc_els_spp *rspp; /* request service param page */
197542e9a92fSRobert Love struct fc_els_spp *spp; /* response spp */
197642e9a92fSRobert Love unsigned int len;
197742e9a92fSRobert Love unsigned int plen;
197842e9a92fSRobert Love enum fc_els_spp_resp resp;
197942e9a92fSRobert Love struct fc_seq_els_data rjt_data;
198096ad8464SJoe Eykholt struct fc4_prov *prov;
198142e9a92fSRobert Love
1982ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
1983ee35624eSHannes Reinecke
19849fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
19859fb9d328SJoe Eykholt fc_rport_state(rdata));
198642e9a92fSRobert Love
1987251748a9SJoe Eykholt len = fr_len(rx_fp) - sizeof(struct fc_frame_header);
198842e9a92fSRobert Love pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
1989a2f6a024SJoe Eykholt if (!pp)
1990a2f6a024SJoe Eykholt goto reject_len;
199142e9a92fSRobert Love plen = ntohs(pp->prli.prli_len);
1992a2f6a024SJoe Eykholt if ((plen % 4) != 0 || plen > len || plen < 16)
1993a2f6a024SJoe Eykholt goto reject_len;
1994a2f6a024SJoe Eykholt if (plen < len)
199542e9a92fSRobert Love len = plen;
199642e9a92fSRobert Love plen = pp->prli.prli_spp_len;
199742e9a92fSRobert Love if ((plen % 4) != 0 || plen < sizeof(*spp) ||
1998a2f6a024SJoe Eykholt plen > len || len < sizeof(*pp) || plen < 12)
1999a2f6a024SJoe Eykholt goto reject_len;
200042e9a92fSRobert Love rspp = &pp->spp;
2001a2f6a024SJoe Eykholt
2002a2f6a024SJoe Eykholt fp = fc_frame_alloc(lport, len);
2003a2f6a024SJoe Eykholt if (!fp) {
2004a2f6a024SJoe Eykholt rjt_data.reason = ELS_RJT_UNAB;
2005a2f6a024SJoe Eykholt rjt_data.explan = ELS_EXPL_INSUF_RES;
2006a2f6a024SJoe Eykholt goto reject;
200742e9a92fSRobert Love }
200842e9a92fSRobert Love pp = fc_frame_payload_get(fp, len);
200942e9a92fSRobert Love WARN_ON(!pp);
201042e9a92fSRobert Love memset(pp, 0, len);
201142e9a92fSRobert Love pp->prli.prli_cmd = ELS_LS_ACC;
201242e9a92fSRobert Love pp->prli.prli_spp_len = plen;
201342e9a92fSRobert Love pp->prli.prli_len = htons(len);
201442e9a92fSRobert Love len -= sizeof(struct fc_els_prli);
201542e9a92fSRobert Love
201642e9a92fSRobert Love /*
201742e9a92fSRobert Love * Go through all the service parameter pages and build
201842e9a92fSRobert Love * response. If plen indicates longer SPP than standard,
201942e9a92fSRobert Love * use that. The entire response has been pre-cleared above.
202042e9a92fSRobert Love */
202142e9a92fSRobert Love spp = &pp->spp;
202296ad8464SJoe Eykholt mutex_lock(&fc_prov_mutex);
202342e9a92fSRobert Love while (len >= plen) {
202475a2792dSBhanu Prakash Gollapudi rdata->spp_type = rspp->spp_type;
202542e9a92fSRobert Love spp->spp_type = rspp->spp_type;
202642e9a92fSRobert Love spp->spp_type_ext = rspp->spp_type_ext;
202796ad8464SJoe Eykholt resp = 0;
2028a2f6a024SJoe Eykholt
202996ad8464SJoe Eykholt if (rspp->spp_type < FC_FC4_PROV_SIZE) {
2030386b97b4SHannes Reinecke enum fc_els_spp_resp active = 0, passive = 0;
2031386b97b4SHannes Reinecke
203296ad8464SJoe Eykholt prov = fc_active_prov[rspp->spp_type];
203396ad8464SJoe Eykholt if (prov)
2034386b97b4SHannes Reinecke active = prov->prli(rdata, plen, rspp, spp);
203596ad8464SJoe Eykholt prov = fc_passive_prov[rspp->spp_type];
2036386b97b4SHannes Reinecke if (prov)
203796ad8464SJoe Eykholt passive = prov->prli(rdata, plen, rspp, spp);
2038386b97b4SHannes Reinecke if (!active || passive == FC_SPP_RESP_ACK)
203996ad8464SJoe Eykholt resp = passive;
2040386b97b4SHannes Reinecke else
2041386b97b4SHannes Reinecke resp = active;
2042386b97b4SHannes Reinecke FC_RPORT_DBG(rdata, "PRLI rspp type %x "
2043386b97b4SHannes Reinecke "active %x passive %x\n",
2044386b97b4SHannes Reinecke rspp->spp_type, active, passive);
204596ad8464SJoe Eykholt }
204696ad8464SJoe Eykholt if (!resp) {
204796ad8464SJoe Eykholt if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
204896ad8464SJoe Eykholt resp |= FC_SPP_RESP_CONF;
204996ad8464SJoe Eykholt else
205096ad8464SJoe Eykholt resp |= FC_SPP_RESP_INVL;
205142e9a92fSRobert Love }
205242e9a92fSRobert Love spp->spp_flags |= resp;
205342e9a92fSRobert Love len -= plen;
205442e9a92fSRobert Love rspp = (struct fc_els_spp *)((char *)rspp + plen);
205542e9a92fSRobert Love spp = (struct fc_els_spp *)((char *)spp + plen);
205642e9a92fSRobert Love }
205796ad8464SJoe Eykholt mutex_unlock(&fc_prov_mutex);
205842e9a92fSRobert Love
205942e9a92fSRobert Love /*
206042e9a92fSRobert Love * Send LS_ACC. If this fails, the originator should retry.
206142e9a92fSRobert Love */
206224f089e2SJoe Eykholt fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
206324f089e2SJoe Eykholt lport->tt.frame_send(lport, fp);
206442e9a92fSRobert Love
2065a2f6a024SJoe Eykholt goto drop;
2066a2f6a024SJoe Eykholt
2067a2f6a024SJoe Eykholt reject_len:
2068a2f6a024SJoe Eykholt rjt_data.reason = ELS_RJT_PROT;
2069a2f6a024SJoe Eykholt rjt_data.explan = ELS_EXPL_INV_LEN;
2070a2f6a024SJoe Eykholt reject:
20717ab24dd1SHannes Reinecke fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
2072a2f6a024SJoe Eykholt drop:
207342e9a92fSRobert Love fc_frame_free(rx_fp);
207442e9a92fSRobert Love }
207542e9a92fSRobert Love
207642e9a92fSRobert Love /**
20773a3b42bfSRobert Love * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
20783a3b42bfSRobert Love * @rdata: The remote port that sent the PRLO request
2079f8fc6c2cSBhanu Prakash Gollapudi * @rx_fp: The PRLO request frame
208042e9a92fSRobert Love */
fc_rport_recv_prlo_req(struct fc_rport_priv * rdata,struct fc_frame * rx_fp)20819fb9d328SJoe Eykholt static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
2082f8fc6c2cSBhanu Prakash Gollapudi struct fc_frame *rx_fp)
208342e9a92fSRobert Love {
208442e9a92fSRobert Love struct fc_lport *lport = rdata->local_port;
2085f8fc6c2cSBhanu Prakash Gollapudi struct fc_frame *fp;
2086f8fc6c2cSBhanu Prakash Gollapudi struct {
2087f8fc6c2cSBhanu Prakash Gollapudi struct fc_els_prlo prlo;
2088f8fc6c2cSBhanu Prakash Gollapudi struct fc_els_spp spp;
2089f8fc6c2cSBhanu Prakash Gollapudi } *pp;
2090f8fc6c2cSBhanu Prakash Gollapudi struct fc_els_spp *rspp; /* request service param page */
2091f8fc6c2cSBhanu Prakash Gollapudi struct fc_els_spp *spp; /* response spp */
2092f8fc6c2cSBhanu Prakash Gollapudi unsigned int len;
2093f8fc6c2cSBhanu Prakash Gollapudi unsigned int plen;
209442e9a92fSRobert Love struct fc_seq_els_data rjt_data;
209542e9a92fSRobert Love
2096ee35624eSHannes Reinecke lockdep_assert_held(&rdata->rp_mutex);
2097ee35624eSHannes Reinecke
20989fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
20999fb9d328SJoe Eykholt fc_rport_state(rdata));
210042e9a92fSRobert Love
2101251748a9SJoe Eykholt len = fr_len(rx_fp) - sizeof(struct fc_frame_header);
2102f8fc6c2cSBhanu Prakash Gollapudi pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
2103f8fc6c2cSBhanu Prakash Gollapudi if (!pp)
2104f8fc6c2cSBhanu Prakash Gollapudi goto reject_len;
2105f8fc6c2cSBhanu Prakash Gollapudi plen = ntohs(pp->prlo.prlo_len);
2106f8fc6c2cSBhanu Prakash Gollapudi if (plen != 20)
2107f8fc6c2cSBhanu Prakash Gollapudi goto reject_len;
2108f8fc6c2cSBhanu Prakash Gollapudi if (plen < len)
2109f8fc6c2cSBhanu Prakash Gollapudi len = plen;
2110f8fc6c2cSBhanu Prakash Gollapudi
2111f8fc6c2cSBhanu Prakash Gollapudi rspp = &pp->spp;
2112f8fc6c2cSBhanu Prakash Gollapudi
2113f8fc6c2cSBhanu Prakash Gollapudi fp = fc_frame_alloc(lport, len);
2114f8fc6c2cSBhanu Prakash Gollapudi if (!fp) {
211542e9a92fSRobert Love rjt_data.reason = ELS_RJT_UNAB;
2116f8fc6c2cSBhanu Prakash Gollapudi rjt_data.explan = ELS_EXPL_INSUF_RES;
2117f8fc6c2cSBhanu Prakash Gollapudi goto reject;
2118f8fc6c2cSBhanu Prakash Gollapudi }
2119f8fc6c2cSBhanu Prakash Gollapudi
2120f8fc6c2cSBhanu Prakash Gollapudi pp = fc_frame_payload_get(fp, len);
2121f8fc6c2cSBhanu Prakash Gollapudi WARN_ON(!pp);
2122f8fc6c2cSBhanu Prakash Gollapudi memset(pp, 0, len);
2123f8fc6c2cSBhanu Prakash Gollapudi pp->prlo.prlo_cmd = ELS_LS_ACC;
2124f8fc6c2cSBhanu Prakash Gollapudi pp->prlo.prlo_obs = 0x10;
2125f8fc6c2cSBhanu Prakash Gollapudi pp->prlo.prlo_len = htons(len);
2126f8fc6c2cSBhanu Prakash Gollapudi spp = &pp->spp;
2127f8fc6c2cSBhanu Prakash Gollapudi spp->spp_type = rspp->spp_type;
2128f8fc6c2cSBhanu Prakash Gollapudi spp->spp_type_ext = rspp->spp_type_ext;
2129f8fc6c2cSBhanu Prakash Gollapudi spp->spp_flags = FC_SPP_RESP_ACK;
2130f8fc6c2cSBhanu Prakash Gollapudi
2131166f310bSHannes Reinecke fc_rport_enter_prli(rdata);
2132f8fc6c2cSBhanu Prakash Gollapudi
213392261156SJoe Eykholt fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
213492261156SJoe Eykholt lport->tt.frame_send(lport, fp);
2135f8fc6c2cSBhanu Prakash Gollapudi goto drop;
2136f8fc6c2cSBhanu Prakash Gollapudi
2137f8fc6c2cSBhanu Prakash Gollapudi reject_len:
2138f8fc6c2cSBhanu Prakash Gollapudi rjt_data.reason = ELS_RJT_PROT;
2139f8fc6c2cSBhanu Prakash Gollapudi rjt_data.explan = ELS_EXPL_INV_LEN;
2140f8fc6c2cSBhanu Prakash Gollapudi reject:
21417ab24dd1SHannes Reinecke fc_seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
2142f8fc6c2cSBhanu Prakash Gollapudi drop:
2143f8fc6c2cSBhanu Prakash Gollapudi fc_frame_free(rx_fp);
214442e9a92fSRobert Love }
214542e9a92fSRobert Love
214642e9a92fSRobert Love /**
21473a3b42bfSRobert Love * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests
21483a3b42bfSRobert Love * @lport: The local port that received the LOGO request
21493a3b42bfSRobert Love * @fp: The LOGO request frame
215042e9a92fSRobert Love *
21514d2095ccSHannes Reinecke * Reference counting: drops kref on return
215242e9a92fSRobert Love */
fc_rport_recv_logo_req(struct fc_lport * lport,struct fc_frame * fp)215392261156SJoe Eykholt static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
215442e9a92fSRobert Love {
215583fe6a93SJoe Eykholt struct fc_rport_priv *rdata;
215683fe6a93SJoe Eykholt u32 sid;
215742e9a92fSRobert Love
2158ee35624eSHannes Reinecke lockdep_assert_held(&lport->lp_mutex);
2159ee35624eSHannes Reinecke
21607ab24dd1SHannes Reinecke fc_seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
2161feab4ae7SJoe Eykholt
2162251748a9SJoe Eykholt sid = fc_frame_sid(fp);
216342e9a92fSRobert Love
2164e87b7777SHannes Reinecke rdata = fc_rport_lookup(lport, sid);
216583fe6a93SJoe Eykholt if (rdata) {
216683fe6a93SJoe Eykholt mutex_lock(&rdata->rp_mutex);
21679fb9d328SJoe Eykholt FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
21689fb9d328SJoe Eykholt fc_rport_state(rdata));
2169feab4ae7SJoe Eykholt
2170649eb869SHannes Reinecke fc_rport_enter_delete(rdata, RPORT_EV_STOP);
217183fe6a93SJoe Eykholt mutex_unlock(&rdata->rp_mutex);
2172944ef968SHannes Reinecke kref_put(&rdata->kref, fc_rport_destroy);
217383fe6a93SJoe Eykholt } else
217483fe6a93SJoe Eykholt FC_RPORT_ID_DBG(lport, sid,
217583fe6a93SJoe Eykholt "Received LOGO from non-logged-in port\n");
217642e9a92fSRobert Love fc_frame_free(fp);
217742e9a92fSRobert Love }
217842e9a92fSRobert Love
21793a3b42bfSRobert Love /**
21803a3b42bfSRobert Love * fc_rport_flush_queue() - Flush the rport_event_queue
21813a3b42bfSRobert Love */
fc_rport_flush_queue(void)21825922a957SHannes Reinecke void fc_rport_flush_queue(void)
218342e9a92fSRobert Love {
218442e9a92fSRobert Love flush_workqueue(rport_event_queue);
218542e9a92fSRobert Love }
21865922a957SHannes Reinecke EXPORT_SYMBOL(fc_rport_flush_queue);
218742e9a92fSRobert Love
21883a3b42bfSRobert Love /**
218996ad8464SJoe Eykholt * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
219096ad8464SJoe Eykholt * @rdata: remote port private
219196ad8464SJoe Eykholt * @spp_len: service parameter page length
219296ad8464SJoe Eykholt * @rspp: received service parameter page
219396ad8464SJoe Eykholt * @spp: response service parameter page
219496ad8464SJoe Eykholt *
219596ad8464SJoe Eykholt * Returns the value for the response code to be placed in spp_flags;
219696ad8464SJoe Eykholt * Returns 0 if not an initiator.
219796ad8464SJoe Eykholt */
fc_rport_fcp_prli(struct fc_rport_priv * rdata,u32 spp_len,const struct fc_els_spp * rspp,struct fc_els_spp * spp)219896ad8464SJoe Eykholt static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
219996ad8464SJoe Eykholt const struct fc_els_spp *rspp,
220096ad8464SJoe Eykholt struct fc_els_spp *spp)
220196ad8464SJoe Eykholt {
220296ad8464SJoe Eykholt struct fc_lport *lport = rdata->local_port;
220396ad8464SJoe Eykholt u32 fcp_parm;
220496ad8464SJoe Eykholt
220596ad8464SJoe Eykholt fcp_parm = ntohl(rspp->spp_params);
220696ad8464SJoe Eykholt rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
220796ad8464SJoe Eykholt if (fcp_parm & FCP_SPPF_INIT_FCN)
220896ad8464SJoe Eykholt rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
220996ad8464SJoe Eykholt if (fcp_parm & FCP_SPPF_TARG_FCN)
221096ad8464SJoe Eykholt rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
221196ad8464SJoe Eykholt if (fcp_parm & FCP_SPPF_RETRY)
221296ad8464SJoe Eykholt rdata->flags |= FC_RP_FLAGS_RETRY;
221396ad8464SJoe Eykholt rdata->supported_classes = FC_COS_CLASS3;
221496ad8464SJoe Eykholt
2215732bdb9dSMark Rustad if (!(lport->service_params & FCP_SPPF_INIT_FCN))
221696ad8464SJoe Eykholt return 0;
221796ad8464SJoe Eykholt
221896ad8464SJoe Eykholt spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
221996ad8464SJoe Eykholt
222096ad8464SJoe Eykholt /*
222196ad8464SJoe Eykholt * OR in our service parameters with other providers (target), if any.
222296ad8464SJoe Eykholt */
222396ad8464SJoe Eykholt fcp_parm = ntohl(spp->spp_params);
222496ad8464SJoe Eykholt spp->spp_params = htonl(fcp_parm | lport->service_params);
222596ad8464SJoe Eykholt return FC_SPP_RESP_ACK;
222696ad8464SJoe Eykholt }
222796ad8464SJoe Eykholt
222896ad8464SJoe Eykholt /*
222996ad8464SJoe Eykholt * FC-4 provider ops for FCP initiator.
223096ad8464SJoe Eykholt */
223196ad8464SJoe Eykholt struct fc4_prov fc_rport_fcp_init = {
223296ad8464SJoe Eykholt .prli = fc_rport_fcp_prli,
223396ad8464SJoe Eykholt };
223496ad8464SJoe Eykholt
223596ad8464SJoe Eykholt /**
223696ad8464SJoe Eykholt * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
223796ad8464SJoe Eykholt * @rdata: remote port private
223896ad8464SJoe Eykholt * @spp_len: service parameter page length
223996ad8464SJoe Eykholt * @rspp: received service parameter page
224096ad8464SJoe Eykholt * @spp: response service parameter page
224196ad8464SJoe Eykholt */
fc_rport_t0_prli(struct fc_rport_priv * rdata,u32 spp_len,const struct fc_els_spp * rspp,struct fc_els_spp * spp)224296ad8464SJoe Eykholt static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
224396ad8464SJoe Eykholt const struct fc_els_spp *rspp,
224496ad8464SJoe Eykholt struct fc_els_spp *spp)
224596ad8464SJoe Eykholt {
224696ad8464SJoe Eykholt if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
224796ad8464SJoe Eykholt return FC_SPP_RESP_INVL;
224896ad8464SJoe Eykholt return FC_SPP_RESP_ACK;
224996ad8464SJoe Eykholt }
225096ad8464SJoe Eykholt
225196ad8464SJoe Eykholt /*
225296ad8464SJoe Eykholt * FC-4 provider ops for type 0 service parameters.
225396ad8464SJoe Eykholt *
225496ad8464SJoe Eykholt * This handles the special case of type 0 which is always successful
225596ad8464SJoe Eykholt * but doesn't do anything otherwise.
225696ad8464SJoe Eykholt */
225796ad8464SJoe Eykholt struct fc4_prov fc_rport_t0_prov = {
225896ad8464SJoe Eykholt .prli = fc_rport_t0_prli,
225996ad8464SJoe Eykholt };
226096ad8464SJoe Eykholt
226196ad8464SJoe Eykholt /**
22623a3b42bfSRobert Love * fc_setup_rport() - Initialize the rport_event_queue
22633a3b42bfSRobert Love */
fc_setup_rport(void)226455204909SRandy Dunlap int fc_setup_rport(void)
226542e9a92fSRobert Love {
226642e9a92fSRobert Love rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
226742e9a92fSRobert Love if (!rport_event_queue)
226842e9a92fSRobert Love return -ENOMEM;
226942e9a92fSRobert Love return 0;
227042e9a92fSRobert Love }
227142e9a92fSRobert Love
22723a3b42bfSRobert Love /**
22733a3b42bfSRobert Love * fc_destroy_rport() - Destroy the rport_event_queue
22743a3b42bfSRobert Love */
fc_destroy_rport(void)227555204909SRandy Dunlap void fc_destroy_rport(void)
227642e9a92fSRobert Love {
227742e9a92fSRobert Love destroy_workqueue(rport_event_queue);
227842e9a92fSRobert Love }
227942e9a92fSRobert Love
22803a3b42bfSRobert Love /**
22813a3b42bfSRobert Love * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port
22823a3b42bfSRobert Love * @rport: The remote port whose I/O should be terminated
22833a3b42bfSRobert Love */
fc_rport_terminate_io(struct fc_rport * rport)228442e9a92fSRobert Love void fc_rport_terminate_io(struct fc_rport *rport)
228542e9a92fSRobert Love {
22863a3b42bfSRobert Love struct fc_rport_libfc_priv *rpriv = rport->dd_data;
22873a3b42bfSRobert Love struct fc_lport *lport = rpriv->local_port;
228842e9a92fSRobert Love
22891f6ff364SAbhijeet Joglekar lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
22901f6ff364SAbhijeet Joglekar lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
229142e9a92fSRobert Love }
229242e9a92fSRobert Love EXPORT_SYMBOL(fc_rport_terminate_io);
2293