diff options
Diffstat (limited to 'drivers/scsi/libfc')
-rw-r--r-- | drivers/scsi/libfc/fc_disc.c | 523 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_elsct.c | 49 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 515 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_fcp.c | 31 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 283 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 1144 |
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); |