summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libfc')
-rw-r--r--drivers/scsi/libfc/fc_disc.c523
-rw-r--r--drivers/scsi/libfc/fc_elsct.c49
-rw-r--r--drivers/scsi/libfc/fc_exch.c515
-rw-r--r--drivers/scsi/libfc/fc_fcp.c31
-rw-r--r--drivers/scsi/libfc/fc_lport.c283
-rw-r--r--drivers/scsi/libfc/fc_rport.c1144
6 files changed, 1361 insertions, 1184 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 6fabf66972b..c48799e9dd8 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -43,47 +43,14 @@
#define FC_DISC_RETRY_LIMIT 3 /* max retries */
#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
-#define FC_DISC_DELAY 3
-
static void fc_disc_gpn_ft_req(struct fc_disc *);
static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
-static int fc_disc_new_target(struct fc_disc *, struct fc_rport *,
- struct fc_rport_identifiers *);
-static void fc_disc_del_target(struct fc_disc *, struct fc_rport *);
-static void fc_disc_done(struct fc_disc *);
+static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
static void fc_disc_timeout(struct work_struct *);
-static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
+static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
static void fc_disc_restart(struct fc_disc *);
/**
- * fc_disc_lookup_rport() - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
- */
-struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
- u32 port_id)
-{
- const struct fc_disc *disc = &lport->disc;
- struct fc_rport *rport, *found = NULL;
- struct fc_rport_libfc_priv *rdata;
- int disc_found = 0;
-
- list_for_each_entry(rdata, &disc->rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- if (rport->port_id == port_id) {
- disc_found = 1;
- found = rport;
- break;
- }
- }
-
- if (!disc_found)
- found = NULL;
-
- return found;
-}
-
-/**
* fc_disc_stop_rports() - delete all the remote ports associated with the lport
* @disc: The discovery job to stop rports on
*
@@ -93,70 +60,17 @@ struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
void fc_disc_stop_rports(struct fc_disc *disc)
{
struct fc_lport *lport;
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata, *next;
+ struct fc_rport_priv *rdata, *next;
lport = disc->lport;
mutex_lock(&disc->disc_mutex);
- list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
- }
-
- list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- lport->tt.rport_logoff(rport);
- }
-
+ list_for_each_entry_safe(rdata, next, &disc->rports, peers)
+ lport->tt.rport_logoff(rdata);
mutex_unlock(&disc->disc_mutex);
}
/**
- * fc_disc_rport_callback() - Event handler for rport events
- * @lport: The lport which is receiving the event
- * @rport: The rport which the event has occured on
- * @event: The event that occured
- *
- * Locking Note: The rport lock should not be held when calling
- * this function.
- */
-static void fc_disc_rport_callback(struct fc_lport *lport,
- struct fc_rport *rport,
- enum fc_rport_event event)
-{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_disc *disc = &lport->disc;
-
- FC_DISC_DBG(disc, "Received a %d event for port (%6x)\n", event,
- rport->port_id);
-
- switch (event) {
- case RPORT_EV_CREATED:
- if (disc) {
- mutex_lock(&disc->disc_mutex);
- list_add_tail(&rdata->peers, &disc->rports);
- mutex_unlock(&disc->disc_mutex);
- }
- break;
- case RPORT_EV_LOGO:
- case RPORT_EV_FAILED:
- case RPORT_EV_STOP:
- mutex_lock(&disc->disc_mutex);
- mutex_lock(&rdata->rp_mutex);
- if (rdata->trans_state == FC_PORTSTATE_ROGUE)
- list_del(&rdata->peers);
- mutex_unlock(&rdata->rp_mutex);
- mutex_unlock(&disc->disc_mutex);
- break;
- default:
- break;
- }
-
-}
-
-/**
* fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
* @sp: Current sequence of the RSCN exchange
* @fp: RSCN Frame
@@ -169,8 +83,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
struct fc_disc *disc)
{
struct fc_lport *lport;
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
struct fc_els_rscn *rp;
struct fc_els_rscn_page *pp;
struct fc_seq_els_data rjt_data;
@@ -224,10 +136,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
break;
}
dp->lp = lport;
- dp->ids.port_id = ntoh24(pp->rscn_fid);
- dp->ids.port_name = -1;
- dp->ids.node_name = -1;
- dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ dp->port_id = ntoh24(pp->rscn_fid);
list_add_tail(&dp->peers, &disc_ports);
break;
case ELS_ADDR_FMT_AREA:
@@ -240,6 +149,19 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
}
}
lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+
+ /*
+ * If not doing a complete rediscovery, do GPN_ID on
+ * the individual ports mentioned in the list.
+ * If any of these get an error, do a full rediscovery.
+ * In any case, go through the list and free the entries.
+ */
+ list_for_each_entry_safe(dp, next, &disc_ports, peers) {
+ list_del(&dp->peers);
+ if (!redisc)
+ redisc = fc_disc_single(lport, dp);
+ kfree(dp);
+ }
if (redisc) {
FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
fc_disc_restart(disc);
@@ -247,16 +169,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
"redisc %d state %d in_prog %d\n",
redisc, lport->state, disc->pending);
- list_for_each_entry_safe(dp, next, &disc_ports, peers) {
- list_del(&dp->peers);
- rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
- if (rport) {
- rdata = rport->dd_data;
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
- }
- fc_disc_single(disc, dp);
- }
}
fc_frame_free(fp);
return;
@@ -308,35 +220,34 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
*/
static void fc_disc_restart(struct fc_disc *disc)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata, *next;
- struct fc_lport *lport = disc->lport;
+ if (!disc->disc_callback)
+ return;
FC_DISC_DBG(disc, "Restarting discovery\n");
- list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
- }
-
disc->requested = 1;
- if (!disc->pending)
- fc_disc_gpn_ft_req(disc);
+ if (disc->pending)
+ return;
+
+ /*
+ * Advance disc_id. This is an arbitrary non-zero number that will
+ * match the value in the fc_rport_priv after discovery for all
+ * freshly-discovered remote ports. Avoid wrapping to zero.
+ */
+ disc->disc_id = (disc->disc_id + 2) | 1;
+ disc->retry_count = 0;
+ fc_disc_gpn_ft_req(disc);
}
/**
* fc_disc_start() - Fibre Channel Target discovery
* @lport: FC local port
- *
- * Returns non-zero if discovery cannot be started.
+ * @disc_callback: function to be called when discovery is complete
*/
static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
enum fc_disc_event),
struct fc_lport *lport)
{
- struct fc_rport *rport;
- struct fc_rport_identifiers ids;
struct fc_disc *disc = &lport->disc;
/*
@@ -345,145 +256,47 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
* and send the GPN_FT request.
*/
mutex_lock(&disc->disc_mutex);
-
disc->disc_callback = disc_callback;
-
- /*
- * If not ready, or already running discovery, just set request flag.
- */
- disc->requested = 1;
-
- if (disc->pending) {
- mutex_unlock(&disc->disc_mutex);
- return;
- }
-
- /*
- * Handle point-to-point mode as a simple discovery
- * of the remote port. Yucky, yucky, yuck, yuck!
- */
- rport = disc->lport->ptp_rp;
- if (rport) {
- ids.port_id = rport->port_id;
- ids.port_name = rport->port_name;
- ids.node_name = rport->node_name;
- ids.roles = FC_RPORT_ROLE_UNKNOWN;
- get_device(&rport->dev);
-
- if (!fc_disc_new_target(disc, rport, &ids)) {
- disc->event = DISC_EV_SUCCESS;
- fc_disc_done(disc);
- }
- put_device(&rport->dev);
- } else {
- fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
- }
-
+ fc_disc_restart(disc);
mutex_unlock(&disc->disc_mutex);
}
-static struct fc_rport_operations fc_disc_rport_ops = {
- .event_callback = fc_disc_rport_callback,
-};
-
-/**
- * fc_disc_new_target() - Handle new target found by discovery
- * @lport: FC local port
- * @rport: The previous FC remote port (NULL if new remote port)
- * @ids: Identifiers for the new FC remote port
- *
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
- */
-static int fc_disc_new_target(struct fc_disc *disc,
- struct fc_rport *rport,
- struct fc_rport_identifiers *ids)
-{
- struct fc_lport *lport = disc->lport;
- struct fc_rport_libfc_priv *rdata;
- int error = 0;
-
- if (rport && ids->port_name) {
- if (rport->port_name == -1) {
- /*
- * Set WWN and fall through to notify of create.
- */
- fc_rport_set_name(rport, ids->port_name,
- rport->node_name);
- } else if (rport->port_name != ids->port_name) {
- /*
- * This is a new port with the same FCID as
- * a previously-discovered port. Presumably the old
- * port logged out and a new port logged in and was
- * assigned the same FCID. This should be rare.
- * Delete the old one and fall thru to re-create.
- */
- fc_disc_del_target(disc, rport);
- rport = NULL;
- }
- }
- if (((ids->port_name != -1) || (ids->port_id != -1)) &&
- ids->port_id != fc_host_port_id(lport->host) &&
- ids->port_name != lport->wwpn) {
- if (!rport) {
- rport = lport->tt.rport_lookup(lport, ids->port_id);
- if (!rport) {
- struct fc_disc_port dp;
- dp.lp = lport;
- dp.ids.port_id = ids->port_id;
- dp.ids.port_name = ids->port_name;
- dp.ids.node_name = ids->node_name;
- dp.ids.roles = ids->roles;
- rport = lport->tt.rport_create(&dp);
- }
- if (!rport)
- error = -ENOMEM;
- }
- if (rport) {
- rdata = rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- rdata->rp_state = RPORT_ST_INIT;
- list_add_tail(&rdata->peers, &disc->rogue_rports);
- lport->tt.rport_login(rport);
- }
- }
- return error;
-}
-
-/**
- * fc_disc_del_target() - Delete a target
- * @disc: FC discovery context
- * @rport: The remote port to be removed
- */
-static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
-{
- struct fc_lport *lport = disc->lport;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
-}
-
/**
* fc_disc_done() - Discovery has been completed
* @disc: FC discovery context
+ * @event: discovery completion status
+ *
* Locking Note: This function expects that the disc mutex is locked before
* it is called. The discovery callback is then made with the lock released,
* and the lock is re-taken before returning from this function
*/
-static void fc_disc_done(struct fc_disc *disc)
+static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
{
struct fc_lport *lport = disc->lport;
- enum fc_disc_event event;
+ struct fc_rport_priv *rdata;
FC_DISC_DBG(disc, "Discovery complete\n");
- event = disc->event;
- disc->event = DISC_EV_NONE;
+ disc->pending = 0;
+ if (disc->requested) {
+ fc_disc_restart(disc);
+ return;
+ }
- if (disc->requested)
- fc_disc_gpn_ft_req(disc);
- else
- disc->pending = 0;
+ /*
+ * Go through all remote ports. If they were found in the latest
+ * discovery, reverify or log them in. Otherwise, log them out.
+ * Skip ports which were never discovered. These are the dNS port
+ * and ports which were created by PLOGI.
+ */
+ list_for_each_entry(rdata, &disc->rports, peers) {
+ if (!rdata->disc_id)
+ continue;
+ if (rdata->disc_id == disc->disc_id)
+ lport->tt.rport_login(rdata);
+ else
+ lport->tt.rport_logoff(rdata);
+ }
mutex_unlock(&disc->disc_mutex);
disc->disc_callback(lport, event);
@@ -522,11 +335,8 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
}
disc->retry_count++;
schedule_delayed_work(&disc->disc_work, delay);
- } else {
- /* exceeded retries */
- disc->event = DISC_EV_FAILED;
- fc_disc_done(disc);
- }
+ } else
+ fc_disc_done(disc, DISC_EV_FAILED);
}
}
@@ -555,7 +365,7 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
if (!fp)
goto err;
- if (lport->tt.elsct_send(lport, NULL, fp,
+ if (lport->tt.elsct_send(lport, 0, fp,
FC_NS_GPN_FT,
fc_disc_gpn_ft_resp,
disc, lport->e_d_tov))
@@ -565,10 +375,12 @@ err:
}
/**
- * fc_disc_gpn_ft_parse() - Parse the list of IDs and names resulting from a request
+ * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
* @lport: Fibre Channel host port instance
* @buf: GPN_FT response buffer
* @len: size of response buffer
+ *
+ * Goes through the list of IDs and names resulting from a request.
*/
static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
{
@@ -578,11 +390,11 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
size_t plen;
size_t tlen;
int error = 0;
- struct fc_disc_port dp;
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
+ struct fc_rport_identifiers ids;
+ struct fc_rport_priv *rdata;
lport = disc->lport;
+ disc->seq_count++;
/*
* Handle partial name record left over from previous call.
@@ -591,6 +403,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
plen = len;
np = (struct fc_gpn_ft_resp *)bp;
tlen = disc->buf_len;
+ disc->buf_len = 0;
if (tlen) {
WARN_ON(tlen >= sizeof(*np));
plen = sizeof(*np) - tlen;
@@ -621,31 +434,25 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
* After the first time through the loop, things return to "normal".
*/
while (plen >= sizeof(*np)) {
- dp.lp = lport;
- dp.ids.port_id = ntoh24(np->fp_fid);
- dp.ids.port_name = ntohll(np->fp_wwpn);
- dp.ids.node_name = -1;
- dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
- if ((dp.ids.port_id != fc_host_port_id(lport->host)) &&
- (dp.ids.port_name != lport->wwpn)) {
- rport = lport->tt.rport_create(&dp);
- if (rport) {
- rdata = rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- rdata->local_port = lport;
- list_add_tail(&rdata->peers,
- &disc->rogue_rports);
- lport->tt.rport_login(rport);
- } else
+ ids.port_id = ntoh24(np->fp_fid);
+ ids.port_name = ntohll(np->fp_wwpn);
+
+ if (ids.port_id != fc_host_port_id(lport->host) &&
+ ids.port_name != lport->wwpn) {
+ rdata = lport->tt.rport_create(lport, ids.port_id);
+ if (rdata) {
+ rdata->ids.port_name = ids.port_name;
+ rdata->disc_id = disc->disc_id;
+ } else {
printk(KERN_WARNING "libfc: Failed to allocate "
"memory for the newly discovered port "
- "(%6x)\n", dp.ids.port_id);
+ "(%6x)\n", ids.port_id);
+ error = -ENOMEM;
+ }
}
if (np->fp_flags & FC_NS_FID_LAST) {
- disc->event = DISC_EV_SUCCESS;
- fc_disc_done(disc);
+ fc_disc_done(disc, DISC_EV_SUCCESS);
len = 0;
break;
}
@@ -665,8 +472,6 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
memcpy(&disc->partial_buf, np, len);
}
disc->buf_len = (unsigned char) len;
- } else {
- disc->buf_len = 0;
}
return error;
}
@@ -683,8 +488,7 @@ static void fc_disc_timeout(struct work_struct *work)
struct fc_disc,
disc_work.work);
mutex_lock(&disc->disc_mutex);
- if (disc->requested && !disc->pending)
- fc_disc_gpn_ft_req(disc);
+ fc_disc_gpn_ft_req(disc);
mutex_unlock(&disc->disc_mutex);
}
@@ -703,10 +507,10 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_disc *disc = disc_arg;
struct fc_ct_hdr *cp;
struct fc_frame_header *fh;
+ enum fc_disc_event event = DISC_EV_NONE;
unsigned int seq_cnt;
- void *buf = NULL;
unsigned int len;
- int error;
+ int error = 0;
mutex_lock(&disc->disc_mutex);
FC_DISC_DBG(disc, "Received a GPN_FT response\n");
@@ -721,77 +525,158 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
fh = fc_frame_header_get(fp);
len = fr_len(fp) - sizeof(*fh);
seq_cnt = ntohs(fh->fh_seq_cnt);
- if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
- disc->seq_count == 0) {
+ if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
cp = fc_frame_payload_get(fp, sizeof(*cp));
if (!cp) {
FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
fr_len(fp));
+ event = DISC_EV_FAILED;
} else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
/* Accepted, parse the response. */
- buf = cp + 1;
len -= sizeof(*cp);
+ error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
"(check zoning)\n", cp->ct_reason,
cp->ct_explan);
- disc->event = DISC_EV_FAILED;
- fc_disc_done(disc);
+ event = DISC_EV_FAILED;
+ if (cp->ct_reason == FC_FS_RJT_UNABL &&
+ cp->ct_explan == FC_FS_EXP_FTNR)
+ event = DISC_EV_SUCCESS;
} else {
FC_DISC_DBG(disc, "GPN_FT unexpected response code "
"%x\n", ntohs(cp->ct_cmd));
+ event = DISC_EV_FAILED;
}
- } else if (fr_sof(fp) == FC_SOF_N3 &&
- seq_cnt == disc->seq_count) {
- buf = fh + 1;
+ } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
+ error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
} else {
FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
"seq_cnt %x expected %x sof %x eof %x\n",
seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
+ event = DISC_EV_FAILED;
}
- if (buf) {
- error = fc_disc_gpn_ft_parse(disc, buf, len);
- if (error)
- fc_disc_error(disc, fp);
- else
- disc->seq_count++;
- }
+ if (error)
+ fc_disc_error(disc, fp);
+ else if (event != DISC_EV_NONE)
+ fc_disc_done(disc, event);
fc_frame_free(fp);
-
mutex_unlock(&disc->disc_mutex);
}
/**
- * fc_disc_single() - Discover the directory information for a single target
- * @lport: FC local port
- * @dp: The port to rediscover
+ * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
+ * @sp: exchange sequence
+ * @fp: response frame
+ * @rdata_arg: remote port private data
*
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
+ * Locking Note: This function is called without disc mutex held.
*/
-static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
+static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rdata_arg)
{
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_rport_priv *new_rdata;
struct fc_lport *lport;
- struct fc_rport *new_rport;
- struct fc_rport_libfc_priv *rdata;
+ struct fc_disc *disc;
+ struct fc_ct_hdr *cp;
+ struct fc_ns_gid_pn *pn;
+ u64 port_name;
- lport = disc->lport;
+ lport = rdata->local_port;
+ disc = &lport->disc;
- if (dp->ids.port_id == fc_host_port_id(lport->host))
+ mutex_lock(&disc->disc_mutex);
+ if (PTR_ERR(fp) == -FC_EX_CLOSED)
goto out;
-
- new_rport = lport->tt.rport_create(dp);
- if (new_rport) {
- rdata = new_rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- kfree(dp);
- list_add_tail(&rdata->peers, &disc->rogue_rports);
- lport->tt.rport_login(new_rport);
+ if (IS_ERR(fp))
+ goto redisc;
+
+ cp = fc_frame_payload_get(fp, sizeof(*cp));
+ if (!cp)
+ goto redisc;
+ if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
+ if (fr_len(fp) < sizeof(struct fc_frame_header) +
+ sizeof(*cp) + sizeof(*pn))
+ goto redisc;
+ pn = (struct fc_ns_gid_pn *)(cp + 1);
+ port_name = get_unaligned_be64(&pn->fn_wwpn);
+ if (rdata->ids.port_name == -1)
+ rdata->ids.port_name = port_name;
+ else if (rdata->ids.port_name != port_name) {
+ FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
+ "Port-id %x wwpn %llx\n",
+ rdata->ids.port_id, port_name);
+ lport->tt.rport_logoff(rdata);
+
+ new_rdata = lport->tt.rport_create(lport,
+ rdata->ids.port_id);
+ if (new_rdata) {
+ new_rdata->disc_id = disc->disc_id;
+ lport->tt.rport_login(new_rdata);
+ }
+ goto out;
+ }
+ rdata->disc_id = disc->disc_id;
+ lport->tt.rport_login(rdata);
+ } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
+ FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
+ cp->ct_reason, cp->ct_explan);
+ lport->tt.rport_logoff(rdata);
+ } else {
+ FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
+ ntohs(cp->ct_cmd));
+redisc:
+ fc_disc_restart(disc);
}
- return;
out:
- kfree(dp);
+ mutex_unlock(&disc->disc_mutex);
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+}
+
+/**
+ * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
+ * @lport: local port
+ * @rdata: remote port private data
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ * On failure, an error code is returned.
+ */
+static int fc_disc_gpn_id_req(struct fc_lport *lport,
+ struct fc_rport_priv *rdata)
+{
+ struct fc_frame *fp;
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_fid));
+ if (!fp)
+ return -ENOMEM;
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
+ fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
+ return -ENOMEM;
+ kref_get(&rdata->kref);
+ return 0;
+}
+
+/**
+ * fc_disc_single() - Discover the directory information for a single target
+ * @lport: local port
+ * @dp: The port to rediscover
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
+{
+ struct fc_rport_priv *rdata;
+
+ rdata = lport->tt.rport_create(lport, dp->port_id);
+ if (!rdata)
+ return -ENOMEM;
+ rdata->disc_id = 0;
+ return fc_disc_gpn_id_req(lport, rdata);
}
/**
@@ -841,18 +726,12 @@ int fc_disc_init(struct fc_lport *lport)
if (!lport->tt.disc_recv_req)
lport->tt.disc_recv_req = fc_disc_recv_req;
- if (!lport->tt.rport_lookup)
- lport->tt.rport_lookup = fc_disc_lookup_rport;
-
disc = &lport->disc;
INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
mutex_init(&disc->disc_mutex);
INIT_LIST_HEAD(&disc->rports);
- INIT_LIST_HEAD(&disc->rogue_rports);
disc->lport = lport;
- disc->delay = FC_DISC_DELAY;
- disc->event = DISC_EV_NONE;
return 0;
}
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index 5878b34bff1..5cfa68732e9 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -32,7 +32,7 @@
* fc_elsct_send - sends ELS/CT frame
*/
static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
- struct fc_rport *rport,
+ u32 did,
struct fc_frame *fp,
unsigned int op,
void (*resp)(struct fc_seq *,
@@ -41,16 +41,17 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
void *arg, u32 timer_msec)
{
enum fc_rctl r_ctl;
- u32 did = FC_FID_NONE;
enum fc_fh_type fh_type;
int rc;
/* ELS requests */
if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS))
- rc = fc_els_fill(lport, rport, fp, op, &r_ctl, &did, &fh_type);
- else
+ rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);
+ else {
/* CT requests */
- rc = fc_ct_fill(lport, fp, op, &r_ctl, &did, &fh_type);
+ rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type);
+ did = FC_FID_DIR_SERV;
+ }
if (rc)
return NULL;
@@ -69,3 +70,41 @@ int fc_elsct_init(struct fc_lport *lport)
return 0;
}
EXPORT_SYMBOL(fc_elsct_init);
+
+/**
+ * fc_els_resp_type() - return string describing ELS response for debug.
+ * @fp: frame pointer with possible error code.
+ */
+const char *fc_els_resp_type(struct fc_frame *fp)
+{
+ const char *msg;
+ if (IS_ERR(fp)) {
+ switch (-PTR_ERR(fp)) {
+ case FC_NO_ERR:
+ msg = "response no error";
+ break;
+ case FC_EX_TIMEOUT:
+ msg = "response timeout";
+ break;
+ case FC_EX_CLOSED:
+ msg = "response closed";
+ break;
+ default:
+ msg = "response unknown error";
+ break;
+ }
+ } else {
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LS_ACC:
+ msg = "accept";
+ break;
+ case ELS_LS_RJT:
+ msg = "reject";
+ break;
+ default:
+ msg = "response unknown ELS";
+ break;
+ }
+ }
+ return msg;
+}
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 145ab9ba55e..c1c15748220 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -32,6 +32,9 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+u16 fc_cpu_mask; /* cpu mask for possible cpus */
+EXPORT_SYMBOL(fc_cpu_mask);
+static u16 fc_cpu_order; /* 2's power to represent total possible cpus */
static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
/*
@@ -48,6 +51,20 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
*/
/*
+ * Per cpu exchange pool
+ *
+ * This structure manages per cpu exchanges in array of exchange pointers.
+ * This array is allocated followed by struct fc_exch_pool memory for
+ * assigned range of exchanges to per cpu pool.
+ */
+struct fc_exch_pool {
+ u16 next_index; /* next possible free exchange index */
+ u16 total_exches; /* total allocated exchanges */
+ spinlock_t lock; /* exch pool lock */
+ struct list_head ex_list; /* allocated exchanges list */
+};
+
+/*
* Exchange manager.
*
* This structure is the center for creating exchanges and sequences.
@@ -55,17 +72,13 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
*/
struct fc_exch_mgr {
enum fc_class class; /* default class for sequences */
- spinlock_t em_lock; /* exchange manager lock,
- must be taken before ex_lock */
- u16 last_xid; /* last allocated exchange ID */
+ struct kref kref; /* exchange mgr reference count */
u16 min_xid; /* min exchange ID */
u16 max_xid; /* max exchange ID */
- u16 max_read; /* max exchange ID for read */
- u16 last_read; /* last xid allocated for read */
- u32 total_exches; /* total allocated exchanges */
struct list_head ex_list; /* allocated exchanges list */
- struct fc_lport *lp; /* fc device instance */
mempool_t *ep_pool; /* reserve ep's */
+ u16 pool_max_index; /* max exch array index in exch pool */
+ struct fc_exch_pool *pool; /* per cpu exch pool */
/*
* currently exchange mgr stats are updated but not used.
@@ -80,10 +93,15 @@ struct fc_exch_mgr {
atomic_t seq_not_found;
atomic_t non_bls_resp;
} stats;
- struct fc_exch **exches; /* for exch pointers indexed by xid */
};
#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+struct fc_exch_mgr_anchor {
+ struct list_head ema_list;
+ struct fc_exch_mgr *mp;
+ bool (*match)(struct fc_frame *);
+};
+
static void fc_exch_rrq(struct fc_exch *);
static void fc_seq_ls_acc(struct fc_seq *);
static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
@@ -167,8 +185,8 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
* sequence allocation and deallocation must be locked.
* - exchange refcnt can be done atomicly without locks.
* - sequence allocation must be locked by exch lock.
- * - If the em_lock and ex_lock must be taken at the same time, then the
- * em_lock must be taken before the ex_lock.
+ * - If the EM pool lock and ex_lock must be taken at the same time, then the
+ * EM pool lock must be taken before the ex_lock.
*/
/*
@@ -268,8 +286,6 @@ static void fc_exch_release(struct fc_exch *ep)
mp = ep->em;
if (ep->destructor)
ep->destructor(&ep->seq, ep->arg);
- if (ep->lp->tt.exch_put)
- ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
WARN_ON(!(ep->esb_stat & ESB_ST_COMPLETE));
mempool_free(ep, mp->ep_pool);
}
@@ -299,17 +315,31 @@ static int fc_exch_done_locked(struct fc_exch *ep)
return rc;
}
-static void fc_exch_mgr_delete_ep(struct fc_exch *ep)
+static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
+ u16 index)
{
- struct fc_exch_mgr *mp;
+ struct fc_exch **exches = (struct fc_exch **)(pool + 1);
+ return exches[index];
+}
- mp = ep->em;
- spin_lock_bh(&mp->em_lock);
- WARN_ON(mp->total_exches <= 0);
- mp->total_exches--;
- mp->exches[ep->xid - mp->min_xid] = NULL;
+static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
+ struct fc_exch *ep)
+{
+ ((struct fc_exch **)(pool + 1))[index] = ep;
+}
+
+static void fc_exch_delete(struct fc_exch *ep)
+{
+ struct fc_exch_pool *pool;
+
+ pool = ep->pool;
+ spin_lock_bh(&pool->lock);
+ WARN_ON(pool->total_exches <= 0);
+ pool->total_exches--;
+ fc_exch_ptr_set(pool, (ep->xid - ep->em->min_xid) >> fc_cpu_order,
+ NULL);
list_del(&ep->ex_list);
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
fc_exch_release(ep); /* drop hold for exch in mp */
}
@@ -322,7 +352,7 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
return;
- FC_EXCH_DBG(ep, "Exchange timed out, notifying the upper layer\n");
+ FC_EXCH_DBG(ep, "Exchange timer armed\n");
if (schedule_delayed_work(&ep->timeout_work,
msecs_to_jiffies(timer_msec)))
@@ -408,6 +438,8 @@ static void fc_exch_timeout(struct work_struct *work)
u32 e_stat;
int rc = 1;
+ FC_EXCH_DBG(ep, "Exchange timed out\n");
+
spin_lock_bh(&ep->ex_lock);
if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
goto unlock;
@@ -427,7 +459,7 @@ static void fc_exch_timeout(struct work_struct *work)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
if (resp)
resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
@@ -460,65 +492,20 @@ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
return sp;
}
-/*
- * fc_em_alloc_xid - returns an xid based on request type
- * @lp : ptr to associated lport
- * @fp : ptr to the assocated frame
+/**
+ * fc_exch_em_alloc() - allocate an exchange from a specified EM.
+ * @lport: ptr to the local port
+ * @mp: ptr to the exchange manager
*
- * check the associated fc_fsp_pkt to get scsi command type and
- * command direction to decide from which range this exch id
- * will be allocated from.
- *
- * Returns : 0 or an valid xid
+ * Returns pointer to allocated fc_exch with exch lock held.
*/
-static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
-{
- u16 xid, min, max;
- u16 *plast;
- struct fc_exch *ep = NULL;
-
- if (mp->max_read) {
- if (fc_fcp_is_read(fr_fsp(fp))) {
- min = mp->min_xid;
- max = mp->max_read;
- plast = &mp->last_read;
- } else {
- min = mp->max_read + 1;
- max = mp->max_xid;
- plast = &mp->last_xid;
- }
- } else {
- min = mp->min_xid;
- max = mp->max_xid;
- plast = &mp->last_xid;
- }
- xid = *plast;
- do {
- xid = (xid == max) ? min : xid + 1;
- ep = mp->exches[xid - mp->min_xid];
- } while ((ep != NULL) && (xid != *plast));
-
- if (unlikely(ep))
- xid = 0;
- else
- *plast = xid;
-
- return xid;
-}
-
-/*
- * fc_exch_alloc - allocate an exchange.
- * @mp : ptr to the exchange manager
- * @xid: input xid
- *
- * if xid is supplied zero then assign next free exchange ID
- * from exchange manager, otherwise use supplied xid.
- * Returns with exch lock held.
- */
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
- struct fc_frame *fp, u16 xid)
+static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
+ struct fc_exch_mgr *mp)
{
struct fc_exch *ep;
+ unsigned int cpu;
+ u16 index;
+ struct fc_exch_pool *pool;
/* allocate memory for exchange */
ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
@@ -528,16 +515,17 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
}
memset(ep, 0, sizeof(*ep));
- spin_lock_bh(&mp->em_lock);
- /* alloc xid if input xid 0 */
- if (!xid) {
- /* alloc a new xid */
- xid = fc_em_alloc_xid(mp, fp);
- if (!xid) {
- printk(KERN_WARNING "libfc: Failed to allocate an exhange\n");
+ cpu = smp_processor_id();
+ pool = per_cpu_ptr(mp->pool, cpu);
+ spin_lock_bh(&pool->lock);
+ index = pool->next_index;
+ /* allocate new exch from pool */
+ while (fc_exch_ptr_get(pool, index)) {
+ index = index == mp->pool_max_index ? 0 : index + 1;
+ if (index == pool->next_index)
goto err;
- }
}
+ pool->next_index = index == mp->pool_max_index ? 0 : index + 1;
fc_exch_hold(ep); /* hold for exch in mp */
spin_lock_init(&ep->ex_lock);
@@ -548,18 +536,19 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
*/
spin_lock_bh(&ep->ex_lock);
- mp->exches[xid - mp->min_xid] = ep;
- list_add_tail(&ep->ex_list, &mp->ex_list);
+ fc_exch_ptr_set(pool, index, ep);
+ list_add_tail(&ep->ex_list, &pool->ex_list);
fc_seq_alloc(ep, ep->seq_id++);
- mp->total_exches++;
- spin_unlock_bh(&mp->em_lock);
+ pool->total_exches++;
+ spin_unlock_bh(&pool->lock);
/*
* update exchange
*/
- ep->oxid = ep->xid = xid;
+ ep->oxid = ep->xid = (index << fc_cpu_order | cpu) + mp->min_xid;
ep->em = mp;
- ep->lp = mp->lp;
+ ep->pool = pool;
+ ep->lp = lport;
ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
ep->rxid = FC_XID_UNKNOWN;
ep->class = mp->class;
@@ -567,11 +556,36 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
out:
return ep;
err:
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
atomic_inc(&mp->stats.no_free_exch_xid);
mempool_free(ep, mp->ep_pool);
return NULL;
}
+
+/**
+ * fc_exch_alloc() - allocate an exchange.
+ * @lport: ptr to the local port
+ * @fp: ptr to the FC frame
+ *
+ * This function walks the list of the exchange manager(EM)
+ * anchors to select a EM for new exchange allocation. The
+ * EM is selected having either a NULL match function pointer
+ * or call to match function returning true.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_exch_mgr_anchor *ema;
+ struct fc_exch *ep;
+
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
+ if (!ema->match || ema->match(fp)) {
+ ep = fc_exch_em_alloc(lport, ema->mp);
+ if (ep)
+ return ep;
+ }
+ }
+ return NULL;
+}
EXPORT_SYMBOL(fc_exch_alloc);
/*
@@ -579,16 +593,18 @@ EXPORT_SYMBOL(fc_exch_alloc);
*/
static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
{
+ struct fc_exch_pool *pool;
struct fc_exch *ep = NULL;
if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) {
- spin_lock_bh(&mp->em_lock);
- ep = mp->exches[xid - mp->min_xid];
+ pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask);
+ spin_lock_bh(&pool->lock);
+ ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);
if (ep) {
fc_exch_hold(ep);
WARN_ON(ep->xid != xid);
}
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
}
return ep;
}
@@ -602,7 +618,7 @@ void fc_exch_done(struct fc_seq *sp)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
}
EXPORT_SYMBOL(fc_exch_done);
@@ -610,12 +626,14 @@ EXPORT_SYMBOL(fc_exch_done);
* Allocate a new exchange as responder.
* Sets the responder ID in the frame header.
*/
-static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
{
struct fc_exch *ep;
struct fc_frame_header *fh;
- ep = mp->lp->tt.exch_get(mp->lp, fp);
+ ep = fc_exch_alloc(lport, fp);
if (ep) {
ep->class = fc_frame_class(fp);
@@ -641,7 +659,7 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
ep->esb_stat &= ~ESB_ST_SEQ_INIT;
fc_exch_hold(ep); /* hold for caller */
- spin_unlock_bh(&ep->ex_lock); /* lock from exch_get */
+ spin_unlock_bh(&ep->ex_lock); /* lock from fc_exch_alloc */
}
return ep;
}
@@ -651,7 +669,8 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
* If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
* on the ep that should be released by the caller.
*/
-static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
+static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
@@ -705,7 +724,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
reject = FC_RJT_RX_ID;
goto rel;
}
- ep = fc_exch_resp(mp, fp);
+ ep = fc_exch_resp(lport, mp, fp);
if (!ep) {
reject = FC_RJT_EXCH_EST; /* XXX */
goto out;
@@ -822,7 +841,6 @@ struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
struct fc_exch *ep = fc_seq_exch(sp);
spin_lock_bh(&ep->ex_lock);
- WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0);
sp = fc_seq_start_next_locked(sp);
spin_unlock_bh(&ep->ex_lock);
@@ -999,8 +1017,8 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
*/
memcpy(fh->fh_s_id, rx_fh->fh_d_id, 3);
memcpy(fh->fh_d_id, rx_fh->fh_s_id, 3);
- fh->fh_ox_id = rx_fh->fh_rx_id;
- fh->fh_rx_id = rx_fh->fh_ox_id;
+ fh->fh_ox_id = rx_fh->fh_ox_id;
+ fh->fh_rx_id = rx_fh->fh_rx_id;
fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
fh->fh_r_ctl = FC_RCTL_BA_RJT;
fh->fh_type = FC_TYPE_BLS;
@@ -1097,7 +1115,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
enum fc_pf_rjt_reason reject;
fr_seq(fp) = NULL;
- reject = fc_seq_lookup_recip(mp, fp);
+ reject = fc_seq_lookup_recip(lp, mp, fp);
if (reject == FC_RJT_NONE) {
sp = fr_seq(fp); /* sequence will be held */
ep = fc_seq_exch(sp);
@@ -1123,7 +1141,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
lp->tt.lport_recv(lp, sp, fp);
fc_exch_release(ep); /* release from lookup */
} else {
- FC_EM_DBG(mp, "exch/seq lookup failed: reject %x\n", reject);
+ FC_LPORT_DBG(lp, "exch/seq lookup failed: reject %x\n", reject);
fc_frame_free(fp);
}
}
@@ -1193,7 +1211,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
WARN_ON(fc_seq_exch(sp) != ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
}
/*
@@ -1229,13 +1247,12 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
struct fc_seq *sp;
sp = fc_seq_lookup_orig(mp, fp); /* doesn't hold sequence */
- if (!sp) {
+
+ if (!sp)
atomic_inc(&mp->stats.xid_not_found);
- FC_EM_DBG(mp, "seq lookup failed\n");
- } else {
+ else
atomic_inc(&mp->stats.non_bls_resp);
- FC_EM_DBG(mp, "non-BLS response to sequence");
- }
+
fc_frame_free(fp);
}
@@ -1304,7 +1321,7 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
if (resp)
resp(sp, fp, ex_resp_arg);
@@ -1447,44 +1464,77 @@ static void fc_exch_reset(struct fc_exch *ep)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
if (resp)
resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
}
-/*
- * Reset an exchange manager, releasing all sequences and exchanges.
- * If sid is non-zero, reset only exchanges we source from that FID.
- * If did is non-zero, reset only exchanges destined to that FID.
+/**
+ * fc_exch_pool_reset() - Resets an per cpu exches pool.
+ * @lport: ptr to the local port
+ * @pool: ptr to the per cpu exches pool
+ * @sid: source FC ID
+ * @did: destination FC ID
+ *
+ * Resets an per cpu exches pool, releasing its all sequences
+ * and exchanges. If sid is non-zero, then reset only exchanges
+ * we sourced from that FID. If did is non-zero, reset only
+ * exchanges destined to that FID.
*/
-void fc_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
+static void fc_exch_pool_reset(struct fc_lport *lport,
+ struct fc_exch_pool *pool,
+ u32 sid, u32 did)
{
struct fc_exch *ep;
struct fc_exch *next;
- struct fc_exch_mgr *mp = lp->emp;
- spin_lock_bh(&mp->em_lock);
+ spin_lock_bh(&pool->lock);
restart:
- list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) {
- if ((sid == 0 || sid == ep->sid) &&
+ list_for_each_entry_safe(ep, next, &pool->ex_list, ex_list) {
+ if ((lport == ep->lp) &&
+ (sid == 0 || sid == ep->sid) &&
(did == 0 || did == ep->did)) {
fc_exch_hold(ep);
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
fc_exch_reset(ep);
fc_exch_release(ep);
- spin_lock_bh(&mp->em_lock);
+ spin_lock_bh(&pool->lock);
/*
- * must restart loop incase while lock was down
- * multiple eps were released.
+ * must restart loop incase while lock
+ * was down multiple eps were released.
*/
goto restart;
}
}
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
+}
+
+/**
+ * fc_exch_mgr_reset() - Resets all EMs of a lport
+ * @lport: ptr to the local port
+ * @sid: source FC ID
+ * @did: destination FC ID
+ *
+ * Reset all EMs of a lport, releasing its all sequences and
+ * exchanges. If sid is non-zero, then reset only exchanges
+ * we sourced from that FID. If did is non-zero, reset only
+ * exchanges destined to that FID.
+ */
+void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
+{
+ struct fc_exch_mgr_anchor *ema;
+ unsigned int cpu;
+
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
+ for_each_possible_cpu(cpu)
+ fc_exch_pool_reset(lport,
+ per_cpu_ptr(ema->mp->pool, cpu),
+ sid, did);
+ }
}
EXPORT_SYMBOL(fc_exch_mgr_reset);
@@ -1730,85 +1780,129 @@ reject:
fc_frame_free(fp);
}
+struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
+ bool (*match)(struct fc_frame *))
+{
+ struct fc_exch_mgr_anchor *ema;
+
+ ema = kmalloc(sizeof(*ema), GFP_ATOMIC);
+ if (!ema)
+ return ema;
+
+ ema->mp = mp;
+ ema->match = match;
+ /* add EM anchor to EM anchors list */
+ list_add_tail(&ema->ema_list, &lport->ema_list);
+ kref_get(&mp->kref);
+ return ema;
+}
+EXPORT_SYMBOL(fc_exch_mgr_add);
+
+static void fc_exch_mgr_destroy(struct kref *kref)
+{
+ struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
+
+ mempool_destroy(mp->ep_pool);
+ free_percpu(mp->pool);
+ kfree(mp);
+}
+
+void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
+{
+ /* remove EM anchor from EM anchors list */
+ list_del(&ema->ema_list);
+ kref_put(&ema->mp->kref, fc_exch_mgr_destroy);
+ kfree(ema);
+}
+EXPORT_SYMBOL(fc_exch_mgr_del);
+
struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
enum fc_class class,
- u16 min_xid, u16 max_xid)
+ u16 min_xid, u16 max_xid,
+ bool (*match)(struct fc_frame *))
{
struct fc_exch_mgr *mp;
- size_t len;
+ u16 pool_exch_range;
+ size_t pool_size;
+ unsigned int cpu;
+ struct fc_exch_pool *pool;
- if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) {
+ if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN ||
+ (min_xid & fc_cpu_mask) != 0) {
FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
min_xid, max_xid);
return NULL;
}
/*
- * Memory need for EM
+ * allocate memory for EM
*/
-#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2)))
- len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *));
- len += sizeof(struct fc_exch_mgr);
-
- mp = kzalloc(len, GFP_ATOMIC);
+ mp = kzalloc(sizeof(struct fc_exch_mgr), GFP_ATOMIC);
if (!mp)
return NULL;
mp->class = class;
- mp->total_exches = 0;
- mp->exches = (struct fc_exch **)(mp + 1);
- mp->lp = lp;
/* adjust em exch xid range for offload */
mp->min_xid = min_xid;
mp->max_xid = max_xid;
- mp->last_xid = min_xid - 1;
- mp->max_read = 0;
- mp->last_read = 0;
- if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) {
- mp->max_read = lp->lro_xid;
- mp->last_read = min_xid - 1;
- mp->last_xid = mp->max_read;
- } else {
- /* disable lro if no xid control over read */
- lp->lro_enabled = 0;
- }
-
- INIT_LIST_HEAD(&mp->ex_list);
- spin_lock_init(&mp->em_lock);
mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
if (!mp->ep_pool)
goto free_mp;
+ /*
+ * Setup per cpu exch pool with entire exchange id range equally
+ * divided across all cpus. The exch pointers array memory is
+ * allocated for exch range per pool.
+ */
+ pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);
+ mp->pool_max_index = pool_exch_range - 1;
+
+ /*
+ * Allocate and initialize per cpu exch pool
+ */
+ pool_size = sizeof(*pool) + pool_exch_range * sizeof(struct fc_exch *);
+ mp->pool = __alloc_percpu(pool_size, __alignof__(struct fc_exch_pool));
+ if (!mp->pool)
+ goto free_mempool;
+ for_each_possible_cpu(cpu) {
+ pool = per_cpu_ptr(mp->pool, cpu);
+ spin_lock_init(&pool->lock);
+ INIT_LIST_HEAD(&pool->ex_list);
+ }
+
+ kref_init(&mp->kref);
+ if (!fc_exch_mgr_add(lp, mp, match)) {
+ free_percpu(mp->pool);
+ goto free_mempool;
+ }
+
+ /*
+ * Above kref_init() sets mp->kref to 1 and then
+ * call to fc_exch_mgr_add incremented mp->kref again,
+ * so adjust that extra increment.
+ */
+ kref_put(&mp->kref, fc_exch_mgr_destroy);
return mp;
+free_mempool:
+ mempool_destroy(mp->ep_pool);
free_mp:
kfree(mp);
return NULL;
}
EXPORT_SYMBOL(fc_exch_mgr_alloc);
-void fc_exch_mgr_free(struct fc_exch_mgr *mp)
+void fc_exch_mgr_free(struct fc_lport *lport)
{
- WARN_ON(!mp);
- /*
- * The total exch count must be zero
- * before freeing exchange manager.
- */
- WARN_ON(mp->total_exches != 0);
- mempool_destroy(mp->ep_pool);
- kfree(mp);
+ struct fc_exch_mgr_anchor *ema, *next;
+
+ list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list)
+ fc_exch_mgr_del(ema);
}
EXPORT_SYMBOL(fc_exch_mgr_free);
-struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp)
-{
- if (!lp || !lp->emp)
- return NULL;
-
- return fc_exch_alloc(lp->emp, fp, 0);
-}
-EXPORT_SYMBOL(fc_exch_get);
struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
struct fc_frame *fp,
@@ -1823,7 +1917,7 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
struct fc_frame_header *fh;
int rc = 1;
- ep = lp->tt.exch_get(lp, fp);
+ ep = fc_exch_alloc(lp, fp);
if (!ep) {
fc_frame_free(fp);
return NULL;
@@ -1843,7 +1937,8 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
fc_exch_setup_hdr(ep, fp, ep->f_ctl);
sp->cnt++;
- fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+ if (ep->xid <= lp->lro_xid)
+ fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
if (unlikely(lp->tt.frame_send(lp, fp)))
goto err;
@@ -1860,7 +1955,7 @@ err:
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
return NULL;
}
EXPORT_SYMBOL(fc_exch_seq_send);
@@ -1868,24 +1963,44 @@ EXPORT_SYMBOL(fc_exch_seq_send);
/*
* Receive a frame
*/
-void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
- struct fc_frame *fp)
+void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
- u32 f_ctl;
+ struct fc_exch_mgr_anchor *ema;
+ u32 f_ctl, found = 0;
+ u16 oxid;
/* lport lock ? */
- if (!lp || !mp || (lp->state == LPORT_ST_NONE)) {
+ if (!lp || lp->state == LPORT_ST_DISABLED) {
FC_LPORT_DBG(lp, "Receiving frames for an lport that "
"has not been initialized correctly\n");
fc_frame_free(fp);
return;
}
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ oxid = ntohs(fh->fh_ox_id);
+ if (f_ctl & FC_FC_EX_CTX) {
+ list_for_each_entry(ema, &lp->ema_list, ema_list) {
+ if ((oxid >= ema->mp->min_xid) &&
+ (oxid <= ema->mp->max_xid)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ FC_LPORT_DBG(lp, "Received response for out "
+ "of range oxid:%hx\n", oxid);
+ fc_frame_free(fp);
+ return;
+ }
+ } else
+ ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list);
+
/*
* If frame is marked invalid, just drop it.
*/
- f_ctl = ntoh24(fh->fh_f_ctl);
switch (fr_eof(fp)) {
case FC_EOF_T:
if (f_ctl & FC_FC_END_SEQ)
@@ -1893,34 +2008,24 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
/* fall through */
case FC_EOF_N:
if (fh->fh_type == FC_TYPE_BLS)
- fc_exch_recv_bls(mp, fp);
+ fc_exch_recv_bls(ema->mp, fp);
else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) ==
FC_FC_EX_CTX)
- fc_exch_recv_seq_resp(mp, fp);
+ fc_exch_recv_seq_resp(ema->mp, fp);
else if (f_ctl & FC_FC_SEQ_CTX)
- fc_exch_recv_resp(mp, fp);
+ fc_exch_recv_resp(ema->mp, fp);
else
- fc_exch_recv_req(lp, mp, fp);
+ fc_exch_recv_req(lp, ema->mp, fp);
break;
default:
- FC_EM_DBG(mp, "dropping invalid frame (eof %x)", fr_eof(fp));
+ FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp));
fc_frame_free(fp);
- break;
}
}
EXPORT_SYMBOL(fc_exch_recv);
int fc_exch_init(struct fc_lport *lp)
{
- if (!lp->tt.exch_get) {
- /*
- * exch_put() should be NULL if
- * exch_get() is NULL
- */
- WARN_ON(lp->tt.exch_put);
- lp->tt.exch_get = fc_exch_get;
- }
-
if (!lp->tt.seq_start_next)
lp->tt.seq_start_next = fc_seq_start_next;
@@ -1942,6 +2047,28 @@ int fc_exch_init(struct fc_lport *lp)
if (!lp->tt.seq_exch_abort)
lp->tt.seq_exch_abort = fc_seq_exch_abort;
+ /*
+ * Initialize fc_cpu_mask and fc_cpu_order. The
+ * fc_cpu_mask is set for nr_cpu_ids rounded up
+ * to order of 2's * power and order is stored
+ * in fc_cpu_order as this is later required in
+ * mapping between an exch id and exch array index
+ * in per cpu exch pool.
+ *
+ * This round up is required to align fc_cpu_mask
+ * to exchange id's lower bits such that all incoming
+ * frames of an exchange gets delivered to the same
+ * cpu on which exchange originated by simple bitwise
+ * AND operation between fc_cpu_mask and exchange id.
+ */
+ fc_cpu_mask = 1;
+ fc_cpu_order = 0;
+ while (fc_cpu_mask < nr_cpu_ids) {
+ fc_cpu_mask <<= 1;
+ fc_cpu_order++;
+ }
+ fc_cpu_mask--;
+
return 0;
}
EXPORT_SYMBOL(fc_exch_init);
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index e303e0d12c4..59a4408b27b 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -507,33 +507,6 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
f_ctl = FC_FC_REL_OFF;
WARN_ON(!seq);
- /*
- * If a get_page()/put_page() will fail, don't use sg lists
- * in the fc_frame structure.
- *
- * The put_page() may be long after the I/O has completed
- * in the case of FCoE, since the network driver does it
- * via free_skb(). See the test in free_pages_check().
- *
- * Test this case with 'dd </dev/zero >/dev/st0 bs=64k'.
- */
- if (using_sg) {
- for (sg = scsi_sglist(sc); sg; sg = sg_next(sg)) {
- if (page_count(sg_page(sg)) == 0 ||
- (sg_page(sg)->flags & (1 << PG_lru |
- 1 << PG_private |
- 1 << PG_locked |
- 1 << PG_active |
- 1 << PG_slab |
- 1 << PG_swapcache |
- 1 << PG_writeback |
- 1 << PG_reserved |
- 1 << PG_buddy))) {
- using_sg = 0;
- break;
- }
- }
- }
sg = scsi_sglist(sc);
while (remaining > 0 && sg) {
@@ -569,8 +542,6 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
}
sg_bytes = min(tlen, sg->length - offset);
if (using_sg) {
- WARN_ON(skb_shinfo(fp_skb(fp))->nr_frags >
- FC_FRAME_SG_LEN);
get_page(sg_page(sg));
skb_fill_page_desc(fp_skb(fp),
skb_shinfo(fp_skb(fp))->nr_frags,
@@ -1337,7 +1308,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp,
+ if (lp->tt.elsct_send(lp, rport->port_id, fp, ELS_REC, fc_fcp_rec_resp,
fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */
return;
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 745fa5555d6..bd2f7719744 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -113,7 +113,7 @@ static void fc_lport_enter_ready(struct fc_lport *);
static void fc_lport_enter_logo(struct fc_lport *);
static const char *fc_lport_state_names[] = {
- [LPORT_ST_NONE] = "none",
+ [LPORT_ST_DISABLED] = "disabled",
[LPORT_ST_FLOGI] = "FLOGI",
[LPORT_ST_DNS] = "dNS",
[LPORT_ST_RPN_ID] = "RPN_ID",
@@ -133,57 +133,44 @@ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
/**
* fc_lport_rport_callback() - Event handler for rport events
* @lport: The lport which is receiving the event
- * @rport: The rport which the event has occured on
+ * @rdata: private remote port data
* @event: The event that occured
*
* Locking Note: The rport lock should not be held when calling
* this function.
*/
static void fc_lport_rport_callback(struct fc_lport *lport,
- struct fc_rport *rport,
+ struct fc_rport_priv *rdata,
enum fc_rport_event event)
{
FC_LPORT_DBG(lport, "Received a %d event for port (%6x)\n", event,
- rport->port_id);
+ rdata->ids.port_id);
+ mutex_lock(&lport->lp_mutex);
switch (event) {
- case RPORT_EV_CREATED:
- if (rport->port_id == FC_FID_DIR_SERV) {
- mutex_lock(&lport->lp_mutex);
- if (lport->state == LPORT_ST_DNS) {
- lport->dns_rp = rport;
- fc_lport_enter_rpn_id(lport);
- } else {
- FC_LPORT_DBG(lport, "Received an CREATED event "
- "on port (%6x) for the directory "
- "server, but the lport is not "
- "in the DNS state, it's in the "
- "%d state", rport->port_id,
- lport->state);
- lport->tt.rport_logoff(rport);
- }
- mutex_unlock(&lport->lp_mutex);
- } else
- FC_LPORT_DBG(lport, "Received an event for port (%6x) "
- "which is not the directory server\n",
- rport->port_id);
+ case RPORT_EV_READY:
+ if (lport->state == LPORT_ST_DNS) {
+ lport->dns_rp = rdata;
+ fc_lport_enter_rpn_id(lport);
+ } else {
+ FC_LPORT_DBG(lport, "Received an READY event "
+ "on port (%6x) for the directory "
+ "server, but the lport is not "
+ "in the DNS state, it's in the "
+ "%d state", rdata->ids.port_id,
+ lport->state);
+ lport->tt.rport_logoff(rdata);
+ }
break;
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
- if (rport->port_id == FC_FID_DIR_SERV) {
- mutex_lock(&lport->lp_mutex);
- lport->dns_rp = NULL;
- mutex_unlock(&lport->lp_mutex);
-
- } else
- FC_LPORT_DBG(lport, "Received an event for port (%6x) "
- "which is not the directory server\n",
- rport->port_id);
+ lport->dns_rp = NULL;
break;
case RPORT_EV_NONE:
break;
}
+ mutex_unlock(&lport->lp_mutex);
}
/**
@@ -211,20 +198,13 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
u32 remote_fid, u64 remote_wwpn,
u64 remote_wwnn)
{
- struct fc_disc_port dp;
-
- dp.lp = lport;
- dp.ids.port_id = remote_fid;
- dp.ids.port_name = remote_wwpn;
- dp.ids.node_name = remote_wwnn;
- dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
- if (lport->ptp_rp) {
+ mutex_lock(&lport->disc.disc_mutex);
+ if (lport->ptp_rp)
lport->tt.rport_logoff(lport->ptp_rp);
- lport->ptp_rp = NULL;
- }
-
- lport->ptp_rp = lport->tt.rport_create(&dp);
+ lport->ptp_rp = lport->tt.rport_create(lport, remote_fid);
+ lport->ptp_rp->ids.port_name = remote_wwpn;
+ lport->ptp_rp->ids.node_name = remote_wwnn;
+ mutex_unlock(&lport->disc.disc_mutex);
lport->tt.rport_login(lport->ptp_rp);
@@ -472,56 +452,6 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
}
/**
- * fc_lport_recv_adisc_req() - Handle received Address Discovery Request
- * @lport: Fibre Channel local port recieving the ADISC
- * @sp: current sequence in the ADISC exchange
- * @fp: ADISC request frame
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
- */
-static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp,
- struct fc_lport *lport)
-{
- struct fc_frame *fp;
- struct fc_exch *ep = fc_seq_exch(sp);
- struct fc_els_adisc *req, *rp;
- struct fc_seq_els_data rjt_data;
- size_t len;
- u32 f_ctl;
-
- FC_LPORT_DBG(lport, "Received ADISC request while in state %s\n",
- fc_lport_state(lport));
-
- req = fc_frame_payload_get(in_fp, sizeof(*req));
- if (!req) {
- rjt_data.fp = NULL;
- rjt_data.reason = ELS_RJT_LOGIC;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- } else {
- len = sizeof(*rp);
- fp = fc_frame_alloc(lport, len);
- if (fp) {
- rp = fc_frame_payload_get(fp, len);
- memset(rp, 0, len);
- rp->adisc_cmd = ELS_LS_ACC;
- rp->adisc_wwpn = htonll(lport->wwpn);
- rp->adisc_wwnn = htonll(lport->wwnn);
- hton24(rp->adisc_port_id,
- fc_host_port_id(lport->host));
- sp = lport->tt.seq_start_next(sp);
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
- FC_TYPE_ELS, f_ctl, 0);
- lport->tt.seq_send(lport, sp, fp);
- }
- }
- fc_frame_free(in_fp);
-}
-
-/**
* fc_lport_recv_logo_req() - Handle received fabric LOGO request
* @lport: Fibre Channel local port recieving the LOGO
* @sp: current sequence in the LOGO exchange
@@ -550,7 +480,7 @@ int fc_fabric_login(struct fc_lport *lport)
int rc = -1;
mutex_lock(&lport->lp_mutex);
- if (lport->state == LPORT_ST_NONE) {
+ if (lport->state == LPORT_ST_DISABLED) {
fc_lport_enter_reset(lport);
rc = 0;
}
@@ -637,12 +567,13 @@ EXPORT_SYMBOL(fc_fabric_logoff);
int fc_lport_destroy(struct fc_lport *lport)
{
mutex_lock(&lport->lp_mutex);
- lport->state = LPORT_ST_NONE;
+ lport->state = LPORT_ST_DISABLED;
lport->link_up = 0;
lport->tt.frame_send = fc_frame_drop;
mutex_unlock(&lport->lp_mutex);
lport->tt.fcp_abort_io(lport);
+ lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
return 0;
}
@@ -722,7 +653,8 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
fc_lport_state_enter(lport, LPORT_ST_READY);
- lport->tt.disc_start(fc_lport_disc_callback, lport);
+ if (!lport->ptp_rp)
+ lport->tt.disc_start(fc_lport_disc_callback, lport);
}
/**
@@ -808,8 +740,6 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
get_unaligned_be64(&flp->fl_wwnn));
- lport->tt.disc_start(fc_lport_disc_callback, lport);
-
out:
sp = fr_seq(rx_fp);
fc_frame_free(rx_fp);
@@ -832,10 +762,6 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
- struct fc_rport *rport;
- u32 s_id;
- u32 d_id;
- struct fc_seq_els_data rjt_data;
mutex_lock(&lport->lp_mutex);
@@ -844,11 +770,14 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
* RSCN here. These don't require a session.
* Even if we had a session, it might not be ready.
*/
- if (fh->fh_type == FC_TYPE_ELS && fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ if (!lport->link_up)
+ fc_frame_free(fp);
+ else if (fh->fh_type == FC_TYPE_ELS &&
+ fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
/*
* Check opcode.
*/
- recv = NULL;
+ recv = lport->tt.rport_recv_req;
switch (fc_frame_payload_op(fp)) {
case ELS_FLOGI:
recv = fc_lport_recv_flogi_req;
@@ -870,34 +799,9 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
case ELS_RNID:
recv = fc_lport_recv_rnid_req;
break;
- case ELS_ADISC:
- recv = fc_lport_recv_adisc_req;
- break;
}
- if (recv)
- recv(sp, fp, lport);
- else {
- /*
- * Find session.
- * If this is a new incoming PLOGI, we won't find it.
- */
- s_id = ntoh24(fh->fh_s_id);
- d_id = ntoh24(fh->fh_d_id);
-
- rport = lport->tt.rport_lookup(lport, s_id);
- if (rport)
- lport->tt.rport_recv_req(sp, fp, rport);
- else {
- rjt_data.fp = NULL;
- rjt_data.reason = ELS_RJT_UNAB;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp,
- ELS_LS_RJT,
- &rjt_data);
- fc_frame_free(fp);
- }
- }
+ recv(sp, fp, lport);
} else {
FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
fr_eof(fp));
@@ -930,38 +834,61 @@ int fc_lport_reset(struct fc_lport *lport)
EXPORT_SYMBOL(fc_lport_reset);
/**
- * fc_rport_enter_reset() - Reset the local port
+ * fc_lport_reset_locked() - Reset the local port
* @lport: Fibre Channel local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
-static void fc_lport_enter_reset(struct fc_lport *lport)
+static void fc_lport_reset_locked(struct fc_lport *lport)
{
- FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
- fc_lport_state(lport));
-
- fc_lport_state_enter(lport, LPORT_ST_RESET);
-
if (lport->dns_rp)
lport->tt.rport_logoff(lport->dns_rp);
- if (lport->ptp_rp) {
- lport->tt.rport_logoff(lport->ptp_rp);
- lport->ptp_rp = NULL;
- }
+ lport->ptp_rp = NULL;
lport->tt.disc_stop(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
fc_host_fabric_name(lport->host) = 0;
fc_host_port_id(lport->host) = 0;
+}
+/**
+ * fc_lport_enter_reset() - Reset the local port
+ * @lport: Fibre Channel local port to be reset
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_reset(struct fc_lport *lport)
+{
+ FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RESET);
+ fc_lport_reset_locked(lport);
if (lport->link_up)
fc_lport_enter_flogi(lport);
}
/**
+ * fc_lport_enter_disabled() - disable the local port
+ * @lport: Fibre Channel local port to be reset
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_disabled(struct fc_lport *lport)
+{
+ FC_LPORT_DBG(lport, "Entered disabled state from %s state\n",
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_DISABLED);
+ fc_lport_reset_locked(lport);
+}
+
+/**
* fc_lport_error() - Handler for any errors
* @lport: The fc_lport object
* @fp: The frame pointer
@@ -992,7 +919,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
schedule_delayed_work(&lport->retry_work, delay);
} else {
switch (lport->state) {
- case LPORT_ST_NONE:
+ case LPORT_ST_DISABLED:
case LPORT_ST_READY:
case LPORT_ST_RESET:
case LPORT_ST_RPN_ID:
@@ -1026,13 +953,13 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_frame_header *fh;
struct fc_ct_hdr *ct;
+ FC_LPORT_DBG(lport, "Received a RFT_ID %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a RFT_ID response\n");
-
if (lport->state != LPORT_ST_RFT_ID) {
FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1080,13 +1007,13 @@ static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_frame_header *fh;
struct fc_ct_hdr *ct;
+ FC_LPORT_DBG(lport, "Received a RPN_ID %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a RPN_ID response\n");
-
if (lport->state != LPORT_ST_RPN_ID) {
FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1132,13 +1059,13 @@ static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_lport *lport = lp_arg;
u8 op;
+ FC_LPORT_DBG(lport, "Received a SCR %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a SCR response\n");
-
if (lport->state != LPORT_ST_SCR) {
FC_LPORT_DBG(lport, "Received a SCR response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1186,7 +1113,7 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, ELS_SCR,
+ if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR,
fc_lport_scr_resp, lport, lport->e_d_tov))
fc_lport_error(lport, fp);
}
@@ -1227,7 +1154,7 @@ static void fc_lport_enter_rft_id(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RFT_ID,
+ if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID,
fc_lport_rft_id_resp,
lport, lport->e_d_tov))
fc_lport_error(lport, fp);
@@ -1256,7 +1183,7 @@ static void fc_lport_enter_rpn_id(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RPN_ID,
+ if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID,
fc_lport_rpn_id_resp,
lport, lport->e_d_tov))
fc_lport_error(lport, fp);
@@ -1275,28 +1202,21 @@ static struct fc_rport_operations fc_lport_rport_ops = {
*/
static void fc_lport_enter_dns(struct fc_lport *lport)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
- struct fc_disc_port dp;
-
- dp.ids.port_id = FC_FID_DIR_SERV;
- dp.ids.port_name = -1;
- dp.ids.node_name = -1;
- dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
- dp.lp = lport;
+ struct fc_rport_priv *rdata;
FC_LPORT_DBG(lport, "Entered DNS state from %s state\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_DNS);
- rport = lport->tt.rport_create(&dp);
- if (!rport)
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_create(lport, FC_FID_DIR_SERV);
+ mutex_unlock(&lport->disc.disc_mutex);
+ if (!rdata)
goto err;
- rdata = rport->dd_data;
rdata->ops = &fc_lport_rport_ops;
- lport->tt.rport_login(rport);
+ lport->tt.rport_login(rdata);
return;
err:
@@ -1316,7 +1236,7 @@ static void fc_lport_timeout(struct work_struct *work)
mutex_lock(&lport->lp_mutex);
switch (lport->state) {
- case LPORT_ST_NONE:
+ case LPORT_ST_DISABLED:
case LPORT_ST_READY:
case LPORT_ST_RESET:
WARN_ON(1);
@@ -1360,13 +1280,13 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_lport *lport = lp_arg;
u8 op;
+ FC_LPORT_DBG(lport, "Received a LOGO %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a LOGO response\n");
-
if (lport->state != LPORT_ST_LOGO) {
FC_LPORT_DBG(lport, "Received a LOGO response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1382,7 +1302,7 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
- fc_lport_enter_reset(lport);
+ fc_lport_enter_disabled(lport);
else
fc_lport_error(lport, fp);
@@ -1415,8 +1335,8 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, ELS_LOGO, fc_lport_logo_resp,
- lport, lport->e_d_tov))
+ if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO,
+ fc_lport_logo_resp, lport, lport->e_d_tov))
fc_lport_error(lport, fp);
}
@@ -1442,13 +1362,13 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
unsigned int e_d_tov;
u16 mfs;
+ FC_LPORT_DBG(lport, "Received a FLOGI %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a FLOGI response\n");
-
if (lport->state != LPORT_ST_FLOGI) {
FC_LPORT_DBG(lport, "Received a FLOGI response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1501,14 +1421,6 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_lport_enter_dns(lport);
}
}
-
- if (flp) {
- csp_flags = ntohs(flp->fl_csp.sp_features);
- if ((csp_flags & FC_SP_FT_FPORT) == 0) {
- lport->tt.disc_start(fc_lport_disc_callback,
- lport);
- }
- }
} else {
FC_LPORT_DBG(lport, "Bad FLOGI response\n");
}
@@ -1539,7 +1451,7 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
if (!fp)
return fc_lport_error(lport, fp);
- if (!lport->tt.elsct_send(lport, NULL, fp, ELS_FLOGI,
+ if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI,
fc_lport_flogi_resp, lport, lport->e_d_tov))
fc_lport_error(lport, fp);
}
@@ -1550,7 +1462,7 @@ int fc_lport_config(struct fc_lport *lport)
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
mutex_init(&lport->lp_mutex);
- fc_lport_state_enter(lport, LPORT_ST_NONE);
+ fc_lport_state_enter(lport, LPORT_ST_DISABLED);
fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
fc_lport_add_fc4_type(lport, FC_TYPE_CT);
@@ -1588,6 +1500,7 @@ int fc_lport_init(struct fc_lport *lport)
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+ INIT_LIST_HEAD(&lport->ema_list);
return 0;
}
EXPORT_SYMBOL(fc_lport_init);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 7162385f52e..03ea6748e7e 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -57,94 +57,114 @@
struct workqueue_struct *rport_event_queue;
-static void fc_rport_enter_plogi(struct fc_rport *);
-static void fc_rport_enter_prli(struct fc_rport *);
-static void fc_rport_enter_rtv(struct fc_rport *);
-static void fc_rport_enter_ready(struct fc_rport *);
-static void fc_rport_enter_logo(struct fc_rport *);
-
-static void fc_rport_recv_plogi_req(struct fc_rport *,
+static void fc_rport_enter_plogi(struct fc_rport_priv *);
+static void fc_rport_enter_prli(struct fc_rport_priv *);
+static void fc_rport_enter_rtv(struct fc_rport_priv *);
+static void fc_rport_enter_ready(struct fc_rport_priv *);
+static void fc_rport_enter_logo(struct fc_rport_priv *);
+static void fc_rport_enter_adisc(struct fc_rport_priv *);
+
+static void fc_rport_recv_plogi_req(struct fc_lport *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prli_req(struct fc_rport *,
+static void fc_rport_recv_prli_req(struct fc_rport_priv *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prlo_req(struct fc_rport *,
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_logo_req(struct fc_rport *,
+static void fc_rport_recv_logo_req(struct fc_lport *,
struct fc_seq *, struct fc_frame *);
static void fc_rport_timeout(struct work_struct *);
-static void fc_rport_error(struct fc_rport *, struct fc_frame *);
-static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *);
+static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *);
static void fc_rport_work(struct work_struct *);
static const char *fc_rport_state_names[] = {
- [RPORT_ST_NONE] = "None",
[RPORT_ST_INIT] = "Init",
[RPORT_ST_PLOGI] = "PLOGI",
[RPORT_ST_PRLI] = "PRLI",
[RPORT_ST_RTV] = "RTV",
[RPORT_ST_READY] = "Ready",
[RPORT_ST_LOGO] = "LOGO",
+ [RPORT_ST_ADISC] = "ADISC",
+ [RPORT_ST_DELETE] = "Delete",
};
-static void fc_rport_rogue_destroy(struct device *dev)
+/**
+ * fc_rport_lookup() - lookup a remote port by port_id
+ * @lport: Fibre Channel host port instance
+ * @port_id: remote port port_id to match
+ */
+static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport *rport = dev_to_rport(dev);
- FC_RPORT_DBG(rport, "Destroying rogue rport\n");
- kfree(rport);
+ struct fc_rport_priv *rdata;
+
+ list_for_each_entry(rdata, &lport->disc.rports, peers)
+ if (rdata->ids.port_id == port_id &&
+ rdata->rp_state != RPORT_ST_DELETE)
+ return rdata;
+ return NULL;
}
-struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
+/**
+ * fc_rport_create() - Create a new remote port
+ * @lport: The local port that the new remote port is for
+ * @port_id: The port ID for the new remote port
+ *
+ * Locking note: must be called with the disc_mutex held.
+ */
+static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
- rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL);
+ struct fc_rport_priv *rdata;
- if (!rport)
- return NULL;
+ rdata = lport->tt.rport_lookup(lport, port_id);
+ if (rdata)
+ return rdata;
- rdata = RPORT_TO_PRIV(rport);
+ rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return NULL;
- rport->dd_data = rdata;
- rport->port_id = dp->ids.port_id;
- rport->port_name = dp->ids.port_name;
- rport->node_name = dp->ids.node_name;
- rport->roles = dp->ids.roles;
- rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
- /*
- * Note: all this libfc rogue rport code will be removed for
- * upstream so it fine that this is really ugly and hacky right now.
- */
- device_initialize(&rport->dev);
- rport->dev.release = fc_rport_rogue_destroy;
+ rdata->ids.node_name = -1;
+ rdata->ids.port_name = -1;
+ rdata->ids.port_id = port_id;
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ kref_init(&rdata->kref);
mutex_init(&rdata->rp_mutex);
- rdata->local_port = dp->lp;
- rdata->trans_state = FC_PORTSTATE_ROGUE;
+ rdata->local_port = lport;
rdata->rp_state = RPORT_ST_INIT;
rdata->event = RPORT_EV_NONE;
rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- rdata->ops = NULL;
- rdata->e_d_tov = dp->lp->e_d_tov;
- rdata->r_a_tov = dp->lp->r_a_tov;
+ rdata->e_d_tov = lport->e_d_tov;
+ rdata->r_a_tov = lport->r_a_tov;
+ rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
INIT_WORK(&rdata->event_work, fc_rport_work);
- /*
- * For good measure, but not necessary as we should only
- * add REAL rport to the lport list.
- */
- INIT_LIST_HEAD(&rdata->peers);
+ if (port_id != FC_FID_DIR_SERV)
+ list_add(&rdata->peers, &lport->disc.rports);
+ return rdata;
+}
+
+/**
+ * fc_rport_destroy() - free a remote port after last reference is released.
+ * @kref: pointer to kref inside struct fc_rport_priv
+ */
+static void fc_rport_destroy(struct kref *kref)
+{
+ struct fc_rport_priv *rdata;
- return rport;
+ rdata = container_of(kref, struct fc_rport_priv, kref);
+ kfree(rdata);
}
/**
* fc_rport_state() - return a string for the state the rport is in
- * @rport: The rport whose state we want to get a string for
+ * @rdata: remote port private data
*/
-static const char *fc_rport_state(struct fc_rport *rport)
+static const char *fc_rport_state(struct fc_rport_priv *rdata)
{
const char *cp;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
cp = fc_rport_state_names[rdata->rp_state];
if (!cp)
@@ -191,15 +211,14 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
/**
* fc_rport_state_enter() - Change the rport's state
- * @rport: The rport whose state should change
+ * @rdata: The rport whose state should change
* @new: The new state of the rport
*
* Locking Note: Called with the rport lock held
*/
-static void fc_rport_state_enter(struct fc_rport *rport,
+static void fc_rport_state_enter(struct fc_rport_priv *rdata,
enum fc_rport_state new)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
if (rdata->rp_state != new)
rdata->retries = 0;
rdata->rp_state = new;
@@ -208,147 +227,187 @@ static void fc_rport_state_enter(struct fc_rport *rport,
static void fc_rport_work(struct work_struct *work)
{
u32 port_id;
- struct fc_rport_libfc_priv *rdata =
- container_of(work, struct fc_rport_libfc_priv, event_work);
+ struct fc_rport_priv *rdata =
+ container_of(work, struct fc_rport_priv, event_work);
+ struct fc_rport_libfc_priv *rp;
enum fc_rport_event event;
- enum fc_rport_trans_state trans_state;
struct fc_lport *lport = rdata->local_port;
struct fc_rport_operations *rport_ops;
- struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
rport_ops = rdata->ops;
+ rport = rdata->rport;
- if (event == RPORT_EV_CREATED) {
- struct fc_rport *new_rport;
- struct fc_rport_libfc_priv *new_rdata;
- struct fc_rport_identifiers ids;
+ FC_RPORT_DBG(rdata, "work event %u\n", event);
- ids.port_id = rport->port_id;
- ids.roles = rport->roles;
- ids.port_name = rport->port_name;
- ids.node_name = rport->node_name;
+ switch (event) {
+ case RPORT_EV_READY:
+ ids = rdata->ids;
+ rdata->event = RPORT_EV_NONE;
+ kref_get(&rdata->kref);
+ mutex_unlock(&rdata->rp_mutex);
+ if (!rport)
+ rport = fc_remote_port_add(lport->host, 0, &ids);
+ if (!rport) {
+ FC_RPORT_DBG(rdata, "Failed to add the rport\n");
+ lport->tt.rport_logoff(rdata);
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ return;
+ }
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->rport)
+ FC_RPORT_DBG(rdata, "rport already allocated\n");
+ rdata->rport = rport;
+ rport->maxframe_size = rdata->maxframe_size;
+ rport->supported_classes = rdata->supported_classes;
+
+ rp = rport->dd_data;
+ rp->local_port = lport;
+ rp->rp_state = rdata->rp_state;
+ rp->flags = rdata->flags;
+ rp->e_d_tov = rdata->e_d_tov;
+ rp->r_a_tov = rdata->r_a_tov;
mutex_unlock(&rdata->rp_mutex);
- new_rport = fc_remote_port_add(lport->host, 0, &ids);
- if (new_rport) {
- /*
- * Switch from the rogue rport to the rport
- * returned by the FC class.
- */
- new_rport->maxframe_size = rport->maxframe_size;
-
- new_rdata = new_rport->dd_data;
- new_rdata->e_d_tov = rdata->e_d_tov;
- new_rdata->r_a_tov = rdata->r_a_tov;
- new_rdata->ops = rdata->ops;
- new_rdata->local_port = rdata->local_port;
- new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- new_rdata->trans_state = FC_PORTSTATE_REAL;
- mutex_init(&new_rdata->rp_mutex);
- INIT_DELAYED_WORK(&new_rdata->retry_work,
- fc_rport_timeout);
- INIT_LIST_HEAD(&new_rdata->peers);
- INIT_WORK(&new_rdata->event_work, fc_rport_work);
-
- fc_rport_state_enter(new_rport, RPORT_ST_READY);
- } else {
- printk(KERN_WARNING "libfc: Failed to allocate "
- " memory for rport (%6x)\n", ids.port_id);
- event = RPORT_EV_FAILED;
+ if (rport_ops && rport_ops->event_callback) {
+ FC_RPORT_DBG(rdata, "callback ev %d\n", event);
+ rport_ops->event_callback(lport, rdata, event);
}
- if (rport->port_id != FC_FID_DIR_SERV)
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport,
- RPORT_EV_FAILED);
- put_device(&rport->dev);
- rport = new_rport;
- rdata = new_rport->dd_data;
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport, event);
- } else if ((event == RPORT_EV_FAILED) ||
- (event == RPORT_EV_LOGO) ||
- (event == RPORT_EV_STOP)) {
- trans_state = rdata->trans_state;
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ break;
+
+ case RPORT_EV_FAILED:
+ case RPORT_EV_LOGO:
+ case RPORT_EV_STOP:
+ port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport, event);
- if (trans_state == FC_PORTSTATE_ROGUE)
- put_device(&rport->dev);
- else {
- port_id = rport->port_id;
+
+ if (port_id != FC_FID_DIR_SERV) {
+ mutex_lock(&lport->disc.disc_mutex);
+ list_del(&rdata->peers);
+ mutex_unlock(&lport->disc.disc_mutex);
+ }
+
+ if (rport_ops && rport_ops->event_callback) {
+ FC_RPORT_DBG(rdata, "callback ev %d\n", event);
+ rport_ops->event_callback(lport, rdata, event);
+ }
+ cancel_delayed_work_sync(&rdata->retry_work);
+
+ /*
+ * Reset any outstanding exchanges before freeing rport.
+ */
+ lport->tt.exch_mgr_reset(lport, 0, port_id);
+ lport->tt.exch_mgr_reset(lport, port_id, 0);
+
+ if (rport) {
+ rp = rport->dd_data;
+ rp->rp_state = RPORT_ST_DELETE;
+ mutex_lock(&rdata->rp_mutex);
+ rdata->rport = NULL;
+ mutex_unlock(&rdata->rp_mutex);
fc_remote_port_delete(rport);
- lport->tt.exch_mgr_reset(lport, 0, port_id);
- lport->tt.exch_mgr_reset(lport, port_id, 0);
}
- } else
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ break;
+
+ default:
mutex_unlock(&rdata->rp_mutex);
+ break;
+ }
}
/**
* fc_rport_login() - Start the remote port login state machine
- * @rport: Fibre Channel remote port
+ * @rdata: private remote port
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
+ *
+ * This indicates the intent to be logged into the remote port.
+ * If it appears we are already logged in, ADISC is used to verify
+ * the setup.
*/
-int fc_rport_login(struct fc_rport *rport)
+int fc_rport_login(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Login to port\n");
-
- fc_rport_enter_plogi(rport);
-
+ switch (rdata->rp_state) {
+ case RPORT_ST_READY:
+ FC_RPORT_DBG(rdata, "ADISC port\n");
+ fc_rport_enter_adisc(rdata);
+ break;
+ default:
+ FC_RPORT_DBG(rdata, "Login to port\n");
+ fc_rport_enter_plogi(rdata);
+ break;
+ }
mutex_unlock(&rdata->rp_mutex);
return 0;
}
/**
+ * fc_rport_enter_delete() - schedule a remote port to be deleted.
+ * @rdata: private remote port
+ * @event: event to report as the reason for deletion
+ *
+ * Locking Note: Called with the rport lock held.
+ *
+ * Allow state change into DELETE only once.
+ *
+ * Call queue_work only if there's no event already pending.
+ * Set the new event so that the old pending event will not occur.
+ * Since we have the mutex, even if fc_rport_work() is already started,
+ * it'll see the new event.
+ */
+static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
+ enum fc_rport_event event)
+{
+ if (rdata->rp_state == RPORT_ST_DELETE)
+ return;
+
+ FC_RPORT_DBG(rdata, "Delete port\n");
+
+ fc_rport_state_enter(rdata, RPORT_ST_DELETE);
+
+ if (rdata->event == RPORT_EV_NONE)
+ queue_work(rport_event_queue, &rdata->event_work);
+ rdata->event = event;
+}
+
+/**
* fc_rport_logoff() - Logoff and remove an rport
- * @rport: Fibre Channel remote port to be removed
+ * @rdata: private remote port
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
*/
-int fc_rport_logoff(struct fc_rport *rport)
+int fc_rport_logoff(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Remove port\n");
+ FC_RPORT_DBG(rdata, "Remove port\n");
- if (rdata->rp_state == RPORT_ST_NONE) {
- FC_RPORT_DBG(rport, "Port in NONE state, not removing\n");
+ if (rdata->rp_state == RPORT_ST_DELETE) {
+ FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
mutex_unlock(&rdata->rp_mutex);
goto out;
}
- fc_rport_enter_logo(rport);
+ fc_rport_enter_logo(rdata);
/*
- * Change the state to NONE so that we discard
+ * Change the state to Delete so that we discard
* the response.
*/
- fc_rport_state_enter(rport, RPORT_ST_NONE);
-
- mutex_unlock(&rdata->rp_mutex);
-
- cancel_delayed_work_sync(&rdata->retry_work);
-
- mutex_lock(&rdata->rp_mutex);
-
- rdata->event = RPORT_EV_STOP;
- queue_work(rport_event_queue, &rdata->event_work);
-
+ fc_rport_enter_delete(rdata, RPORT_EV_STOP);
mutex_unlock(&rdata->rp_mutex);
out:
@@ -357,26 +416,25 @@ out:
/**
* fc_rport_enter_ready() - The rport is ready
- * @rport: Fibre Channel remote port that is ready
+ * @rdata: private remote port
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_ready(struct fc_rport *rport)
+static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
- fc_rport_state_enter(rport, RPORT_ST_READY);
+ fc_rport_state_enter(rdata, RPORT_ST_READY);
- FC_RPORT_DBG(rport, "Port is Ready\n");
+ FC_RPORT_DBG(rdata, "Port is Ready\n");
- rdata->event = RPORT_EV_CREATED;
- queue_work(rport_event_queue, &rdata->event_work);
+ if (rdata->event == RPORT_EV_NONE)
+ queue_work(rport_event_queue, &rdata->event_work);
+ rdata->event = RPORT_EV_READY;
}
/**
* fc_rport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_rport_libfc_priv
+ * @work: The work struct of the fc_rport_priv
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -384,63 +442,63 @@ static void fc_rport_enter_ready(struct fc_rport *rport)
*/
static void fc_rport_timeout(struct work_struct *work)
{
- struct fc_rport_libfc_priv *rdata =
- container_of(work, struct fc_rport_libfc_priv, retry_work.work);
- struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+ struct fc_rport_priv *rdata =
+ container_of(work, struct fc_rport_priv, retry_work.work);
mutex_lock(&rdata->rp_mutex);
switch (rdata->rp_state) {
case RPORT_ST_PLOGI:
- fc_rport_enter_plogi(rport);
+ fc_rport_enter_plogi(rdata);
break;
case RPORT_ST_PRLI:
- fc_rport_enter_prli(rport);
+ fc_rport_enter_prli(rdata);
break;
case RPORT_ST_RTV:
- fc_rport_enter_rtv(rport);
+ fc_rport_enter_rtv(rdata);
break;
case RPORT_ST_LOGO:
- fc_rport_enter_logo(rport);
+ fc_rport_enter_logo(rdata);
+ break;
+ case RPORT_ST_ADISC:
+ fc_rport_enter_adisc(rdata);
break;
case RPORT_ST_READY:
case RPORT_ST_INIT:
- case RPORT_ST_NONE:
+ case RPORT_ST_DELETE:
break;
}
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
}
/**
* fc_rport_error() - Error handler, called once retries have been exhausted
- * @rport: The fc_rport object
+ * @rdata: private remote port
* @fp: The frame pointer
*
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
-static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
+static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
- FC_RPORT_DBG(rport, "Error %ld in state %s, retries %d\n",
- PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
+ FC_RPORT_DBG(rdata, "Error %ld in state %s, retries %d\n",
+ IS_ERR(fp) ? -PTR_ERR(fp) : 0,
+ fc_rport_state(rdata), rdata->retries);
switch (rdata->rp_state) {
case RPORT_ST_PLOGI:
- case RPORT_ST_PRLI:
case RPORT_ST_LOGO:
- rdata->event = RPORT_EV_FAILED;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue,
- &rdata->event_work);
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
break;
case RPORT_ST_RTV:
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
break;
- case RPORT_ST_NONE:
+ case RPORT_ST_PRLI:
+ case RPORT_ST_ADISC:
+ fc_rport_enter_logo(rdata);
+ break;
+ case RPORT_ST_DELETE:
case RPORT_ST_READY:
case RPORT_ST_INIT:
break;
@@ -449,7 +507,7 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
/**
* fc_rport_error_retry() - Error handler when retries are desired
- * @rport: The fc_rport object
+ * @rdata: private remote port data
* @fp: The frame pointer
*
* If the error was an exchange timeout retry immediately,
@@ -458,45 +516,43 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
-static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
+static void fc_rport_error_retry(struct fc_rport_priv *rdata,
+ struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
unsigned long delay = FC_DEF_E_D_TOV;
/* make sure this isn't an FC_EX_CLOSED error, never retry those */
if (PTR_ERR(fp) == -FC_EX_CLOSED)
- return fc_rport_error(rport, fp);
+ return fc_rport_error(rdata, fp);
if (rdata->retries < rdata->local_port->max_rport_retry_count) {
- FC_RPORT_DBG(rport, "Error %ld in state %s, retrying\n",
- PTR_ERR(fp), fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n",
+ PTR_ERR(fp), fc_rport_state(rdata));
rdata->retries++;
/* no additional delay on exchange timeouts */
if (PTR_ERR(fp) == -FC_EX_TIMEOUT)
delay = 0;
- get_device(&rport->dev);
schedule_delayed_work(&rdata->retry_work, delay);
return;
}
- return fc_rport_error(rport, fp);
+ return fc_rport_error(rdata, fp);
}
/**
* fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response
* @sp: current sequence in the PLOGI exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
struct fc_lport *lport = rdata->local_port;
struct fc_els_flogi *plp = NULL;
unsigned int tov;
@@ -506,26 +562,26 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a PLOGI response\n");
+ FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_PLOGI) {
- FC_RPORT_DBG(rport, "Received a PLOGI response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a PLOGI response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC &&
(plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
- rport->port_name = get_unaligned_be64(&plp->fl_wwpn);
- rport->node_name = get_unaligned_be64(&plp->fl_wwnn);
+ rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
+ rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
tov = ntohl(plp->fl_csp.sp_e_d_tov);
if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
@@ -537,75 +593,64 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
if (cssp_seq < csp_seq)
csp_seq = cssp_seq;
rdata->max_seq = csp_seq;
- rport->maxframe_size =
- fc_plogi_get_maxframe(plp, lport->mfs);
-
- /*
- * If the rport is one of the well known addresses
- * we skip PRLI and RTV and go straight to READY.
- */
- if (rport->port_id >= FC_FID_DOM_MGR)
- fc_rport_enter_ready(rport);
- else
- fc_rport_enter_prli(rport);
+ rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs);
+ fc_rport_enter_prli(rdata);
} else
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer
- * @rport: Fibre Channel remote port to send PLOGI to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_plogi(struct fc_rport *rport)
+static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered PLOGI state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_PLOGI);
+ fc_rport_state_enter(rdata, RPORT_ST_PLOGI);
- rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
rdata->e_d_tov = lport->e_d_tov;
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
- fc_rport_plogi_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
+ fc_rport_plogi_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_prli_resp() - Process Login (PRLI) response handler
* @sp: current sequence in the PRLI exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
struct {
struct fc_els_prli prli;
struct fc_els_spp spp;
@@ -616,21 +661,24 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a PRLI response\n");
+ FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_PRLI) {
- FC_RPORT_DBG(rport, "Received a PRLI response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a PRLI response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -640,90 +688,82 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
rdata->flags |= FC_RP_FLAGS_RETRY;
}
- rport->supported_classes = FC_COS_CLASS3;
+ rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
- rport->roles = roles;
- fc_rport_enter_rtv(rport);
+ rdata->ids.roles = roles;
+ fc_rport_enter_rtv(rdata);
} else {
- FC_RPORT_DBG(rport, "Bad ELS response for PRLI command\n");
- rdata->event = RPORT_EV_FAILED;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
+ FC_RPORT_DBG(rdata, "Bad ELS response for PRLI command\n");
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
}
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_logo_resp() - Logout (LOGO) response handler
* @sp: current sequence in the LOGO exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
u8 op;
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a LOGO response\n");
+ FC_RPORT_DBG(rdata, "Received a LOGO %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_LOGO) {
- FC_RPORT_DBG(rport, "Received a LOGO response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a LOGO response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
op = fc_frame_payload_op(fp);
- if (op == ELS_LS_ACC) {
- fc_rport_enter_rtv(rport);
- } else {
- FC_RPORT_DBG(rport, "Bad ELS response for LOGO command\n");
- rdata->event = RPORT_EV_LOGO;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
- }
+ if (op != ELS_LS_ACC)
+ FC_RPORT_DBG(rdata, "Bad ELS response op %x for LOGO command\n",
+ op);
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_prli() - Send Process Login (PRLI) request to peer
- * @rport: Fibre Channel remote port to send PRLI to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_prli(struct fc_rport *rport)
+static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct {
struct fc_els_prli prli;
@@ -731,29 +771,38 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
} *pp;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered PRLI state from %s state\n",
- fc_rport_state(rport));
+ /*
+ * If the rport is one of the well known addresses
+ * we skip PRLI and RTV and go straight to READY.
+ */
+ if (rdata->ids.port_id >= FC_FID_DOM_MGR) {
+ fc_rport_enter_ready(rdata);
+ return;
+ }
+
+ FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_PRLI);
+ fc_rport_state_enter(rdata, RPORT_ST_PRLI);
fp = fc_frame_alloc(lport, sizeof(*pp));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
- fc_rport_prli_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
+ fc_rport_prli_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_els_rtv_resp() - Request Timeout Value response handler
* @sp: current sequence in the RTV exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Many targets don't seem to support this.
*
@@ -762,26 +811,25 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
* and then unlock the rport.
*/
static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
u8 op;
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a RTV response\n");
+ FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_RTV) {
- FC_RPORT_DBG(rport, "Received a RTV response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a RTV response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
+ fc_rport_error(rdata, fp);
goto err;
}
@@ -807,184 +855,376 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
}
}
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer
- * @rport: Fibre Channel remote port to send RTV to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_rtv(struct fc_rport *rport)
+static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
{
struct fc_frame *fp;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
- FC_RPORT_DBG(rport, "Port entered RTV state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered RTV state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_RTV);
+ fc_rport_state_enter(rdata, RPORT_ST_RTV);
fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
- fc_rport_rtv_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
+ fc_rport_rtv_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_enter_logo() - Send Logout (LOGO) request to peer
- * @rport: Fibre Channel remote port to send LOGO to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_logo(struct fc_rport *rport)
+static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered LOGO state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered LOGO state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_LOGO);
+ fc_rport_state_enter(rdata, RPORT_ST_LOGO);
fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
- fc_rport_logo_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
+ fc_rport_logo_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
-
/**
- * fc_rport_recv_req() - Receive a request from a rport
- * @sp: current sequence in the PLOGI exchange
+ * fc_rport_els_adisc_resp() - Address Discovery response handler
+ * @sp: current sequence in the ADISC exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: remote port private.
*
- * Locking Note: Called without the rport lock held. This
- * function will hold the rport lock, call an _enter_*
- * function and then unlock the rport.
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
*/
-void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
- struct fc_rport *rport)
+static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rdata_arg)
+{
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_els_adisc *adisc;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_RPORT_DBG(rdata, "Received a ADISC response\n");
+
+ if (rdata->rp_state != RPORT_ST_ADISC) {
+ FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n",
+ fc_rport_state(rdata));
+ if (IS_ERR(fp))
+ goto err;
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rdata, fp);
+ goto err;
+ }
+
+ /*
+ * If address verification failed. Consider us logged out of the rport.
+ * Since the rport is still in discovery, we want to be
+ * logged in, so go to PLOGI state. Otherwise, go back to READY.
+ */
+ op = fc_frame_payload_op(fp);
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ if (op != ELS_LS_ACC || !adisc ||
+ ntoh24(adisc->adisc_port_id) != rdata->ids.port_id ||
+ get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name ||
+ get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) {
+ FC_RPORT_DBG(rdata, "ADISC error or mismatch\n");
+ fc_rport_enter_plogi(rdata);
+ } else {
+ FC_RPORT_DBG(rdata, "ADISC OK\n");
+ fc_rport_enter_ready(rdata);
+ }
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+}
+
+/**
+ * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer
+ * @rdata: remote port private data
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_RPORT_DBG(rdata, "sending ADISC from %s state\n",
+ fc_rport_state(rdata));
+ fc_rport_state_enter(rdata, RPORT_ST_ADISC);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_adisc));
+ if (!fp) {
+ fc_rport_error_retry(rdata, fp);
+ return;
+ }
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
+ fc_rport_adisc_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
+ else
+ kref_get(&rdata->kref);
+}
+
+/**
+ * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request
+ * @rdata: remote port private
+ * @sp: current sequence in the ADISC exchange
+ * @in_fp: ADISC request frame
+ *
+ * Locking Note: Called with the lport and rport locks held.
+ */
+static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp, struct fc_frame *in_fp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_adisc *adisc;
+ struct fc_seq_els_data rjt_data;
+ u32 f_ctl;
+
+ FC_RPORT_DBG(rdata, "Received ADISC request\n");
+
+ adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
+ if (!adisc) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ goto drop;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(*adisc));
+ if (!fp)
+ goto drop;
+ fc_adisc_fill(lport, fp);
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_LS_ACC;
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+drop:
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_rport_recv_els_req() - handle a validated ELS request.
+ * @lport: Fibre Channel local port
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ *
+ * Handle incoming ELS requests that require port login.
+ * The ELS opcode has already been validated by the caller.
+ *
+ * Locking Note: Called with the lport lock held.
+ */
+static void fc_rport_recv_els_req(struct fc_lport *lport,
+ struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_rport_priv *rdata;
struct fc_frame_header *fh;
struct fc_seq_els_data els_data;
- u8 op;
-
- mutex_lock(&rdata->rp_mutex);
els_data.fp = NULL;
- els_data.explan = ELS_EXPL_NONE;
- els_data.reason = ELS_RJT_NONE;
+ els_data.reason = ELS_RJT_UNAB;
+ els_data.explan = ELS_EXPL_PLOGI_REQD;
fh = fc_frame_header_get(fp);
- if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && fh->fh_type == FC_TYPE_ELS) {
- op = fc_frame_payload_op(fp);
- switch (op) {
- case ELS_PLOGI:
- fc_rport_recv_plogi_req(rport, sp, fp);
- break;
- case ELS_PRLI:
- fc_rport_recv_prli_req(rport, sp, fp);
- break;
- case ELS_PRLO:
- fc_rport_recv_prlo_req(rport, sp, fp);
- break;
- case ELS_LOGO:
- fc_rport_recv_logo_req(rport, sp, fp);
- break;
- case ELS_RRQ:
- els_data.fp = fp;
- lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
- break;
- case ELS_REC:
- els_data.fp = fp;
- lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
- break;
- default:
- els_data.reason = ELS_RJT_UNSUP;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
- break;
- }
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_lookup(lport, ntoh24(fh->fh_s_id));
+ if (!rdata) {
+ mutex_unlock(&lport->disc.disc_mutex);
+ goto reject;
+ }
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&lport->disc.disc_mutex);
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
+ case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
+ break;
+ default:
+ mutex_unlock(&rdata->rp_mutex);
+ goto reject;
+ }
+
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_PRLI:
+ fc_rport_recv_prli_req(rdata, sp, fp);
+ break;
+ case ELS_PRLO:
+ fc_rport_recv_prlo_req(rdata, sp, fp);
+ break;
+ case ELS_ADISC:
+ fc_rport_recv_adisc_req(rdata, sp, fp);
+ break;
+ case ELS_RRQ:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+ break;
+ case ELS_REC:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+ break;
+ default:
+ fc_frame_free(fp); /* can't happen */
+ break;
}
mutex_unlock(&rdata->rp_mutex);
+ return;
+
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_recv_req() - Handle a received ELS request from a rport
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @lport: Fibre Channel local port
+ *
+ * Locking Note: Called with the lport lock held.
+ */
+void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ struct fc_seq_els_data els_data;
+
+ /*
+ * Handle PLOGI and LOGO requests separately, since they
+ * don't require prior login.
+ * Check for unsupported opcodes first and reject them.
+ * For some ops, it would be incorrect to reject with "PLOGI required".
+ */
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_PLOGI:
+ fc_rport_recv_plogi_req(lport, sp, fp);
+ break;
+ case ELS_LOGO:
+ fc_rport_recv_logo_req(lport, sp, fp);
+ break;
+ case ELS_PRLI:
+ case ELS_PRLO:
+ case ELS_ADISC:
+ case ELS_RRQ:
+ case ELS_REC:
+ fc_rport_recv_els_req(lport, sp, fp);
+ break;
+ default:
+ fc_frame_free(fp);
+ els_data.fp = NULL;
+ els_data.reason = ELS_RJT_UNSUP;
+ els_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ break;
+ }
}
/**
* fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
- * @rport: Fibre Channel remote port that initiated PLOGI
+ * @lport: local port
* @sp: current sequence in the PLOGI exchange
* @fp: PLOGI request frame
*
- * Locking Note: The rport lock is exected to be held before calling
- * this function.
+ * Locking Note: The rport lock is held before calling this function.
*/
-static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+static void fc_rport_recv_plogi_req(struct fc_lport *lport,
struct fc_seq *sp, struct fc_frame *rx_fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
+ struct fc_disc *disc;
+ struct fc_rport_priv *rdata;
struct fc_frame *fp = rx_fp;
struct fc_exch *ep;
struct fc_frame_header *fh;
struct fc_els_flogi *pl;
struct fc_seq_els_data rjt_data;
- u32 sid;
- u64 wwpn;
- u64 wwnn;
- enum fc_els_rjt_reason reject = 0;
- u32 f_ctl;
- rjt_data.fp = NULL;
+ u32 sid, f_ctl;
+ rjt_data.fp = NULL;
fh = fc_frame_header_get(fp);
+ sid = ntoh24(fh->fh_s_id);
- FC_RPORT_DBG(rport, "Received PLOGI request while in state %s\n",
- fc_rport_state(rport));
+ FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
- sid = ntoh24(fh->fh_s_id);
pl = fc_frame_payload_get(fp, sizeof(*pl));
if (!pl) {
- FC_RPORT_DBG(rport, "Received PLOGI too short\n");
- WARN_ON(1);
- /* XXX TBD: send reject? */
- fc_frame_free(fp);
- return;
+ FC_RPORT_ID_DBG(lport, sid, "Received PLOGI too short\n");
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ goto reject;
+ }
+
+ disc = &lport->disc;
+ mutex_lock(&disc->disc_mutex);
+ rdata = lport->tt.rport_create(lport, sid);
+ if (!rdata) {
+ mutex_unlock(&disc->disc_mutex);
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ goto reject;
}
- wwpn = get_unaligned_be64(&pl->fl_wwpn);
- wwnn = get_unaligned_be64(&pl->fl_wwnn);
+
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&disc->disc_mutex);
+
+ rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
+ rdata->ids.node_name = get_unaligned_be64(&pl->fl_wwnn);
/*
- * If the session was just created, possibly due to the incoming PLOGI,
+ * If the rport was just created, possibly due to the incoming PLOGI,
* set the state appropriately and accept the PLOGI.
*
* If we had also sent a PLOGI, and if the received PLOGI is from a
@@ -996,86 +1236,76 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
*/
switch (rdata->rp_state) {
case RPORT_ST_INIT:
- FC_RPORT_DBG(rport, "Received PLOGI, wwpn %llx state INIT "
- "- reject\n", (unsigned long long)wwpn);
- reject = ELS_RJT_UNSUP;
+ FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
break;
case RPORT_ST_PLOGI:
- FC_RPORT_DBG(rport, "Received PLOGI in PLOGI state %d\n",
- rdata->rp_state);
- if (wwpn < lport->wwpn)
- reject = ELS_RJT_INPROG;
+ FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
+ if (rdata->ids.port_name < lport->wwpn) {
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_INPROG;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
+ }
break;
case RPORT_ST_PRLI:
case RPORT_ST_READY:
- FC_RPORT_DBG(rport, "Received PLOGI in logged-in state %d "
+ case RPORT_ST_ADISC:
+ FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
"- ignored for now\n", rdata->rp_state);
/* XXX TBD - should reset */
break;
- case RPORT_ST_NONE:
+ case RPORT_ST_DELETE:
default:
- FC_RPORT_DBG(rport, "Received PLOGI in unexpected "
- "state %d\n", rdata->rp_state);
- fc_frame_free(fp);
- return;
- break;
+ FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
+ rdata->rp_state);
+ fc_frame_free(rx_fp);
+ goto out;
}
- if (reject) {
- rjt_data.reason = reject;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- fc_frame_free(fp);
- } else {
- fp = fc_frame_alloc(lport, sizeof(*pl));
- if (fp == NULL) {
- fp = rx_fp;
- rjt_data.reason = ELS_RJT_UNAB;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- fc_frame_free(fp);
- } else {
- sp = lport->tt.seq_start_next(sp);
- WARN_ON(!sp);
- fc_rport_set_name(rport, wwpn, wwnn);
-
- /*
- * Get session payload size from incoming PLOGI.
- */
- rport->maxframe_size =
- fc_plogi_get_maxframe(pl, lport->mfs);
- fc_frame_free(rx_fp);
- fc_plogi_fill(lport, fp, ELS_LS_ACC);
-
- /*
- * Send LS_ACC. If this fails,
- * the originator should retry.
- */
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
- ep = fc_seq_exch(sp);
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
- FC_TYPE_ELS, f_ctl, 0);
- lport->tt.seq_send(lport, sp, fp);
- if (rdata->rp_state == RPORT_ST_PLOGI)
- fc_rport_enter_prli(rport);
- }
- }
+ /*
+ * Get session payload size from incoming PLOGI.
+ */
+ rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
+ fc_frame_free(rx_fp);
+
+ /*
+ * Send LS_ACC. If this fails, the originator should retry.
+ */
+ sp = lport->tt.seq_start_next(sp);
+ if (!sp)
+ goto out;
+ fp = fc_frame_alloc(lport, sizeof(*pl));
+ if (!fp)
+ goto out;
+
+ fc_plogi_fill(lport, fp, ELS_LS_ACC);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ fc_rport_enter_prli(rdata);
+out:
+ mutex_unlock(&rdata->rp_mutex);
+ return;
+
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
}
/**
* fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request
- * @rport: Fibre Channel remote port that initiated PRLI
+ * @rdata: private remote port data
* @sp: current sequence in the PRLI exchange
* @fp: PRLI request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_prli_req(struct fc_rport *rport,
+static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_seq *sp, struct fc_frame *rx_fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_exch *ep;
struct fc_frame *fp;
@@ -1099,12 +1329,14 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
fh = fc_frame_header_get(rx_fp);
- FC_RPORT_DBG(rport, "Received PRLI request while in state %s\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
+ fc_rport_state(rdata));
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
reason = ELS_RJT_NONE;
break;
default:
@@ -1149,6 +1381,9 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
@@ -1169,12 +1404,12 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
fcp_parm = ntohl(rspp->spp_params);
if (fcp_parm * FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
- rport->supported_classes = FC_COS_CLASS3;
+ rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
- rport->roles = roles;
+ rdata->ids.roles = roles;
spp->spp_params =
htonl(lport->service_params);
@@ -1204,9 +1439,10 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
*/
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
break;
case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
break;
default:
break;
@@ -1217,17 +1453,17 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
/**
* fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request
- * @rport: Fibre Channel remote port that initiated PRLO
+ * @rdata: private remote port data
* @sp: current sequence in the PRLO exchange
* @fp: PRLO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp,
struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame_header *fh;
@@ -1235,13 +1471,8 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
fh = fc_frame_header_get(fp);
- FC_RPORT_DBG(rport, "Received PRLO request while in state %s\n",
- fc_rport_state(rport));
-
- if (rdata->rp_state == RPORT_ST_NONE) {
- fc_frame_free(fp);
- return;
- }
+ FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
+ fc_rport_state(rdata));
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_UNAB;
@@ -1252,35 +1483,46 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
/**
* fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request
- * @rport: Fibre Channel remote port that initiated LOGO
+ * @lport: local port.
* @sp: current sequence in the LOGO exchange
* @fp: LOGO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_logo_req(struct fc_lport *lport,
+ struct fc_seq *sp,
struct fc_frame *fp)
{
struct fc_frame_header *fh;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
-
- fh = fc_frame_header_get(fp);
+ struct fc_rport_priv *rdata;
+ u32 sid;
- FC_RPORT_DBG(rport, "Received LOGO request while in state %s\n",
- fc_rport_state(rport));
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
- if (rdata->rp_state == RPORT_ST_NONE) {
- fc_frame_free(fp);
- return;
- }
+ fh = fc_frame_header_get(fp);
+ sid = ntoh24(fh->fh_s_id);
- rdata->event = RPORT_EV_LOGO;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_lookup(lport, sid);
+ if (rdata) {
+ mutex_lock(&rdata->rp_mutex);
+ FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
+ fc_rport_state(rdata));
- lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ /*
+ * If the remote port was created due to discovery,
+ * log back in. It may have seen a stale RSCN about us.
+ */
+ if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id)
+ fc_rport_enter_plogi(rdata);
+ else
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ mutex_unlock(&rdata->rp_mutex);
+ } else
+ FC_RPORT_ID_DBG(lport, sid,
+ "Received LOGO from non-logged-in port\n");
+ mutex_unlock(&lport->disc.disc_mutex);
fc_frame_free(fp);
}
@@ -1291,8 +1533,11 @@ static void fc_rport_flush_queue(void)
int fc_rport_init(struct fc_lport *lport)
{
+ if (!lport->tt.rport_lookup)
+ lport->tt.rport_lookup = fc_rport_lookup;
+
if (!lport->tt.rport_create)
- lport->tt.rport_create = fc_rport_rogue_create;
+ lport->tt.rport_create = fc_rport_create;
if (!lport->tt.rport_login)
lport->tt.rport_login = fc_rport_login;
@@ -1306,6 +1551,9 @@ int fc_rport_init(struct fc_lport *lport)
if (!lport->tt.rport_flush_queue)
lport->tt.rport_flush_queue = fc_rport_flush_queue;
+ if (!lport->tt.rport_destroy)
+ lport->tt.rport_destroy = fc_rport_destroy;
+
return 0;
}
EXPORT_SYMBOL(fc_rport_init);
@@ -1327,8 +1575,8 @@ EXPORT_SYMBOL(fc_destroy_rport);
void fc_rport_terminate_io(struct fc_rport *rport)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct fc_lport *lport = rp->local_port;
lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
lport->tt.exch_mgr_reset(lport, rport->port_id, 0);